diff --git a/new/Caddyfile b/new/Caddyfile index ae6906e..34d48ac 100644 --- a/new/Caddyfile +++ b/new/Caddyfile @@ -1,6 +1,23 @@ -localhost +{ + debug +} -file_server -templates +localhost { + file_server + templates -try_files {path}.html {path} + try_files {path}.html {path} + + handle_path /temporary-markdown-proxy/* { + reverse_proxy 127.0.0.1:10434 + } +} + +:10434 { + log + bind 127.0.0.1 + root ../src/docs/markdown + uri strip_prefix /docs + try_files {path}.md {path} + file_server +} diff --git a/new/docs/index.html b/new/docs/index.html new file mode 100644 index 0000000..d7fd1f4 --- /dev/null +++ b/new/docs/index.html @@ -0,0 +1,330 @@ + + + + Caddy Documentation + {{include "/includes/head.html"}} + + + + + + + +
+ {{include "/includes/header.html" "logo-light.svg"}} +
+ +
+ +
+
+ + +

Administration API

+

Caddy is configured through an administration endpoint which can be accessed via HTTP using a REST API. You can configure this endpoint in your Caddy config.

+

Default address: localhost:2019

+

The default address can be changed by setting the CADDY_ADMIN environment variable. Some installation methods may set this to something different. The address in the Caddy config always takes precedence over the default.

+ +

The latest configuration will be saved to disk after any changes (unless disabled). You can resume the last working config after a restart with caddy run --resume, which guarantees config durability in the event of a power cycle or similar.

+

To get started with the API, try our API tutorial or, if you only have a minute, our API quick-start guide.

+
+ +

POST /load

+

Sets Caddy's configuration, overriding any previous configuration. It blocks until the reload completes or fails. Configuration changes are lightweight, efficient, and incur zero downtime. If the new config fails for any reason, the old config is rolled back into place without downtime.

+

This endpoint supports different config formats using config adapters. The request's Content-Type header indicates the config format used in the request body. Usually, this should be application/json which represents Caddy's native config format. For another config format, specify the appropriate Content-Type so that the value after the forward slash / is the name of the config adapter to use. For example, when submitting a Caddyfile, use a value like text/caddyfile; or for JSON 5, use a value such as application/json5; etc.

+

If the new config is the same as the current one, no reload will occur. To force a reload, set Cache-Control: must-revalidate in the request headers.

+

Examples

+

Set a new active configuration:

+
curl "http://localhost:2019/load" \
+	-H "Content-Type: application/json" \
+	-d @caddy.json
+

Note: curl's -d flag removes newlines, so if your config format is sensitive to line breaks (e.g. the Caddyfile), use --data-binary instead:

+
curl "http://localhost:2019/load" \
+	-H "Content-Type: text/caddyfile" \
+	--data-binary @Caddyfile
+

POST /stop

+

Gracefully shuts down the server and exits the process. To only stop the running configuration without exiting the process, use DELETE /config/.

+

Example

+

Stop the process:

+
curl -X POST "http://localhost:2019/stop"
+

GET /config/[path]

+

Exports Caddy's current configuration at the named path. Returns a JSON body.

+

Examples

+

Export entire config and pretty-print it:

+
curl "http://localhost:2019/config/" | jq
+{
+	"apps": {
+		"http": {
+			"servers": {
+				"myserver": {
+					"listen": [
+						":443"
+					],
+					"routes": [
+						{
+							"match": [
+								{
+									"host": [
+										"example.com"
+									]
+								}
+							],
+							"handle": [
+								{
+									"handler": "file_server"
+								}
+							]
+						}
+					]
+				}
+			}
+		}
+	}
+}
+

Export just the listener addresses:

+
curl "http://localhost:2019/config/apps/http/servers/myserver/listen"
+[":443"]
+

POST /config/[path]

+

Changes Caddy's configuration at the named path to the JSON body of the request. If the destination value is an array, POST appends; if an object, it creates or replaces.

+

As a special case, many items can be added to an array if:

+
    +
  1. the path ends in /...
  2. +
  3. the element of the path before /... refers to an array
  4. +
  5. the payload is an array
  6. +
+

In this case, the elements in the payload's array will be expanded, and each one will be appended to the destination array. In Go terms, this would have the same effect as:

+
baseSlice = append(baseSlice, newElems...)
+

Examples

+

Add a listener address:

+
curl \
+	-H "Content-Type: application/json" \
+	-d '":8080"' \
+	"http://localhost:2019/config/apps/http/servers/myserver/listen"
+

Add multiple listener addresses:

+
curl \
+	-H "Content-Type: application/json" \
+	-d '[":8080", ":5133"]' \
+	"http://localhost:2019/config/apps/http/servers/myserver/listen/..."
+

PUT /config/[path]

+

Changes Caddy's configuration at the named path to the JSON body of the request. If the destination value is a position (index) in an array, PUT inserts; if an object, it strictly creates a new value.

+

Example

+

Add a listener address in the first slot:

+
curl -X PUT \
+	-H "Content-Type: application/json" \
+	-d '":8080"' \
+	"http://localhost:2019/config/apps/http/servers/myserver/listen/0"
+

PATCH /config/[path]

+

Changes Caddy's configuration at the named path to the JSON body of the request. PATCH strictly replaces an existing value or array element.

+

Example

+

Replace the listener addresses:

+
curl -X PATCH \
+	-H "Content-Type: application/json" \
+	-d '[":8081", ":8082"]' \
+	"http://localhost:2019/config/apps/http/servers/myserver/listen"
+

DELETE /config/[path]

+

Removes Caddy's configuration at the named path. DELETE deletes the target value.

+

Examples

+

To unload the entire current configuration but leave the process running:

+
curl -X DELETE "http://localhost:2019/config/"
+

To stop only one of your HTTP servers:

+
curl -X DELETE "http://localhost:2019/config/apps/http/servers/myserver"
+

Using @id in JSON

+

You can embed IDs in your JSON document for easier direct access to those parts of the JSON.

+

Simply add a field called "@id" to an object and give it a unique name. For example, if you had a reverse proxy handler that you wanted to access frequently:

+
{
+	"@id": "my_proxy",
+	"handler": "reverse_proxy"
+}
+

To use it, simply make a request to the /id/ API endpoint in the same way you would to the corresponding /config/ endpoint, but without the whole path. The ID takes the request directly into that scope of the config for you.

+

For example, to access the upstreams of the reverse proxy without an ID, the path would be something like

+
/config/apps/http/servers/myserver/routes/1/handle/0/upstreams
+
+

but with an ID, the path becomes

+
/id/my_proxy/upstreams
+
+

which is much easier to remember and write by hand.

+

Concurrent config changes

+ +

Caddy's config API provides ACID guarantees for individual requests, but changes that involve more than a single request are subject to collisions or data loss if not properly synchronized.

+

For example, two clients may GET /config/foo at the same time, make an edit within that scope (config path), then call POST|PUT|PATCH|DELETE /config/foo/... at the same time to apply their changes, resulting in a collision: either one will overwrite the other, or the second might leave the config in an unintended state since it was applied to a different version of the config than it was prepared against. This is because the changes are not aware of each other.

+

Caddy's API does not support transactions spanning multiple requests, and HTTP is a stateless protocol. However, you can use the Etag trailer and If-Match header to detect and prevent collisions for any and all changes as a kind of optimistic concurrency control. This is useful if there is any chance that you are using Caddy's /config/... endpoints concurrently without synchronization. All responses to GET /config/... requests have an HTTP trailer called Etag that contains the path and a hash of the contents in that scope (e.g. Etag: "/config/apps/http/servers 65760b8e"). Simply set the If-Match header on a mutative request to that of an Etag trailer from a previous GET request.

+

The basic algorithm for this is as follows:

+
    +
  1. Perform a GET request to any scope S within the config. Hold onto the Etag trailer of the response.
  2. +
  3. Make your desired change on the returned config.
  4. +
  5. Perform a POST|PUT|PATCH|DELETE request within scope S, setting the If-Match header to the recent Etag value.
  6. +
  7. If the response is HTTP 412 (Precondition Failed), repeat from step 1, or give up after too many attempts.
  8. +
+

This algorithm safely allows multiple, overlapping changes to Caddy's configuration without explicit synchronization. It is designed so that simultaneous changes to different parts of the config don't require a retry: only changes that overlap the same scope of the config can possibly cause a collision and thus require a retry.

+

POST /adapt

+

Adapts a configuration to Caddy JSON without loading or running it. If successful, the resulting JSON document is returned in the response body.

+

The Content-Type header is used to specify the configuration format in the same way that /load works. For example, to adapt a Caddyfile, set Content-Type: text/caddyfile.

+

This endpoint will adapt any configuration format as long as the associated config adapter is plugged in to your Caddy build.

+

Examples

+

Adapt a Caddyfile to JSON:

+
curl "http://localhost:2019/adapt" \
+	-H "Content-Type: text/caddyfile" \
+	--data-binary @Caddyfile
+

GET /pki/ca/<id>

+

Returns information about a particular PKI app CA by its ID. If the requested CA ID is the default (local), then the CA will be provisioned if it has not already been. Other CA IDs will return an error if they have not been previously provisioned.

+
curl "http://localhost:2019/pki/ca/local" | jq
+{
+	"id": "local",
+	"name": "Caddy Local Authority",
+	"root_common_name": "Caddy Local Authority - 2022 ECC Root",
+	"intermediate_common_name": "Caddy Local Authority - ECC Intermediate",
+	"root_certificate": "-----BEGIN CERTIFICATE-----\nMIIB ... gRw==\n-----END CERTIFICATE-----\n",
+	"intermediate_certificate": "-----BEGIN CERTIFICATE-----\nMIIB ... FzQ==\n-----END CERTIFICATE-----\n"
+}
+

GET /pki/ca/<id>/certificates

+

Returns the certificate chain of a particular PKI app CA by its ID. If the requested CA ID is the default (local), then the CA will be provisioned if it has not already been. Other CA IDs will return an error if they have not been previously provisioned.

+

This endpoint is used internally by the caddy trust command to allow installing the CA's root certificate to your system's trust store.

+
curl "http://localhost:2019/pki/ca/local/certificates"
+-----BEGIN CERTIFICATE-----
+MIIByDCCAW2gAwIBAgIQViS12trTXBS/nyxy7Zg9JDAKBggqhkjOPQQDAjAwMS4w
+...
+By75JkP6C14OfU733oElfDUMa5ctbMY53rWFzQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIBpDCCAUmgAwIBAgIQTS5a+3LUKNxC6qN3ZDR8bDAKBggqhkjOPQQDAjAwMS4w
+...
+9M9t0FwCIQCAlUr4ZlFzHE/3K6dARYKusR1ck4A3MtucSSyar6lgRw==
+-----END CERTIFICATE-----
+

GET /reverse_proxy/upstreams

+

Returns the current status of the configured reverse proxy upstreams (backends) as a JSON document.

+
curl "http://localhost:2019/reverse_proxy/upstreams" | jq
+[
+	{"address": "10.0.1.1:80", "num_requests": 4, "fails": 2},
+	{"address": "10.0.1.2:80", "num_requests": 5, "fails": 4},
+	{"address": "10.0.1.3:80", "num_requests": 3, "fails": 3}
+]
+

Each entry in the JSON array is a configured upstream stored in the global upstream pool.

+
    +
  • address is the dial address of the upstream. For SRV upstreams, this is the lookup_srv DNS name.
  • +
  • num_requests is the amount of active requests currently being handled by the upstream.
  • +
  • fails the current number of failed requests remembered, as configured by passive health checks.
  • +
+

If your goal is to determine a backend's availability, you will need to cross-check relevant properties of the upstream against the handler configuration you are utilizing. For example, if you've enabled passive health checks for your proxies, then you need to also take into consideration the fails and num_requests values to determine if an upstream is considered available: check that the fails amount is less than your configured maximum amount of failures for your proxy (i.e. max_fails), and that num_requests is less than or equal to your configured amount of maximum requests per upstream (i.e. unhealthy_request_count for the whole proxy, or max_requests for individual upstreams).

+
+ + +
+
+ + + \ No newline at end of file diff --git a/new/includes/head.html b/new/includes/head.html index 9152ca8..ed418d7 100644 --- a/new/includes/head.html +++ b/new/includes/head.html @@ -4,8 +4,14 @@ - + + + + + + + diff --git a/new/includes/header.html b/new/includes/header.html new file mode 100644 index 0000000..d1cef5d --- /dev/null +++ b/new/includes/header.html @@ -0,0 +1,190 @@ +
+
+ +
+ +
\ No newline at end of file diff --git a/new/index.html b/new/index.html index 10a529f..6ab0a69 100644 --- a/new/index.html +++ b/new/index.html @@ -9,186 +9,7 @@
-
- - -
+ {{include "/includes/header.html" "logo-dark.svg"}}
diff --git a/new/resources/css/chroma.css b/new/resources/css/chroma.css new file mode 100644 index 0000000..49d53a4 --- /dev/null +++ b/new/resources/css/chroma.css @@ -0,0 +1,162 @@ +/* + Derived from https://gist.github.com/nicolashery/5765395 + Adjusted to be high-contrast +*/ + + +/* + Solarized Light (High Contrast) + Derived from http://ethanschoonover.com/solarized +*/ +.chroma { + /* background: linear-gradient(0deg, #f8fbfd 0%, #edf5fd 100%); */ + background: linear-gradient(to bottom, #eff7f9, transparent); + color: #254048; +} +.chroma .c { color: #93a1a1 } /* Comment */ +.chroma .err { color: #586e75 } /* Error */ +.chroma .g { color: #586e75 } /* Generic */ +.chroma .k { color: #577b00 } /* Keyword */ +.chroma .l { color: #586e75 } /* Literal */ +.chroma .n { color: #586e75 } /* Name */ +.chroma .o { color: #577b00 } /* Operator */ +.chroma .x { color: #d03d00 } /* Other */ +.chroma .p { color: #586e75 } /* Punctuation */ +.chroma .cm { color: #93a1a1 } /* Comment.Multiline */ +.chroma .cp { color: #577b00 } /* Comment.Preproc */ +.chroma .c1 { color: #93a1a1 } /* Comment.Single */ +.chroma .cs { color: #577b00 } /* Comment.Special */ +.chroma .gd { color: #dc322f; background-color: #efdede } /* Generic.Deleted */ +.chroma .ge { color: #586e75; font-style: italic } /* Generic.Emph */ +.chroma .gr { color: #dc322f } /* Generic.Error */ +.chroma .gh { color: #d03d00 } /* Generic.Heading */ +.chroma .gi { color: #577b00; background-color: #ddecdc } /* Generic.Inserted */ +.chroma .go { color: #586e75 } /* Generic.Output */ +.chroma .gp { color: #586e75 } /* Generic.Prompt */ +.chroma .gs { color: #586e75; font-weight: bold } /* Generic.Strong */ +.chroma .gu { color: #d03d00 } /* Generic.Subheading */ +.chroma .gt { color: #586e75 } /* Generic.Traceback */ +.chroma .kc { color: #d03d00 } /* Keyword.Constant */ +.chroma .kd { color: #0673bf } /* Keyword.Declaration */ +.chroma .kn { color: #577b00 } /* Keyword.Namespace */ +.chroma .kp { color: #577b00 } /* Keyword.Pseudo */ +.chroma .kr { color: #0673bf } /* Keyword.Reserved */ +.chroma .kt { color: #dc322f } /* Keyword.Type */ +.chroma .ld { color: #586e75 } /* Literal.Date */ +.chroma .m { color: #008076 } /* Literal.Number */ +.chroma .s { color: #008076 } /* Literal.String */ +.chroma .na { color: #586e75 } /* Name.Attribute */ +.chroma .nb { color: #B58900 } /* Name.Builtin */ +.chroma .nc { color: #0673bf } /* Name.Class */ +.chroma .no { color: #d03d00 } /* Name.Constant */ +.chroma .nd { color: #0673bf } /* Name.Decorator */ +.chroma .ni { color: #d03d00 } /* Name.Entity */ +.chroma .ne { color: #d03d00 } /* Name.Exception */ +.chroma .nf { color: #0673bf } /* Name.Function */ +.chroma .nl { color: #586e75 } /* Name.Label */ +.chroma .nn { color: #586e75 } /* Name.Namespace */ +.chroma .nx { color: #586e75 } /* Name.Other */ +.chroma .py { color: #586e75 } /* Name.Property */ +.chroma .nt { color: #0673bf } /* Name.Tag */ +.chroma .nv { color: #0673bf } /* Name.Variable */ +.chroma .ow { color: #577b00 } /* Operator.Word */ +.chroma .w { color: #586e75 } /* Text.Whitespace */ +.chroma .mf { color: #008076 } /* Literal.Number.Float */ +.chroma .mh { color: #008076 } /* Literal.Number.Hex */ +.chroma .mi { color: #008076 } /* Literal.Number.Integer */ +.chroma .mo { color: #008076 } /* Literal.Number.Oct */ +.chroma .sb { color: #93a1a1 } /* Literal.String.Backtick */ +.chroma .sc { color: #008076 } /* Literal.String.Char */ +.chroma .sd { color: #586e75 } /* Literal.String.Doc */ +.chroma .s2 { color: #008076 } /* Literal.String.Double */ +.chroma .se { color: #d03d00 } /* Literal.String.Escape */ +.chroma .sh { color: #586e75 } /* Literal.String.Heredoc */ +.chroma .si { color: #008076 } /* Literal.String.Interpol */ +.chroma .sx { color: #008076 } /* Literal.String.Other */ +.chroma .sr { color: #dc322f } /* Literal.String.Regex */ +.chroma .s1 { color: #008076 } /* Literal.String.Single */ +.chroma .ss { color: #008076 } /* Literal.String.Symbol */ +.chroma .bp { color: #0673bf } /* Name.Builtin.Pseudo */ +.chroma .vc { color: #0673bf } /* Name.Variable.Class */ +.chroma .vg { color: #0673bf } /* Name.Variable.Global */ +.chroma .vi { color: #0673bf } /* Name.Variable.Instance */ +.chroma .il { color: #008076 } /* Literal.Number.Integer.Long */ + + + +/* + Solarized Dark (High Contrast) + Derived from http://ethanschoonover.com/solarized +*/ +.dark .chroma { + background: linear-gradient(to bottom, #24454f, transparent); + color: #93a1a1; +} +.dark .chroma .c { color: #586e75 } /* Comment */ +.dark .chroma .err { color: #93a1a1 } /* Error */ +.dark .chroma .g { color: #93a1a1 } /* Generic */ +.dark .chroma .k { color: #76a507 } /* Keyword */ +.dark .chroma .l { color: #93a1a1 } /* Literal */ +.dark .chroma .n { color: #93a1a1 } /* Name */ +.dark .chroma .o { color: #76a507 } /* Operator */ +.dark .chroma .x { color: #ec662e } /* Other */ +.dark .chroma .p { color: #93a1a1 } /* Punctuation */ +.dark .chroma .cm { color: #586e75 } /* Comment.Multiline */ +.dark .chroma .cp { color: #76a507 } /* Comment.Preproc */ +.dark .chroma .c1 { color: #586e75 } /* Comment.Single */ +.dark .chroma .cs { color: #76a507 } /* Comment.Special */ +.dark .chroma .gd { color: #dc322f; background-color: #efdede } /* Generic.Deleted */ +.dark .chroma .ge { color: #93a1a1; font-style: italic } /* Generic.Emph */ +.dark .chroma .gr { color: #dc322f } /* Generic.Error */ +.dark .chroma .gh { color: #ec662e } /* Generic.Heading */ +.dark .chroma .gi { color: #76a507 } /* Generic.Inserted */ +.dark .chroma .go { color: #93a1a1 } /* Generic.Output */ +.dark .chroma .gp { color: #93a1a1 } /* Generic.Prompt */ +.dark .chroma .gs { color: #93a1a1; font-weight: bold } /* Generic.Strong */ +.dark .chroma .gu { color: #ec662e } /* Generic.Subheading */ +.dark .chroma .gt { color: #93a1a1 } /* Generic.Traceback */ +.dark .chroma .kc { color: #ec662e } /* Keyword.Constant */ +.dark .chroma .kd { color: #0090f5 } /* Keyword.Declaration */ +.dark .chroma .kn { color: #76a507 } /* Keyword.Namespace */ +.dark .chroma .kp { color: #76a507 } /* Keyword.Pseudo */ +.dark .chroma .kr { color: #0090f5 } /* Keyword.Reserved */ +.dark .chroma .kt { color: #dc322f } /* Keyword.Type */ +.dark .chroma .ld { color: #93a1a1 } /* Literal.Date */ +.dark .chroma .m { color: #09a598 } /* Literal.Number */ +.dark .chroma .s { color: #09a598 } /* Literal.String */ +.dark .chroma .na { color: #93a1a1 } /* Name.Attribute */ +.dark .chroma .nb { color: #B58900 } /* Name.Builtin */ +.dark .chroma .nc { color: #0090f5 } /* Name.Class */ +.dark .chroma .no { color: #ec662e } /* Name.Constant */ +.dark .chroma .nd { color: #0090f5 } /* Name.Decorator */ +.dark .chroma .ni { color: #ec662e } /* Name.Entity */ +.dark .chroma .ne { color: #ec662e } /* Name.Exception */ +.dark .chroma .nf { color: #0090f5 } /* Name.Function */ +.dark .chroma .nl { color: #93a1a1 } /* Name.Label */ +.dark .chroma .nn { color: #93a1a1 } /* Name.Namespace */ +.dark .chroma .nx { color: #93a1a1 } /* Name.Other */ +.dark .chroma .py { color: #93a1a1 } /* Name.Property */ +.dark .chroma .nt { color: #0090f5 } /* Name.Tag */ +.dark .chroma .nv { color: #0090f5 } /* Name.Variable */ +.dark .chroma .ow { color: #76a507 } /* Operator.Word */ +.dark .chroma .w { color: #93a1a1 } /* Text.Whitespace */ +.dark .chroma .mf { color: #09a598 } /* Literal.Number.Float */ +.dark .chroma .mh { color: #09a598 } /* Literal.Number.Hex */ +.dark .chroma .mi { color: #09a598 } /* Literal.Number.Integer */ +.dark .chroma .mo { color: #09a598 } /* Literal.Number.Oct */ +.dark .chroma .sb { color: #586e75 } /* Literal.String.Backtick */ +.dark .chroma .sc { color: #09a598 } /* Literal.String.Char */ +.dark .chroma .sd { color: #93a1a1 } /* Literal.String.Doc */ +.dark .chroma .s2 { color: #09a598 } /* Literal.String.Double */ +.dark .chroma .se { color: #ec662e } /* Literal.String.Escape */ +.dark .chroma .sh { color: #93a1a1 } /* Literal.String.Heredoc */ +.dark .chroma .si { color: #09a598 } /* Literal.String.Interpol */ +.dark .chroma .sx { color: #09a598 } /* Literal.String.Other */ +.dark .chroma .sr { color: #dc322f } /* Literal.String.Regex */ +.dark .chroma .s1 { color: #09a598 } /* Literal.String.Single */ +.dark .chroma .ss { color: #09a598 } /* Literal.String.Symbol */ +.dark .chroma .bp { color: #0090f5 } /* Name.Builtin.Pseudo */ +.dark .chroma .vc { color: #0090f5 } /* Name.Variable.Class */ +.dark .chroma .vg { color: #0090f5 } /* Name.Variable.Global */ +.dark .chroma .vi { color: #0090f5 } /* Name.Variable.Instance */ +.dark .chroma .il { color: #09a598 } /* Literal.Number.Integer.Long */ diff --git a/new/resources/css/common.css b/new/resources/css/common.css index 0ee7eb5..e64a16e 100644 --- a/new/resources/css/common.css +++ b/new/resources/css/common.css @@ -1,3 +1,81 @@ +:root { + --body-bg: white; + --text-color: #222; + --header-bg: rgba(118, 179, 194, 0.11); + --header-border-color: #ebf0f2; + --main-nav-link-color: #546c75; + --main-nav-link-hover-color: #01324b; + --link-color: #0097f2; + --link-decoration-color: #ddd; + --link-hover-color: rgb(27, 170, 70); + --code-bg: #f2f8f9; + --dropdown-bg: #e8ecef; + --dropdown-linkbox: white; + --dropdown-link-title-color: #384f61; + --dropdown-link-color: #647687; + --dropdown-link-hover-bg: rgb(239, 244, 248); + --dropdown-link-hover-color: #142633; + --dropdown-featured-bg: linear-gradient(to bottom, rgb(239 244 247), transparent); + --dropdown-shadow-color: rgb(0 0 0 / .4); +} + +.dark { + --body-bg: #0d171a; + --text-color: #a4c0c2; + --header-bg: rgba(44, 130, 164, 0.11); + --header-border-color: transparent; + --main-nav-link-color: #7d8e93; + --main-nav-link-hover-color: #e2e9ec; + --link-color: #34a1e4; + --link-decoration-color: #375862; + --link-hover-color: rgb(42, 228, 98); + --code-bg: #1f3237; + --dropdown-bg: #34414b; + --dropdown-linkbox: #202b2f; + --dropdown-link-title-color: #f0f3f5; + --dropdown-link-color: #9ebeca; + --dropdown-link-hover-color: white; + --dropdown-link-hover-bg: rgb(30, 48, 63); + --dropdown-featured-bg: linear-gradient(to bottom, rgb(46, 58, 66), transparent); + --dropdown-shadow-color: black; +} + + +.dark .button.secondary { + border-color: rgb(64, 121, 136); +} + +.dark article h2, +.dark article h3 { + color: #e0f8f9; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + * { margin: 0; padding: 0; @@ -5,31 +83,58 @@ } body { - font-family: Inter, system-ui; - font-size: 16px; + font-family: Inter, Figtree, Gantari, 'Albert Sans', Inter, system-ui; + font-size: 18px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; tab-size: 4; - -moz-tab-size: 4; + background-color: var(--body-bg); + color: var(--text-color); +} + +main a { + color: var(--link-color); + text-decoration-line: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + text-decoration-color: var(--link-decoration-color); + transition: all .15s; +} + +main a:hover { + color: var(--link-hover-color); + text-decoration-color: var(--link-color); +} + +b, +strong { + font-weight: 600; +} + +code { + font-family: 'JetBrains Mono', 'Chivo Mono', monospace; + font-feature-settings: "liga" 0; /* prevent the merging of chars like "fi", relevant for Chivo Mono especially */ } .wrapper { max-width: 1400px; margin-left: auto; margin-right: auto; + box-sizing: content-box; + padding-left: 50px; + padding-right: 50px; } header { - background: rgb(255 255 255 / .1); - box-shadow: 0 0 50px rgb(0 0 0 / .2); - backdrop-filter: blur(10px); + background: var(--header-bg); + position: relative; + z-index: 99; } header a { text-decoration: none; } -header .topbar, header nav > ul { background: linear-gradient(to right, rgb(100 190 121), rgb(54 206 255)); background-clip: text; @@ -37,7 +142,7 @@ header nav > ul { } .topbar { - font-size: 14px; + font-size: 12px; border-bottom: 1px solid hsl(203deg 100% 79% / 20%); } @@ -46,19 +151,20 @@ header nav > ul { justify-content: flex-end; align-items: center; gap: 1em; - color: rgba(255, 255, 255, 0.75); + color: #777; } .topbar a { display: inline-block; - padding: 8px; - color: inherit; + padding: 10px 10px 8px; + color: #719bcc; display: flex; align-items: center; } .topbar a:hover { - background: rgb(0 0 0 / .5); + background: rgb(0 0 0 / .05); + color: black; } .navbar { @@ -67,37 +173,44 @@ header nav > ul { gap: 2em; } -nav ul { +header nav ul { list-style: none; margin: 0; padding-left: 0; display: flex; align-items: center; - gap: 2em; flex: 1; } -.topbar, .navbar nav > ul > li > a { -webkit-text-fill-color: rgba(0 0 0 / .1); } -.topbar a:hover, .navbar nav > ul > li > a:hover { -webkit-text-fill-color: rgba(255 255 255 / .4); } +.navbar nav { + position: relative; +} + .navbar nav > ul > li > a, .navbar .button { text-decoration: none; - transition: all 250ms; + transition: all 200ms; color: inherit; font-weight: 500; } .navbar nav > ul > li > a { - padding: 20px 5px; + padding: 20px 1.5em; display: block; + font-family: Figtree, system-ui; + font-size: 16px; +} + +.navbar .button { + font-size: 80%; } .icon { @@ -128,6 +241,7 @@ nav ul { .button.secondary:hover { color: white; + border-color: white; } .button.primary { @@ -138,49 +252,43 @@ nav ul { .button.primary:hover { color: #1a71cb; - transform: scale(1.08); + transform: scale(1.1); box-shadow: 0 4px 15px rgb(0 0 0 / .2); } -.button.primary:active { +.button:active { transition: all 100ms; transform: scale(.95); - box-shadow: 0 -1px 4px rgb(0 0 0 / .5); -} - - - - - -#docs-link:hover .dropdown { - visibility: visible; - opacity: 1; - transform-origin: top center; - animation: rotateMenu 300ms ease; -} - -@keyframes rotateMenu { - 0% { transform: rotateX(-90deg) } - 100% { transform: rotateX(0deg) } + box-shadow: 2px 2px 5px inset rgba(0, 0, 0, 0.2); } .dropdown { + font-size: 16px; position: absolute; display: inline-block; - background: #e8ecef; + background: var(--dropdown-bg); border-radius: 15px; line-height: 1.5; overflow: hidden; - box-shadow: 0 20px 50px black; - transition-duration: .2s; - transition-property: opacity, transform; + box-shadow: 0 30px 75px var(--dropdown-shadow-color); visibility: hidden; - opacity: 0; top: calc(100% - 5px); left: 0; } +.dropdown-trigger:hover .dropdown { + visibility: visible; + transform-origin: top left; + animation: rotateMenu 300ms ease; +} + +@keyframes rotateMenu { + 0% { transform: rotateX(-90deg) scale(0.75); opacity: 0; } + 100% { transform: rotateX(0deg) scale(1); opacity: 1; } +} + + .dropdown .row { display: flex; gap: 1px; @@ -201,24 +309,24 @@ nav ul { } .dropdown .plainbox a { - color: #647687; + color: var(--dropdown-link-color); } .dropdown .plainbox a:hover { - color: #142633; + color: var(--dropdown-link-hover-color); } .dropdown .linkbox { - background: white; + background: var(--dropdown-linkbox); gap: 4em; } -.dropdown h2 { +.dropdown .links-header { font-family: Poppins, ui-rounded; - font-weight: 500; - color: #444; - font-size: 22px; - margin-bottom: 15px; + font-weight: 600; + color: var(--dropdown-link-title-color); + font-size: 20px; + margin-bottom: 5px; } .dropdown .col { @@ -227,20 +335,25 @@ nav ul { .dropdown .col a { display: block; - color: #647687; + color: var(--dropdown-link-color); text-decoration: none; padding: 5px 0; } .dropdown .col a:hover { - color: #142633; + color: var(--dropdown-link-hover-color); +} + +.dropdown .flatlinks a, +.dropdown .featured a { + transition: background-color 150ms; } .dropdown .flatlinks a { - background: white; + background: var(--dropdown-linkbox); padding: 1em 2em; - color: #647687; + color: var(--dropdown-link-color); font-size: 14px; } @@ -252,21 +365,22 @@ nav ul { } .dropdown .flatlinks a b { - color: #142633; + color: var(--dropdown-link-title-color); margin-bottom: .25em; } .dropdown .flatlinks a:hover { - background: rgb(239, 244, 248); + /* background: rgb(239, 244, 248); */ + background: var(--dropdown-link-hover-bg); } .dropdown .featured a b { - color: #384f61; + color: var(--dropdown-link-title-color); margin-bottom: .5em; } .dropdown .featured a:hover b { - color: #1e3141; + color: var(--dropdown-link-hover-color); } @@ -282,13 +396,13 @@ nav ul { font-size: 14px; line-height: 1.4; border-radius: 10px; - color: #647687; + color: var(--dropdown-link-color); font-size: 14px; - background-color: white; - background-image: linear-gradient(to bottom, rgb(239 244 247), rgba(252,252,252,0)); + background-color: var(--dropdown-linkbox); + background-image: var(--dropdown-featured-bg); + /* background-image: linear-gradient(to bottom, rgb(239 244 247), rgba(252,252,252,0)); */ /* background: linear-gradient(137deg, rgb(241 251 247) 0%, rgb(242 248 255) 100%); */ flex: 1; - transition: background-color 150ms; box-shadow: 0 1px 2px rgb(0 0 0 / .2); } @@ -296,10 +410,19 @@ nav ul { background-color: rgb(223, 233, 238); /* rgb(232, 255, 254); */ } +.dark .dropdown .featured a:hover { + background-color: rgb(64, 82, 92); +} + .dropdown .featured a b { display: block; - color: #384f61; + color: var(--dropdown-link-title-color); font-size: 16px; margin-bottom: .5em; font-weight: 600; +} + + +#current-theme { + text-transform: capitalize; } \ No newline at end of file diff --git a/new/resources/css/docs.css b/new/resources/css/docs.css new file mode 100644 index 0000000..1a8a435 --- /dev/null +++ b/new/resources/css/docs.css @@ -0,0 +1,670 @@ +html, +body { + min-height: 100%; +} + +.wrapper { + max-width: 1800px; +} + +:root { + --header-bg: white; +} + +.dark { + --header-bg: rgba(44, 130, 164, 0.11); +} + +header { + border-bottom: 1px solid var(--header-border-color); +} + +.topbar { + border-bottom: 0; +} +.button.primary { + background: linear-gradient(135deg, #7ece98 25%, rgb(49, 155, 208) 80%); + color: white; +} +.button.primary:hover { + color: white; +} +.button.secondary { + color: rgb(64, 131, 153); + border: 1px solid rgb(226, 242, 247); +} +.button.secondary:hover { + color: rgb(7, 86, 134); + border-color: rgb(155, 191, 213); +} + + +main { + margin-top: 50px; + font-family: Inter, system-ui; +} + + + + + + + + + + + + + +.docs { + display: flex; + font-size: 16px; +} + +.docs nav { + flex: 1; +} + + + + + + + + + + + + + + + + + + +main nav ul { + list-style-type: none; +} + +main nav li { + position: relative; +} + +main nav li li::before { + content: ''; + display: block; + position: absolute; + width: 1px; + height: 100%; + background-color: #d2e5e7; + transition: .15s; +} + +main nav li li:hover::before { + background-color: #62868d; + width: 4px; +} + +main nav ul li a, +main nav .heading, +#autonav a { + padding: 8px 18px 8px 28px; +} +/* +main nav li a, +#pagenav a { + color: #546c75; +} */ + +main nav a { + display: block; + text-decoration: none; + color: inherit; + border-radius: 1.5em; + color: var(--main-nav-link-color); + transition: .15s; +} + + +main nav li:hover > a, +#autonav a:hover, +#pagenav a:hover { + color: var(--main-nav-link-hover-color); +} + + +main nav li:hover > a { + /* background: #f6fafc; */ + box-shadow: -10px 0 10px -3px rgba(0, 0, 0, 0.15); +} + +main nav > ul > li > a::before { + content: '\203A'; + font-weight: bold; + font-size: 150%; + line-height: .75; + position: absolute; + opacity: 0; + left: 0; + transition: left .15s, opacity .15s; +} + +main nav li a:hover::before { + opacity: 1; + left: .75rem; +} + +main nav li a.current { + background: linear-gradient(to right, #ecf1f3, transparent); +} + +main nav .heading { + font-weight: bold; + text-transform: uppercase; + font-size: 80%; + letter-spacing: 1px; +} + +main nav .heading:not(:first-child) { + margin-top: 2.5em; +} + +main nav li li a { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + padding-left: 1em; + transition: padding .15s; +} + +main nav li li a:not(#autonav a) { + font-size: 90%; +} + +main nav li ul { + margin-left: 2.5em; + margin-bottom: .5em; +} + + + + + + + + + + + + + + + + + + +article { + font-family: 'Albert Sans', Figree, Assistant, 'Red Hat Text', 'Be Vietnam Pro', system-ui; + font-size: 20px; + word-wrap: break-word; + + max-width: 1100px; + + /* color: #222; */ +} + +/* +while we want most elements that are rendered +server-side from markdown to have a constrained +width, a few elements should be allowed to +extend to the borders of the page +*/ +article > :not(.fullwidth), +article > .fullwidth > *, +.pad { + padding-left: 8%; + padding-right: 8%; +} +article > :not(h1, hr), +dd, +article p, +article ol, +article ul, +article pre, +article table { + margin-bottom: 1.5rem; +} +article > .fullwidth { margin-bottom: 1.5rem; } +article > .fullwidth > * { margin-bottom: 0; } + +article > pre.chroma > code { + background: none; + padding: 0; +} +article > pre.chroma { + padding-top: 2em; + padding-bottom: 2em; +} + +article ul, +article ol, +#hovercard ul, +#hovercard ol { + margin-left: 2.5em; +} + +article ul ul, +article ol ol, +article ol ul, +article ul ol { + margin-bottom: 0; +} + +article p, +article li { + line-height: 1.75; +} + +article li p, +article li ul, +article li ol { + margin-bottom: .5em; +} + +article h1, +article h2, +article h3, +article h4, +article h5, +article h6 { + /* to ensure that the anchor-link icons stay inside the heading */ + position: relative; + + font-family: Gantari; + font-weight: 800; +} + +article h1, +article h2, +article h3 { + text-align: center; +} + +article h1 { + font-size: 72px; + color: #0e3e5b; + letter-spacing: -2px; + margin-top: 5%; + margin-bottom: 50px; + + background: linear-gradient(to right, #23a1ec, #3fd53a); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +article h2 { + font-size: 56px; + padding-bottom: 15px; + margin: 100px 0 40px; + + border-image-slice: 1; + border-bottom-width: 10px; + border-bottom-style: solid; + border-image-source: linear-gradient(to left, #23a1ec, #3fd53a); +} + +article h3 { + font-size: 36px; + margin: 50px 0 20px; + font-weight: 600; + text-align: center; +} + +article h4 { + font-size: 24px; + margin: 25px 0 15px; +} + +article h5 { + font-size: 22px; + margin: 2em 0 1em; +} + +.anchor-link { + opacity: 0; + font-size: .6em; + border-radius: 10px; + padding: .3em .5em; + margin-left: .25em; + position: absolute; + top: 5px; + text-decoration: none; +} + +*:hover > .anchor-link, +.anchor-link:focus { + opacity: 1; + text-decoration: none; +} + +.anchor-link:hover { + background-color: rgba(0, 0, 0, .075); +} + +code { + background-color: var(--code-bg); + border-radius: 6px; + padding: 2px 5px; + font-size: 90%; +} + +code.cmd { + background-color: #333; + color: #eaeaea; +} + +pre > code, +pre.chroma, +.group { + display: block; + white-space: pre; +} + +pre > code, +article > pre { + padding: 1em; + line-height: 1.6; + overflow: auto; +} + +pre > code.cmd, +.chroma { + border-radius: 10px; +} + +code.cmd.bash, +code.cmd .bash, +code.cmd.bash-continuation, +code.cmd .bash-continuation { + font-weight: bold; +} + +code.cmd.bash::before, +code.cmd .bash::before { + content: '$'; + margin-right: .5rem; +} + +code.cmd.bash-continuation::before, +code.cmd .bash-continuation::before { + content: '>'; + margin-right: .5rem; +} + +dt:hover .inline-link { + visibility: visible; +} + +dd { + margin-left: 1em; +} + +#field-list-header { + display: none; +} + +.field-name { + display: block; + font-family: 'Source Code Pro', monospace; + margin-top: 2em; + font-weight: bold; + margin-bottom: .5em; +} + +.inline-link { + text-decoration: none; + position: absolute; + margin-left: -1.5em; + /* margin-top: -.1em; */ + padding-right: .3em; + padding-left: .2em; + visibility: hidden; +} + +.inline-link:hover { + text-decoration: none; +} + +hr { + border: none; + border-top: 4px solid var(--link-decoration-color); + margin: 4em auto; + width: 35%; +} + +article img { + max-width: 100%; +} + +iframe { + margin: 1em 0 2em; +} + + + + + + + + + + + +article aside { + position: relative; + font-size: 16px; + margin: 2em auto 3em !important; + max-width: 800px; +} + +article aside.tip, +article aside.advice { + padding-left: calc(8% + 50px) !important; +} + +article aside.tip::before, +article aside.advice::before { + font-size: 45px; + position: absolute; + top: -4px; + left: 8%; +} + +article aside.tip { + color: #706b95; +} +article aside.advice { + color: #826848; +} + +article aside.tip:nth-child(even)::before { + content: '💁‍♀️'; +} +article aside.tip:nth-child(odd)::before { + content: '💁‍♂️'; +} + +article aside.advice::before { + content: '🤦'; +} + +article aside.complete { + color: #6b6b6b; + border: 2px dotted #88db88; + text-align: center; + max-width: 500px; + padding: 15px 25px !important; +} + +article aside.complete::before { + content: '✅ complete'; + color: #39c849; + text-transform: uppercase; + font-size: 14px; + font-weight: bold; + letter-spacing: 1px; + margin-right: 2em; + margin-bottom: .5em; +} + +table { + table-layout: fixed; + border-collapse: collapse; + font-size: 16px; +} + +article > table { + margin: 25px auto; +} + +th, td { + border-bottom: 1px solid #ddd; + padding: 10px; + line-height: 1.4em; + vertical-align: top; + word-wrap: break-word; +} + +th { + text-align: left; + background: #eee; +} + +td code { + font-size: 14px; + word-wrap: break-word; +} + + + + + + + +#autonav { + position: absolute; + visibility: hidden; + left: 75%; + top: 0; + background: #fff; + box-shadow: 0 10px 40px rgb(0 0 0 / .2); + border-radius: 10px; + border-top-left-radius: 0; + min-width: 250px; + max-width: 350px; + z-index: 999; + padding-top: .5em; + padding-bottom: .5em; + max-height: 400px; + overflow: hidden; + overflow-y: auto; + font-size: 14px; +} + +main nav > ul > li:hover #autonav { + visibility: visible; +} + +#autonav .heading { + color: #888; +} + +#autonav a { + transition: none; + border-radius: 0; +} + +#autonav a:hover { + color: #01324b; + background: #f1f7fb; +} + + + + +#pagenav .heading { + padding-left: .75em; +} + +#pagenav a { + display: block; + animation: fadeIn 500ms; + padding: .75em; + font-size: 90%; +} + +#pagenav a:hover { + background: #f4f7f9; +} + +@keyframes fadeIn { + 0% { opacity: 0; transform: translateY(2em); } + 100% { opacity: 1; transform: translateY(0); } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.dark main nav li a.current { + background: linear-gradient(to right, #1a3c4d, transparent); +} + +.dark main nav a { + color: #7d8e93; +} + +.dark main nav li:hover > a { + box-shadow: -10px 0 10px -3px rgba(255, 255, 255, 0.15); +} + +.dark main nav li li::before { + background-color: #32494f +} + +.dark #autonav { + background: #36383a; +} + +.dark #pagenav a:hover { + background: #18262f; +} + +.dark code.cmd { + background: black; +} \ No newline at end of file diff --git a/new/resources/images/logo-dark.svg b/new/resources/images/logo-dark.svg new file mode 100644 index 0000000..8652614 --- /dev/null +++ b/new/resources/images/logo-dark.svg @@ -0,0 +1,73 @@ + + + + + + + + + diff --git a/new/resources/images/logo-light.svg b/new/resources/images/logo-light.svg new file mode 100644 index 0000000..6be9b71 --- /dev/null +++ b/new/resources/images/logo-light.svg @@ -0,0 +1,73 @@ + + + + + + + + + diff --git a/new/resources/images/logo.svg b/new/resources/images/logo.svg deleted file mode 100644 index 40d96f3..0000000 --- a/new/resources/images/logo.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/new/resources/js/common.js b/new/resources/js/common.js index e69de29..36e6d80 100644 --- a/new/resources/js/common.js +++ b/new/resources/js/common.js @@ -0,0 +1,160 @@ +// AJQuery: https://github.com/coolaj86/ajquery.js (modified slightly by me) +function $(sel, el) { return ((typeof el === 'string' ? $(el) : el) || document).querySelector(sel); } +function $$(sel, el) { return (el || document).querySelectorAll(sel); } + +function ready(fn) { + if (document.readyState !== 'loading') { + fn(); + } else { + document.addEventListener('DOMContentLoaded', fn); + } +} + +function on(eventName, elemSelector, handler, capture) { + let events = [eventName]; + if (eventName.indexOf(',') >= 0) { + events = eventName.split(','); + } + + events.forEach(eventName => { + // from youmightnotneedjquery.com + document.addEventListener(eventName.trim(), function (e) { + // loop parent nodes from the target to the delegation node + for (let target = e.target; target && target != this; target = target.parentNode) { + if (NodeList.prototype.isPrototypeOf(elemSelector)) { + for (el of elemSelector) { + if (el == target) { + handler.call(target, e); + return; + } + } + } else if (!elemSelector || target.matches(elemSelector)) { + handler.call(target, e); + return; + } + } + }, capture); // I find capture=true helpful when using :not() selectors to exclude one elem of the node tree + }); +} + + +// cloneTemplate does a deep clone of the