@zvndev/darkmown 1.5.0 → 2.0.1
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 +18 -4
- package/CHANGELOG.md +40 -0
- package/README.md +49 -19
- package/SECURITY.md +21 -18
- package/docs/cli.md +5 -1
- package/docs/spec-alignment.md +21 -7
- package/package.json +3 -2
- package/site/_/base.skin +8 -0
- package/site/_/base.wd +4 -0
- package/site/_/feature-card.wd +4 -0
- package/site/_/footer.wd +5 -1
- package/site/_/nav.wd +4 -0
- 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.wd +2 -1
- package/site/pages/blog/collections-and-pagination.skin +127 -0
- package/site/pages/blog/drafts-that-never-leak.skin +127 -0
- package/site/pages/blog/index.wd +1 -0
- package/site/pages/blog/shipping-feeds.skin +127 -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 +1 -1
- 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.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 +85 -14
- 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.js +19 -0
- package/site/pages/index.skin +46 -1
- package/site/pages/index.wd +6 -3
- package/site/pages/interactions/index.wd +1 -0
- package/site/pages/ledger/index.wd +1 -0
- 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.wd +1 -0
- package/site/pages/scoped/index.wd +1 -0
- package/site/pages/showcase/index.wd +2 -1
- package/site/pages/store.wd +1 -0
- package/site/pages/theme.wd +1 -0
- package/src/builder.js +444 -65
- package/src/cli.js +96 -18
- package/src/compiler/actions.js +303 -0
- package/src/compiler/body.js +36 -24
- package/src/compiler/context.js +65 -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 +75 -4
- package/src/compiler/index.js +15 -3
- package/src/compiler/loops.js +85 -23
- package/src/compiler/markdown.js +65 -15
- package/src/compiler/media.js +133 -0
- package/src/compiler/page.js +88 -77
- package/src/compiler/state.js +168 -0
- package/src/compiler/structure.js +399 -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 +7 -6
- package/src/templates/blog/site/pages/index.skin +10 -4
- package/src/templates/blog/site/pages/index.wd +11 -6
- 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 +7 -6
- package/src/templates/dashboard/site/pages/index.skin +7 -4
- package/src/templates/dashboard/site/pages/index.wd +2 -2
- package/src/templates/landing/site/_/nav.wd +7 -6
- package/src/templates/landing/site/pages/index.skin +13 -4
- package/src/templates/landing/site/pages/index.wd +22 -8
- package/src/templates/starter/site/_/nav.wd +8 -7
- package/src/templates/starter/site/pages/about.md +8 -2
- package/src/templates/starter/site/pages/about.skin +44 -0
- package/src/templates/starter/site/pages/index.skin +10 -6
- package/src/templates/starter/site/pages/index.wd +2 -2
- package/src/templates/store/site/_/nav.wd +7 -6
- package/src/templates/store/site/pages/index.skin +7 -4
- package/src/templates/store/site/pages/index.wd +2 -2
- 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,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
::: nav
|
|
2
|
+
**My Blog**
|
|
3
|
+
|
|
4
|
+
::: links
|
|
5
|
+
[Posts](/)
|
|
6
|
+
:::
|
|
7
|
+
:::
|
|
@@ -27,12 +27,12 @@ page
|
|
|
27
27
|
line-height 1.6
|
|
28
28
|
-webkit-font-smoothing antialiased
|
|
29
29
|
|
|
30
|
-
main
|
|
30
|
+
.main
|
|
31
31
|
max-width 720px
|
|
32
32
|
margin 0 auto
|
|
33
33
|
padding 2.5rem 1.5rem
|
|
34
34
|
|
|
35
|
-
nav
|
|
35
|
+
.nav
|
|
36
36
|
display flex
|
|
37
37
|
align-items center
|
|
38
38
|
justify-content space-between
|
|
@@ -40,10 +40,13 @@ nav
|
|
|
40
40
|
margin 0 auto
|
|
41
41
|
padding 1.25rem 1.5rem
|
|
42
42
|
|
|
43
|
-
nav
|
|
43
|
+
.nav p
|
|
44
|
+
margin 0
|
|
45
|
+
|
|
46
|
+
.nav strong
|
|
44
47
|
font-size 1.1rem
|
|
45
48
|
|
|
46
|
-
nav a
|
|
49
|
+
.nav a
|
|
47
50
|
color $accent
|
|
48
51
|
text-decoration none
|
|
49
52
|
|
|
@@ -66,6 +69,9 @@ nav a
|
|
|
66
69
|
color $muted
|
|
67
70
|
font-size .9rem
|
|
68
71
|
|
|
72
|
+
.post-meta p
|
|
73
|
+
margin 0
|
|
74
|
+
|
|
69
75
|
/* Buttons & form controls — for newsletter signups, :button actions, etc. */
|
|
70
76
|
button
|
|
71
77
|
display inline-flex
|
|
@@ -7,19 +7,24 @@ transitions: true
|
|
|
7
7
|
|
|
8
8
|
@include /nav.wd
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
::: main
|
|
11
11
|
|
|
12
12
|
# My Blog
|
|
13
13
|
|
|
14
|
-
Posts are plain Markdown files in `site/pages/posts
|
|
15
|
-
|
|
14
|
+
Posts are plain Markdown files in `site/pages/posts/` — that folder is a typed
|
|
15
|
+
content collection, and this index is one `.wd` loop over it. No manifest, no
|
|
16
|
+
config, zero JavaScript; each post's frontmatter is the data.
|
|
16
17
|
|
|
17
|
-
@loop
|
|
18
|
+
@loop posts into post sort by post.date desc
|
|
18
19
|
::: post-card
|
|
19
20
|
### [{ post.title }]({ post.url })
|
|
20
|
-
|
|
21
|
+
::: post-meta
|
|
22
|
+
{ post.date }
|
|
23
|
+
:::
|
|
21
24
|
{ post.excerpt }
|
|
22
25
|
:::
|
|
26
|
+
@empty
|
|
27
|
+
No posts yet — add a `.md` file to `site/pages/posts/`.
|
|
23
28
|
@endloop
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
:::
|
|
@@ -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
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
::: nav
|
|
2
|
+
**Dashboard**
|
|
3
|
+
|
|
4
|
+
::: links
|
|
5
|
+
[Overview](/)
|
|
6
|
+
:::
|
|
7
|
+
:::
|
|
@@ -18,12 +18,12 @@ page
|
|
|
18
18
|
line-height 1.55
|
|
19
19
|
-webkit-font-smoothing antialiased
|
|
20
20
|
|
|
21
|
-
main
|
|
21
|
+
.main
|
|
22
22
|
max-width 880px
|
|
23
23
|
margin 0 auto
|
|
24
24
|
padding 2rem 1.5rem
|
|
25
25
|
|
|
26
|
-
nav
|
|
26
|
+
.nav
|
|
27
27
|
display flex
|
|
28
28
|
align-items center
|
|
29
29
|
justify-content space-between
|
|
@@ -31,10 +31,13 @@ nav
|
|
|
31
31
|
margin 0 auto
|
|
32
32
|
padding 1.25rem 1.5rem
|
|
33
33
|
|
|
34
|
-
nav
|
|
34
|
+
.nav p
|
|
35
|
+
margin 0
|
|
36
|
+
|
|
37
|
+
.nav strong
|
|
35
38
|
font-size 1.1rem
|
|
36
39
|
|
|
37
|
-
nav a
|
|
40
|
+
.nav a
|
|
38
41
|
color $accent
|
|
39
42
|
text-decoration none
|
|
40
43
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
::: nav
|
|
2
|
+
**Acme**
|
|
3
|
+
|
|
4
|
+
::: links
|
|
5
|
+
[Get started](#start)
|
|
6
|
+
:::
|
|
7
|
+
:::
|
|
@@ -27,12 +27,12 @@ page
|
|
|
27
27
|
line-height 1.6
|
|
28
28
|
-webkit-font-smoothing antialiased
|
|
29
29
|
|
|
30
|
-
main
|
|
30
|
+
.main
|
|
31
31
|
max-width 900px
|
|
32
32
|
margin 0 auto
|
|
33
33
|
padding 1rem 1.5rem 4rem
|
|
34
34
|
|
|
35
|
-
nav
|
|
35
|
+
.nav
|
|
36
36
|
display flex
|
|
37
37
|
align-items center
|
|
38
38
|
justify-content space-between
|
|
@@ -40,10 +40,13 @@ nav
|
|
|
40
40
|
margin 0 auto
|
|
41
41
|
padding 1.25rem 1.5rem
|
|
42
42
|
|
|
43
|
-
nav
|
|
43
|
+
.nav p
|
|
44
|
+
margin 0
|
|
45
|
+
|
|
46
|
+
.nav strong
|
|
44
47
|
font-size 1.15rem
|
|
45
48
|
|
|
46
|
-
nav a
|
|
49
|
+
.nav a
|
|
47
50
|
color $accent
|
|
48
51
|
text-decoration none
|
|
49
52
|
|
|
@@ -60,6 +63,9 @@ nav a
|
|
|
60
63
|
color $muted
|
|
61
64
|
margin 0 0 1.5rem
|
|
62
65
|
|
|
66
|
+
.lede p
|
|
67
|
+
margin 0
|
|
68
|
+
|
|
63
69
|
.cta
|
|
64
70
|
display inline-flex
|
|
65
71
|
align-items center
|
|
@@ -186,6 +192,9 @@ input:focus-visible, textarea:focus-visible, select:focus-visible
|
|
|
186
192
|
color $muted
|
|
187
193
|
font-size .95rem
|
|
188
194
|
|
|
195
|
+
.quote .who p
|
|
196
|
+
margin 0
|
|
197
|
+
|
|
189
198
|
.wd-carousel-prev
|
|
190
199
|
position absolute
|
|
191
200
|
left -.5rem
|
|
@@ -6,12 +6,16 @@ transitions: true
|
|
|
6
6
|
|
|
7
7
|
@include /nav.wd
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
::: main
|
|
10
10
|
|
|
11
11
|
::: hero
|
|
12
12
|
# Ship faster with *Acme*
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
::: lede
|
|
15
|
+
The all-in-one toolkit your team actually enjoys using.
|
|
16
|
+
:::
|
|
17
|
+
|
|
18
|
+
[Get started](#start){.cta}
|
|
15
19
|
:::
|
|
16
20
|
|
|
17
21
|
::: features
|
|
@@ -34,21 +38,31 @@ One command to Vercel or Cloudflare — your API comes along for free.
|
|
|
34
38
|
:carousel
|
|
35
39
|
::: quote
|
|
36
40
|
> "Acme cut our launch time in half."
|
|
37
|
-
|
|
41
|
+
|
|
42
|
+
::: who
|
|
43
|
+
— Riley, Head of Product
|
|
44
|
+
:::
|
|
38
45
|
:::
|
|
39
46
|
::: quote
|
|
40
47
|
> "The simplest stack we've ever shipped on."
|
|
41
|
-
|
|
48
|
+
|
|
49
|
+
::: who
|
|
50
|
+
— Sam, CTO
|
|
51
|
+
:::
|
|
42
52
|
:::
|
|
43
53
|
::: quote
|
|
44
54
|
> "Our marketing pages load instantly now."
|
|
45
|
-
|
|
55
|
+
|
|
56
|
+
::: who
|
|
57
|
+
— Jordan, Growth
|
|
58
|
+
:::
|
|
46
59
|
:::
|
|
47
60
|
:endcarousel
|
|
48
61
|
|
|
49
62
|
::: cta-band #start
|
|
50
63
|
## Ready to build?
|
|
51
|
-
|
|
64
|
+
|
|
65
|
+
[Start now](/){.cta}
|
|
52
66
|
:::
|
|
53
67
|
|
|
54
|
-
|
|
68
|
+
:::
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
::: nav
|
|
2
|
+
**My site**
|
|
3
|
+
|
|
4
|
+
::: links
|
|
5
|
+
[Home](/)
|
|
6
|
+
[About](/about/)
|
|
7
|
+
:::
|
|
8
|
+
:::
|
|
@@ -4,7 +4,13 @@ title: About
|
|
|
4
4
|
|
|
5
5
|
# About
|
|
6
6
|
|
|
7
|
-
This page is plain Markdown (`.md`) — strict CommonMark, zero framework
|
|
8
|
-
|
|
7
|
+
This page is plain Markdown (`.md`) — strict CommonMark, zero framework
|
|
8
|
+
JavaScript, and no raw HTML (Darkmown escapes it by default, so untrusted
|
|
9
|
+
content can't inject markup). That's why there's no shared nav bar here: a
|
|
10
|
+
`.md` page can't `@include` or use directives.
|
|
11
|
+
|
|
12
|
+
Rename it to `.wd` when you want that — state, loops, forms, and an
|
|
13
|
+
`@include /nav.wd` for the shared nav. The colocated `about.skin` styles this
|
|
14
|
+
page either way; it attaches automatically by basename, `.md` or `.wd` alike.
|
|
9
15
|
|
|
10
16
|
[Back home](/)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* Colocated page styling — a .skin next to a .md attaches automatically. */
|
|
2
|
+
|
|
3
|
+
tokens
|
|
4
|
+
ink #172026
|
|
5
|
+
muted #5b6b72
|
|
6
|
+
paper #f7f2e8
|
|
7
|
+
panel #ffffff
|
|
8
|
+
line #e4ddcd
|
|
9
|
+
accent #0f8b8d
|
|
10
|
+
|
|
11
|
+
tokens dark
|
|
12
|
+
ink #e8eef0
|
|
13
|
+
muted #93a3a8
|
|
14
|
+
paper #11181b
|
|
15
|
+
panel #182226
|
|
16
|
+
line #2a363a
|
|
17
|
+
accent #3fc6c8
|
|
18
|
+
|
|
19
|
+
html
|
|
20
|
+
bg $paper
|
|
21
|
+
|
|
22
|
+
page
|
|
23
|
+
margin 0
|
|
24
|
+
font ui-sans-serif, system-ui, sans-serif
|
|
25
|
+
color $ink
|
|
26
|
+
bg $paper
|
|
27
|
+
line-height 1.6
|
|
28
|
+
-webkit-font-smoothing antialiased
|
|
29
|
+
|
|
30
|
+
main
|
|
31
|
+
max-width 760px
|
|
32
|
+
margin 0 auto
|
|
33
|
+
padding 3rem 1.5rem
|
|
34
|
+
|
|
35
|
+
a
|
|
36
|
+
color $accent
|
|
37
|
+
|
|
38
|
+
code
|
|
39
|
+
font ui-monospace, SFMono-Regular, Menlo, monospace
|
|
40
|
+
font-size .88em
|
|
41
|
+
bg $panel
|
|
42
|
+
border 1px solid $line
|
|
43
|
+
padding .08rem .28rem
|
|
44
|
+
radius 5px
|