@zvndev/darkmown 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +17 -3
- package/CHANGELOG.md +38 -0
- package/README.md +114 -17
- package/SECURITY.md +21 -18
- package/docs/cli.md +5 -1
- package/docs/spec-alignment.md +31 -4
- package/package.json +1 -1
- package/site/_/base.skin +239 -18
- package/site/_/base.wd +4 -0
- package/site/_/feature-card.wd +4 -0
- package/site/_/footer.wd +4 -0
- package/site/_/nav.wd +5 -1
- package/site/_/scoped-promo.wd +4 -0
- package/site/_/scoped-stat.wd +4 -0
- package/site/pages/404.wd +1 -0
- package/site/pages/app.skin +27 -5
- package/site/pages/app.wd +1 -0
- package/site/pages/blog/_schema.wd +15 -0
- package/site/pages/blog/collections-and-pagination.md +38 -0
- package/site/pages/blog/collections-and-pagination.skin +127 -0
- package/site/pages/blog/drafts-that-never-leak.md +2 -0
- package/site/pages/blog/drafts-that-never-leak.skin +127 -0
- package/site/pages/blog/index.skin +144 -0
- package/site/pages/blog/index.wd +74 -0
- package/site/pages/blog/shipping-feeds.md +1 -0
- package/site/pages/blog/shipping-feeds.skin +127 -0
- package/site/pages/blog/typed-frontmatter.md +45 -0
- package/site/pages/blog/typed-frontmatter.skin +127 -0
- package/site/pages/blog/unfinished-thoughts.skin +127 -0
- package/site/pages/blog/zero-js-by-default.md +31 -0
- package/site/pages/blog/zero-js-by-default.skin +127 -0
- package/site/pages/carousel/index.wd +1 -0
- package/site/pages/compass/index.wd +1 -0
- package/site/pages/data.skin +50 -9
- package/site/pages/data.wd +3 -0
- package/site/pages/docs/-relative-note.wd +4 -0
- package/site/pages/docs/index.skin +27 -0
- package/site/pages/docs/index.wd +115 -11
- package/site/pages/folio/-style.wd +4 -0
- package/site/pages/folio/cart.wd +1 -0
- package/site/pages/folio/index.wd +1 -0
- package/site/pages/forge/index.wd +1 -0
- package/site/pages/highlight/index.wd +1 -0
- package/site/pages/index.wd +1 -0
- package/site/pages/interactions/index.wd +1 -0
- package/site/pages/ledger/index.wd +1 -0
- package/site/pages/loops.skin +43 -6
- package/site/pages/loops.wd +1 -0
- package/site/pages/media/index.wd +1 -0
- package/site/pages/pulse/index.wd +1 -0
- package/site/pages/reactive.skin +38 -6
- package/site/pages/reactive.wd +1 -0
- package/site/pages/scoped/index.wd +1 -0
- package/site/pages/showcase/index.wd +1 -0
- package/site/pages/store.skin +43 -6
- package/site/pages/store.wd +1 -0
- package/site/pages/theme.wd +1 -0
- package/src/builder.js +535 -48
- package/src/cli.js +96 -18
- package/src/compiler/actions.js +303 -0
- package/src/compiler/body.js +36 -24
- package/src/compiler/collections.js +316 -0
- package/src/compiler/context.js +83 -6
- package/src/compiler/directives.js +31 -1470
- package/src/compiler/fetch.js +159 -0
- package/src/compiler/format.js +17 -4
- package/src/compiler/forms.js +456 -0
- package/src/compiler/frontmatter.js +10 -5
- package/src/compiler/includes.js +66 -1
- package/src/compiler/index.js +15 -3
- package/src/compiler/loops.js +181 -31
- package/src/compiler/markdown.js +65 -15
- package/src/compiler/media.js +133 -0
- package/src/compiler/page.js +78 -41
- package/src/compiler/state.js +168 -0
- package/src/compiler/structure.js +377 -0
- package/src/dev.js +76 -0
- package/src/headers.js +56 -17
- package/src/router.js +12 -1
- package/src/runtime.js +5 -3
- package/src/statics.js +63 -12
- package/src/templates/blog/README.md +13 -4
- package/src/templates/blog/site/_/nav.wd +4 -0
- package/src/templates/blog/site/pages/index.skin +87 -0
- package/src/templates/blog/site/pages/index.wd +10 -4
- package/src/templates/blog/site/pages/posts/_schema.wd +16 -0
- package/src/templates/blog/site/pages/posts/hello-darkmown.md +2 -0
- package/src/templates/blog/site/pages/posts/hello-darkmown.skin +59 -0
- package/src/templates/blog/site/pages/posts/markdown-native.md +9 -4
- package/src/templates/blog/site/pages/posts/markdown-native.skin +59 -0
- package/src/templates/dashboard/site/_/nav.wd +4 -0
- package/src/templates/dashboard/site/pages/index.skin +84 -0
- package/src/templates/dashboard/site/pages/index.wd +1 -0
- package/src/templates/landing/site/_/nav.wd +4 -0
- package/src/templates/landing/site/pages/index.skin +113 -6
- package/src/templates/landing/site/pages/index.wd +1 -0
- package/src/templates/starter/site/_/nav.wd +4 -0
- package/src/templates/starter/site/pages/about.md +18 -1
- package/src/templates/starter/site/pages/about.skin +69 -0
- package/src/templates/starter/site/pages/index.skin +81 -3
- package/src/templates/starter/site/pages/index.wd +1 -0
- package/src/templates/store/site/_/nav.wd +4 -0
- package/src/templates/store/site/pages/index.skin +93 -11
- package/src/templates/store/site/pages/index.wd +1 -0
- package/site/pages/blog/index.md +0 -13
- package/src/templates/blog/site/_/posts.json +0 -16
package/src/statics.js
CHANGED
|
@@ -1,19 +1,67 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import http from "node:http";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { BASE_SECURITY_HEADERS, REACTIVE_CSP } from "./headers.js";
|
|
4
|
+
import { BASE_SECURITY_HEADERS, REACTIVE_CSP, STATIC_CSP } from "./headers.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* The security headers (including the right CSP variant) for an HTML response.
|
|
8
|
+
* Mirrors what production ships: a route the build marked reactive gets the
|
|
9
|
+
* relaxed CSP (`'unsafe-eval'` for `new Function`); a static route gets the
|
|
10
|
+
* stricter, eval-free CSP — so a local preview matches the per-route `_headers`
|
|
11
|
+
* / `vercel.json` the same build writes instead of blanket-relaxing every page.
|
|
12
|
+
* Anything not found in `routes.json` (an unknown route, a 404, a missing
|
|
13
|
+
* manifest) falls back to the reactive CSP, exactly like the production
|
|
14
|
+
* `_headers` catch-all block.
|
|
15
|
+
* @param {string} distRoot Absolute path to the build output directory.
|
|
16
|
+
* @param {string} url Request URL (may include a query string).
|
|
17
|
+
* @returns {Record<string, string>}
|
|
12
18
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
function htmlSecurityHeaders(distRoot, url) {
|
|
20
|
+
return {
|
|
21
|
+
...BASE_SECURITY_HEADERS,
|
|
22
|
+
"Content-Security-Policy": routeIsReactive(distRoot, routeOf(url)) ? REACTIVE_CSP : STATIC_CSP
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Normalize a request URL to its clean, trailing-slashed route path so it can be
|
|
28
|
+
* matched against `routes.json` (`/docs` / `/docs/` / `/docs/index.html` all →
|
|
29
|
+
* `/docs/`). Returns null for an unparseable URL.
|
|
30
|
+
* @param {string} url
|
|
31
|
+
* @returns {string | null}
|
|
32
|
+
*/
|
|
33
|
+
function routeOf(url) {
|
|
34
|
+
let pathname;
|
|
35
|
+
try {
|
|
36
|
+
pathname = decodeURIComponent((url || "/").split("?")[0]);
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (pathname.endsWith("/index.html")) pathname = pathname.slice(0, -"index.html".length);
|
|
41
|
+
if (!pathname.endsWith("/")) pathname += "/";
|
|
42
|
+
return pathname;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether the route ships the reactive runtime, per the build's `routes.json`.
|
|
47
|
+
* Defaults to `true` (reactive) when the route is absent or the manifest can't be
|
|
48
|
+
* read — matching the production `_headers` catch-all, so an unknown route is
|
|
49
|
+
* never accidentally served the stricter policy.
|
|
50
|
+
* @param {string} distRoot
|
|
51
|
+
* @param {string | null} route
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
function routeIsReactive(distRoot, route) {
|
|
55
|
+
if (!route) return true;
|
|
56
|
+
try {
|
|
57
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(distRoot, "routes.json"), "utf8"));
|
|
58
|
+
const entry = manifest.find((/** @type {{ route: string }} */ e) => e.route === route);
|
|
59
|
+
if (entry) return Boolean(entry.assets?.runtime);
|
|
60
|
+
} catch {
|
|
61
|
+
/* no/unreadable manifest → reactive default (the catch-all policy) */
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
17
65
|
|
|
18
66
|
/**
|
|
19
67
|
* Serve a built file for a request URL out of `distRoot`, or a 404 page.
|
|
@@ -25,7 +73,10 @@ const HTML_SECURITY_HEADERS = {
|
|
|
25
73
|
export function serve(distRoot, url, res) {
|
|
26
74
|
const file = resolvePublicFile(distRoot, url || "/");
|
|
27
75
|
if (!file || !fs.existsSync(file)) {
|
|
28
|
-
res.writeHead(404, {
|
|
76
|
+
res.writeHead(404, {
|
|
77
|
+
"content-type": "text/html; charset=utf-8",
|
|
78
|
+
...htmlSecurityHeaders(distRoot, url)
|
|
79
|
+
});
|
|
29
80
|
const notFound = path.join(distRoot, "404.html");
|
|
30
81
|
if (fs.existsSync(notFound)) {
|
|
31
82
|
res.end(fs.readFileSync(notFound));
|
|
@@ -36,7 +87,7 @@ export function serve(distRoot, url, res) {
|
|
|
36
87
|
}
|
|
37
88
|
const type = contentType(file);
|
|
38
89
|
const headers = type.startsWith("text/html")
|
|
39
|
-
? { "content-type": type, ...
|
|
90
|
+
? { "content-type": type, ...htmlSecurityHeaders(distRoot, url) }
|
|
40
91
|
: { "content-type": type };
|
|
41
92
|
res.writeHead(200, headers);
|
|
42
93
|
fs.createReadStream(file).pipe(res);
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# Blog — a Darkmown template
|
|
2
2
|
|
|
3
|
-
A markdown-native blog. Posts are plain `.md` files in `site/pages/posts
|
|
4
|
-
|
|
3
|
+
A markdown-native blog. Posts are plain `.md` files in `site/pages/posts/` —
|
|
4
|
+
that folder is a typed content collection, and the home page is one `@loop`
|
|
5
|
+
over it. No manifest, no config. Static pages, zero JavaScript.
|
|
5
6
|
|
|
6
7
|
## Add a post
|
|
7
8
|
|
|
8
|
-
1. Create `site/pages/posts/<slug>.md` with
|
|
9
|
-
|
|
9
|
+
1. Create `site/pages/posts/<slug>.md` with `title`, `date`, and `description`
|
|
10
|
+
in frontmatter (`excerpt` is optional). That's it — the home page lists it
|
|
11
|
+
automatically, newest first, and dated posts land in `rss.xml`.
|
|
12
|
+
2. Optional: copy an existing post's `.skin` beside the new file — a colocated
|
|
13
|
+
`.skin` attaches automatically, keeping the reading design.
|
|
14
|
+
|
|
15
|
+
`site/pages/posts/_schema.wd` type-checks every post's frontmatter at build
|
|
16
|
+
time, so a typo'd or missing field fails the build with a file and line. When
|
|
17
|
+
the list grows, add `paginate 10` to the loop in `site/pages/index.wd` to split
|
|
18
|
+
it into `/page/2/`, `/page/3/`, … automatically.
|
|
10
19
|
|
|
11
20
|
## Commands
|
|
12
21
|
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
tokens
|
|
2
2
|
ink #1d2530
|
|
3
3
|
paper #fbfaf7
|
|
4
|
+
panel #ffffff
|
|
5
|
+
line #e7e3da
|
|
4
6
|
muted #6b7785
|
|
5
7
|
accent #2f6df0
|
|
8
|
+
accent-deep #2356cc
|
|
9
|
+
ring #2f6df0
|
|
10
|
+
radius 10px
|
|
11
|
+
|
|
12
|
+
tokens dark
|
|
13
|
+
ink #e7ecf3
|
|
14
|
+
paper #0f141b
|
|
15
|
+
panel #161d27
|
|
16
|
+
line #28303c
|
|
17
|
+
muted #93a0b2
|
|
18
|
+
accent #5b8cff
|
|
19
|
+
accent-deep #80a6ff
|
|
20
|
+
ring #5b8cff
|
|
6
21
|
|
|
7
22
|
page
|
|
8
23
|
margin 0
|
|
@@ -10,6 +25,7 @@ page
|
|
|
10
25
|
color $ink
|
|
11
26
|
bg $paper
|
|
12
27
|
line-height 1.6
|
|
28
|
+
-webkit-font-smoothing antialiased
|
|
13
29
|
|
|
14
30
|
main
|
|
15
31
|
max-width 720px
|
|
@@ -49,3 +65,74 @@ nav a
|
|
|
49
65
|
margin 0 0 .5rem
|
|
50
66
|
color $muted
|
|
51
67
|
font-size .9rem
|
|
68
|
+
|
|
69
|
+
.post-meta p
|
|
70
|
+
margin 0
|
|
71
|
+
|
|
72
|
+
/* Buttons & form controls — for newsletter signups, :button actions, etc. */
|
|
73
|
+
button
|
|
74
|
+
display inline-flex
|
|
75
|
+
align-items center
|
|
76
|
+
justify-content center
|
|
77
|
+
gap .5rem
|
|
78
|
+
bg $accent
|
|
79
|
+
color white
|
|
80
|
+
border 1px solid $accent
|
|
81
|
+
padding .6rem 1.15rem
|
|
82
|
+
margin .4rem .5rem .4rem 0
|
|
83
|
+
radius 999px
|
|
84
|
+
font-size .94rem
|
|
85
|
+
font-weight 560
|
|
86
|
+
line-height 1.15
|
|
87
|
+
cursor pointer
|
|
88
|
+
appearance none
|
|
89
|
+
box-shadow 0 1px 2px rgba(29,37,48,.08)
|
|
90
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease, background .14s ease, border-color .14s ease
|
|
91
|
+
|
|
92
|
+
button:hover
|
|
93
|
+
bg $accent-deep
|
|
94
|
+
border-color $accent-deep
|
|
95
|
+
transform translateY(-1px)
|
|
96
|
+
box-shadow 0 8px 18px -8px rgba(47,109,240,.55)
|
|
97
|
+
|
|
98
|
+
button:active
|
|
99
|
+
transform translateY(0)
|
|
100
|
+
box-shadow 0 1px 2px rgba(29,37,48,.08)
|
|
101
|
+
|
|
102
|
+
button:focus-visible
|
|
103
|
+
outline 2px solid $ring
|
|
104
|
+
outline-offset 2px
|
|
105
|
+
|
|
106
|
+
button:disabled
|
|
107
|
+
opacity .5
|
|
108
|
+
cursor not-allowed
|
|
109
|
+
box-shadow none
|
|
110
|
+
transform none
|
|
111
|
+
|
|
112
|
+
input, textarea, select
|
|
113
|
+
font inherit
|
|
114
|
+
font-size .95rem
|
|
115
|
+
color $ink
|
|
116
|
+
bg $panel
|
|
117
|
+
border 1px solid $line
|
|
118
|
+
radius $radius
|
|
119
|
+
padding .58rem .8rem
|
|
120
|
+
margin .35rem 0
|
|
121
|
+
width 100%
|
|
122
|
+
max-width 24rem
|
|
123
|
+
appearance none
|
|
124
|
+
transition border-color .14s ease, box-shadow .14s ease
|
|
125
|
+
|
|
126
|
+
input:hover, textarea:hover, select:hover
|
|
127
|
+
border-color $accent
|
|
128
|
+
|
|
129
|
+
input:focus-visible, textarea:focus-visible, select:focus-visible
|
|
130
|
+
outline none
|
|
131
|
+
border-color $accent
|
|
132
|
+
box-shadow 0 0 0 3px rgba(47,109,240,.18)
|
|
133
|
+
|
|
134
|
+
@media (prefers-reduced-motion: reduce)
|
|
135
|
+
button
|
|
136
|
+
transition none
|
|
137
|
+
button:hover
|
|
138
|
+
transform none
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
html: true
|
|
2
3
|
title: My Blog
|
|
3
4
|
description: A markdown-native blog built with Darkmown.
|
|
4
5
|
site_url: https://example.com
|
|
@@ -11,15 +12,20 @@ transitions: true
|
|
|
11
12
|
|
|
12
13
|
# My Blog
|
|
13
14
|
|
|
14
|
-
Posts are plain Markdown files in `site/pages/posts
|
|
15
|
-
|
|
15
|
+
Posts are plain Markdown files in `site/pages/posts/` — that folder is a typed
|
|
16
|
+
content collection, and this index is one `.wd` loop over it. No manifest, no
|
|
17
|
+
config, zero JavaScript; each post's frontmatter is the data.
|
|
16
18
|
|
|
17
|
-
@loop
|
|
19
|
+
@loop posts into post sort by post.date desc
|
|
18
20
|
::: post-card
|
|
19
21
|
### [{ post.title }]({ post.url })
|
|
20
|
-
|
|
22
|
+
::: post-meta
|
|
23
|
+
{ post.date }
|
|
24
|
+
:::
|
|
21
25
|
{ post.excerpt }
|
|
22
26
|
:::
|
|
27
|
+
@empty
|
|
28
|
+
No posts yet — add a `.md` file to `site/pages/posts/`.
|
|
23
29
|
@endloop
|
|
24
30
|
|
|
25
31
|
</main>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Typed schema for the posts collection. Every .md file next to this file is
|
|
3
|
+
# validated against these rules at build time — a missing required field, a
|
|
4
|
+
# wrong type, or an unknown extra field fails the build with a file:line.
|
|
5
|
+
# Append "?" to a type to make a field optional. Page-shell flags a post sets
|
|
6
|
+
# (like transitions) are declared here too. Remove this file to turn
|
|
7
|
+
# validation off (it is opt-in).
|
|
8
|
+
title: string
|
|
9
|
+
date: date
|
|
10
|
+
description: string
|
|
11
|
+
excerpt: string?
|
|
12
|
+
transitions: boolean?
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
The posts collection schema. Posts are plain `.md` files next to this file;
|
|
16
|
+
their frontmatter must match the rules above.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* Colocated post styling — a .skin next to a .md attaches automatically.
|
|
2
|
+
Copy this file beside each new post to keep the reading design. */
|
|
3
|
+
|
|
4
|
+
tokens
|
|
5
|
+
ink #1d2530
|
|
6
|
+
paper #fbfaf7
|
|
7
|
+
panel #ffffff
|
|
8
|
+
line #e7e3da
|
|
9
|
+
muted #6b7785
|
|
10
|
+
accent #2f6df0
|
|
11
|
+
|
|
12
|
+
tokens dark
|
|
13
|
+
ink #e7ecf3
|
|
14
|
+
paper #0f141b
|
|
15
|
+
panel #161d27
|
|
16
|
+
line #28303c
|
|
17
|
+
muted #93a0b2
|
|
18
|
+
accent #5b8cff
|
|
19
|
+
|
|
20
|
+
html
|
|
21
|
+
bg $paper
|
|
22
|
+
|
|
23
|
+
page
|
|
24
|
+
max-width 680px
|
|
25
|
+
margin 0 auto
|
|
26
|
+
padding 2.5rem 1.5rem 4rem
|
|
27
|
+
font ui-serif, Georgia, Cambria, serif
|
|
28
|
+
color $ink
|
|
29
|
+
bg $paper
|
|
30
|
+
line-height 1.7
|
|
31
|
+
-webkit-font-smoothing antialiased
|
|
32
|
+
|
|
33
|
+
h1
|
|
34
|
+
font-size 2.1rem
|
|
35
|
+
line-height 1.2
|
|
36
|
+
margin 1rem 0 1.25rem
|
|
37
|
+
|
|
38
|
+
a
|
|
39
|
+
color $accent
|
|
40
|
+
|
|
41
|
+
code
|
|
42
|
+
font ui-monospace, SFMono-Regular, Menlo, monospace
|
|
43
|
+
font-size .88em
|
|
44
|
+
bg $panel
|
|
45
|
+
border 1px solid $line
|
|
46
|
+
padding .08rem .28rem
|
|
47
|
+
radius 5px
|
|
48
|
+
|
|
49
|
+
pre code
|
|
50
|
+
display block
|
|
51
|
+
padding .9rem 1.1rem
|
|
52
|
+
overflow-x auto
|
|
53
|
+
|
|
54
|
+
blockquote
|
|
55
|
+
margin 1rem 0
|
|
56
|
+
padding .5rem 1.2rem
|
|
57
|
+
border-left 3px solid $accent
|
|
58
|
+
bg $panel
|
|
59
|
+
color $muted
|
|
@@ -6,17 +6,22 @@ excerpt: Every post is a plain .md file; the index is one .wd loop. The date in
|
|
|
6
6
|
transitions: true
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
[← All posts](/)
|
|
10
|
+
|
|
9
11
|
# Markdown-native, by default
|
|
10
12
|
|
|
11
|
-
To add a post, drop a Markdown file in `site/pages/posts
|
|
12
|
-
|
|
13
|
+
To add a post, drop a Markdown file in `site/pages/posts/`. That folder is a
|
|
14
|
+
typed content collection, and the home page loops it directly:
|
|
13
15
|
|
|
14
16
|
```wd
|
|
15
|
-
@loop
|
|
17
|
+
@loop posts into post sort by post.date desc
|
|
16
18
|
### [{ post.title }]({ post.url })
|
|
17
19
|
@endloop
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
No
|
|
22
|
+
No manifest, no build config, no content layer. The file *is* the page, and its
|
|
23
|
+
frontmatter is the data — `site/pages/posts/_schema.wd` type-checks every
|
|
24
|
+
post's `title`, `date`, and `description` at build time, so a typo fails the
|
|
25
|
+
build with a file and line instead of shipping a broken list.
|
|
21
26
|
|
|
22
27
|
[← Back to all posts](/)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* Colocated post styling — a .skin next to a .md attaches automatically.
|
|
2
|
+
Copy this file beside each new post to keep the reading design. */
|
|
3
|
+
|
|
4
|
+
tokens
|
|
5
|
+
ink #1d2530
|
|
6
|
+
paper #fbfaf7
|
|
7
|
+
panel #ffffff
|
|
8
|
+
line #e7e3da
|
|
9
|
+
muted #6b7785
|
|
10
|
+
accent #2f6df0
|
|
11
|
+
|
|
12
|
+
tokens dark
|
|
13
|
+
ink #e7ecf3
|
|
14
|
+
paper #0f141b
|
|
15
|
+
panel #161d27
|
|
16
|
+
line #28303c
|
|
17
|
+
muted #93a0b2
|
|
18
|
+
accent #5b8cff
|
|
19
|
+
|
|
20
|
+
html
|
|
21
|
+
bg $paper
|
|
22
|
+
|
|
23
|
+
page
|
|
24
|
+
max-width 680px
|
|
25
|
+
margin 0 auto
|
|
26
|
+
padding 2.5rem 1.5rem 4rem
|
|
27
|
+
font ui-serif, Georgia, Cambria, serif
|
|
28
|
+
color $ink
|
|
29
|
+
bg $paper
|
|
30
|
+
line-height 1.7
|
|
31
|
+
-webkit-font-smoothing antialiased
|
|
32
|
+
|
|
33
|
+
h1
|
|
34
|
+
font-size 2.1rem
|
|
35
|
+
line-height 1.2
|
|
36
|
+
margin 1rem 0 1.25rem
|
|
37
|
+
|
|
38
|
+
a
|
|
39
|
+
color $accent
|
|
40
|
+
|
|
41
|
+
code
|
|
42
|
+
font ui-monospace, SFMono-Regular, Menlo, monospace
|
|
43
|
+
font-size .88em
|
|
44
|
+
bg $panel
|
|
45
|
+
border 1px solid $line
|
|
46
|
+
padding .08rem .28rem
|
|
47
|
+
radius 5px
|
|
48
|
+
|
|
49
|
+
pre code
|
|
50
|
+
display block
|
|
51
|
+
padding .9rem 1.1rem
|
|
52
|
+
overflow-x auto
|
|
53
|
+
|
|
54
|
+
blockquote
|
|
55
|
+
margin 1rem 0
|
|
56
|
+
padding .5rem 1.2rem
|
|
57
|
+
border-left 3px solid $accent
|
|
58
|
+
bg $panel
|
|
59
|
+
color $muted
|
|
@@ -2,9 +2,13 @@ tokens
|
|
|
2
2
|
ink #0f1722
|
|
3
3
|
paper #0b1018
|
|
4
4
|
panel #131c28
|
|
5
|
+
field-bg #0e1620
|
|
5
6
|
line #243245
|
|
6
7
|
muted #8aa0b8
|
|
7
8
|
accent #4f9cff
|
|
9
|
+
accent-deep #6fb0ff
|
|
10
|
+
ring #4f9cff
|
|
11
|
+
radius 12px
|
|
8
12
|
|
|
9
13
|
page
|
|
10
14
|
margin 0
|
|
@@ -12,6 +16,7 @@ page
|
|
|
12
16
|
color #e7eefc
|
|
13
17
|
bg $paper
|
|
14
18
|
line-height 1.55
|
|
19
|
+
-webkit-font-smoothing antialiased
|
|
15
20
|
|
|
16
21
|
main
|
|
17
22
|
max-width 880px
|
|
@@ -72,3 +77,82 @@ nav a
|
|
|
72
77
|
color $muted
|
|
73
78
|
font-size .85rem
|
|
74
79
|
margin-top .5rem
|
|
80
|
+
|
|
81
|
+
/* Buttons & controls — refresh, filters, range pickers, etc. */
|
|
82
|
+
button
|
|
83
|
+
display inline-flex
|
|
84
|
+
align-items center
|
|
85
|
+
justify-content center
|
|
86
|
+
gap .5rem
|
|
87
|
+
bg $accent
|
|
88
|
+
color #07101d
|
|
89
|
+
border 1px solid $accent
|
|
90
|
+
padding .55rem 1.05rem
|
|
91
|
+
margin .4rem .5rem .4rem 0
|
|
92
|
+
radius 999px
|
|
93
|
+
font-size .92rem
|
|
94
|
+
font-weight 600
|
|
95
|
+
line-height 1.15
|
|
96
|
+
cursor pointer
|
|
97
|
+
appearance none
|
|
98
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease, background .14s ease, border-color .14s ease
|
|
99
|
+
|
|
100
|
+
button:hover
|
|
101
|
+
bg $accent-deep
|
|
102
|
+
border-color $accent-deep
|
|
103
|
+
transform translateY(-1px)
|
|
104
|
+
box-shadow 0 8px 20px -8px rgba(79,156,255,.6)
|
|
105
|
+
|
|
106
|
+
button:active
|
|
107
|
+
transform translateY(0)
|
|
108
|
+
box-shadow none
|
|
109
|
+
|
|
110
|
+
button:focus-visible
|
|
111
|
+
outline 2px solid $ring
|
|
112
|
+
outline-offset 2px
|
|
113
|
+
|
|
114
|
+
button:disabled
|
|
115
|
+
opacity .5
|
|
116
|
+
cursor not-allowed
|
|
117
|
+
transform none
|
|
118
|
+
|
|
119
|
+
button.ghost
|
|
120
|
+
bg transparent
|
|
121
|
+
color #e7eefc
|
|
122
|
+
border-color $line
|
|
123
|
+
|
|
124
|
+
button.ghost:hover
|
|
125
|
+
bg $panel
|
|
126
|
+
border-color $accent
|
|
127
|
+
box-shadow none
|
|
128
|
+
|
|
129
|
+
input, select, textarea
|
|
130
|
+
font inherit
|
|
131
|
+
font-size .95rem
|
|
132
|
+
color #e7eefc
|
|
133
|
+
bg $field-bg
|
|
134
|
+
border 1px solid $line
|
|
135
|
+
radius 10px
|
|
136
|
+
padding .55rem .8rem
|
|
137
|
+
margin .35rem 0
|
|
138
|
+
width 100%
|
|
139
|
+
max-width 24rem
|
|
140
|
+
appearance none
|
|
141
|
+
transition border-color .14s ease, box-shadow .14s ease
|
|
142
|
+
|
|
143
|
+
input::placeholder, textarea::placeholder
|
|
144
|
+
color $muted
|
|
145
|
+
|
|
146
|
+
input:hover, select:hover, textarea:hover
|
|
147
|
+
border-color #2f4763
|
|
148
|
+
|
|
149
|
+
input:focus-visible, select:focus-visible, textarea:focus-visible
|
|
150
|
+
outline none
|
|
151
|
+
border-color $accent
|
|
152
|
+
box-shadow 0 0 0 3px rgba(79,156,255,.22)
|
|
153
|
+
|
|
154
|
+
@media (prefers-reduced-motion: reduce)
|
|
155
|
+
button
|
|
156
|
+
transition none
|
|
157
|
+
button:hover
|
|
158
|
+
transform none
|