@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
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* Colocated post styling — a .skin next to a .md attaches automatically, so
|
|
2
|
+
these prose pages stay static (zero JS) yet still read on-brand. A .md can't
|
|
3
|
+
@include /base.wd, so this reading skin carries the site's tokens itself.
|
|
4
|
+
Kept in sync with site/_/base.skin; copy it beside each new post. */
|
|
5
|
+
|
|
6
|
+
tokens
|
|
7
|
+
ink #1c1814
|
|
8
|
+
ink-soft #4b4239
|
|
9
|
+
muted #756a5d
|
|
10
|
+
paper #faf8f3
|
|
11
|
+
panel #ffffff
|
|
12
|
+
sunk #efe9dc
|
|
13
|
+
line #e7e1d4
|
|
14
|
+
accent #0f6b5e
|
|
15
|
+
accent-deep #0a4f45
|
|
16
|
+
accent-soft #e3f0ec
|
|
17
|
+
code-bg #1b2420
|
|
18
|
+
code-ink #e9efe7
|
|
19
|
+
code-line #2c3b34
|
|
20
|
+
radius 12px
|
|
21
|
+
|
|
22
|
+
tokens dark
|
|
23
|
+
ink #ece6db
|
|
24
|
+
ink-soft #c3bbab
|
|
25
|
+
muted #9a9081
|
|
26
|
+
paper #14110d
|
|
27
|
+
panel #1e1913
|
|
28
|
+
sunk #221d17
|
|
29
|
+
line #2f281f
|
|
30
|
+
accent #5ccab4
|
|
31
|
+
accent-deep #8fe0cf
|
|
32
|
+
accent-soft #16302a
|
|
33
|
+
code-bg #100d0a
|
|
34
|
+
code-ink #e4ebe2
|
|
35
|
+
code-line #2a231b
|
|
36
|
+
|
|
37
|
+
html
|
|
38
|
+
bg $paper
|
|
39
|
+
|
|
40
|
+
page
|
|
41
|
+
max-width 720px
|
|
42
|
+
margin 0 auto
|
|
43
|
+
padding 3rem 1.5rem 5rem
|
|
44
|
+
font-family ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, Cambria, serif
|
|
45
|
+
font-size 18px
|
|
46
|
+
line-height 1.7
|
|
47
|
+
color $ink
|
|
48
|
+
bg $paper
|
|
49
|
+
-webkit-font-smoothing antialiased
|
|
50
|
+
text-rendering optimizeLegibility
|
|
51
|
+
|
|
52
|
+
h1
|
|
53
|
+
font-size clamp(2rem, 5vw, 2.8rem)
|
|
54
|
+
line-height 1.12
|
|
55
|
+
letter-spacing -.022em
|
|
56
|
+
margin 1rem 0 1.25rem
|
|
57
|
+
color $ink
|
|
58
|
+
|
|
59
|
+
h2
|
|
60
|
+
font-size 1.5rem
|
|
61
|
+
line-height 1.2
|
|
62
|
+
letter-spacing -.012em
|
|
63
|
+
margin 2.4rem 0 .6rem
|
|
64
|
+
color $ink
|
|
65
|
+
|
|
66
|
+
h3
|
|
67
|
+
font-size 1.2rem
|
|
68
|
+
margin 1.8rem 0 .4rem
|
|
69
|
+
color $ink
|
|
70
|
+
|
|
71
|
+
p
|
|
72
|
+
margin 1.1rem 0
|
|
73
|
+
|
|
74
|
+
a
|
|
75
|
+
color $accent
|
|
76
|
+
text-underline-offset 2px
|
|
77
|
+
|
|
78
|
+
a:hover
|
|
79
|
+
color $accent-deep
|
|
80
|
+
|
|
81
|
+
strong
|
|
82
|
+
font-weight 650
|
|
83
|
+
color $ink
|
|
84
|
+
|
|
85
|
+
hr
|
|
86
|
+
border 0
|
|
87
|
+
border-top 1px solid $line
|
|
88
|
+
margin 2.5rem 0
|
|
89
|
+
|
|
90
|
+
ul, ol
|
|
91
|
+
margin 1.1rem 0
|
|
92
|
+
padding-left 1.4rem
|
|
93
|
+
|
|
94
|
+
li
|
|
95
|
+
margin .35rem 0
|
|
96
|
+
|
|
97
|
+
code
|
|
98
|
+
font-family ui-monospace, "SF Mono", Menlo, Consolas, monospace
|
|
99
|
+
font-size .88em
|
|
100
|
+
bg $sunk
|
|
101
|
+
color $accent-deep
|
|
102
|
+
padding .12rem .36rem
|
|
103
|
+
radius 6px
|
|
104
|
+
|
|
105
|
+
pre
|
|
106
|
+
bg $code-bg
|
|
107
|
+
color $code-ink
|
|
108
|
+
padding 1.2rem 1.35rem
|
|
109
|
+
margin 1.5rem 0
|
|
110
|
+
radius $radius
|
|
111
|
+
overflow-x auto
|
|
112
|
+
line-height 1.62
|
|
113
|
+
font-size .87rem
|
|
114
|
+
box-shadow inset 0 0 0 1px $code-line
|
|
115
|
+
|
|
116
|
+
pre code
|
|
117
|
+
bg transparent
|
|
118
|
+
color inherit
|
|
119
|
+
padding 0
|
|
120
|
+
font-size 1em
|
|
121
|
+
|
|
122
|
+
blockquote
|
|
123
|
+
margin 1.6rem 0
|
|
124
|
+
padding .6rem 1.3rem
|
|
125
|
+
border-left 3px solid $accent
|
|
126
|
+
color $ink-soft
|
|
127
|
+
font-style italic
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* The Darkmown blog index — a collection-driven, paginated listing. Thin layer
|
|
2
|
+
over /base.wd (tokens, typography, dark mode); only the post-card grid and the
|
|
3
|
+
pager live here. Namespaced under .app-blog so it can't leak. */
|
|
4
|
+
|
|
5
|
+
.app-blog
|
|
6
|
+
max-width 52rem
|
|
7
|
+
|
|
8
|
+
.app-blog .blog-head
|
|
9
|
+
margin 2.5rem 0 2rem
|
|
10
|
+
|
|
11
|
+
.app-blog .blog-head .eyebrow
|
|
12
|
+
display inline-block
|
|
13
|
+
font-size .78rem
|
|
14
|
+
font-weight 600
|
|
15
|
+
text-transform uppercase
|
|
16
|
+
letter-spacing .12em
|
|
17
|
+
color var(--accent)
|
|
18
|
+
margin-bottom .6rem
|
|
19
|
+
|
|
20
|
+
.app-blog .blog-head h1
|
|
21
|
+
margin .2rem 0 .8rem
|
|
22
|
+
line-height 1.1
|
|
23
|
+
|
|
24
|
+
.app-blog .blog-head p
|
|
25
|
+
color var(--muted)
|
|
26
|
+
max-width 38rem
|
|
27
|
+
|
|
28
|
+
/* The list of posts. */
|
|
29
|
+
.app-blog .post-list
|
|
30
|
+
display flex
|
|
31
|
+
flex-direction column
|
|
32
|
+
gap 1.1rem
|
|
33
|
+
margin 2rem 0
|
|
34
|
+
|
|
35
|
+
.app-blog .post-card
|
|
36
|
+
background var(--panel)
|
|
37
|
+
border 1px solid var(--line)
|
|
38
|
+
radius var(--radius-lg)
|
|
39
|
+
padding 1.5rem 1.6rem
|
|
40
|
+
transition border-color .15s ease, transform .15s ease, box-shadow .15s ease
|
|
41
|
+
|
|
42
|
+
.app-blog .post-card:hover
|
|
43
|
+
border-color var(--accent)
|
|
44
|
+
transform translateY(-2px)
|
|
45
|
+
box-shadow 0 8px 24px -16px var(--accent-deep)
|
|
46
|
+
|
|
47
|
+
.app-blog .post-meta
|
|
48
|
+
display flex
|
|
49
|
+
align-items center
|
|
50
|
+
flex-wrap wrap
|
|
51
|
+
gap .55rem
|
|
52
|
+
margin-bottom .55rem
|
|
53
|
+
|
|
54
|
+
/* Each meta item (time / tag) renders as its own paragraph; strip the default
|
|
55
|
+
block margins so they sit inline in the flex row. */
|
|
56
|
+
.app-blog .post-meta p
|
|
57
|
+
margin 0
|
|
58
|
+
display inline-flex
|
|
59
|
+
|
|
60
|
+
.app-blog .post-meta time
|
|
61
|
+
font-size .82rem
|
|
62
|
+
font-weight 600
|
|
63
|
+
color var(--muted)
|
|
64
|
+
font-variant-numeric tabular-nums
|
|
65
|
+
|
|
66
|
+
.app-blog .tag
|
|
67
|
+
font-size .72rem
|
|
68
|
+
font-weight 600
|
|
69
|
+
text-transform uppercase
|
|
70
|
+
letter-spacing .06em
|
|
71
|
+
color var(--accent-deep)
|
|
72
|
+
background var(--accent-soft)
|
|
73
|
+
border 1px solid color-mix(in srgb, var(--accent) 30%, transparent)
|
|
74
|
+
padding .15rem .5rem
|
|
75
|
+
radius 999px
|
|
76
|
+
|
|
77
|
+
.app-blog .post-card h3
|
|
78
|
+
margin .1rem 0 .5rem
|
|
79
|
+
font-size 1.4rem
|
|
80
|
+
line-height 1.2
|
|
81
|
+
|
|
82
|
+
.app-blog .post-card h3 a
|
|
83
|
+
color var(--ink)
|
|
84
|
+
text-decoration none
|
|
85
|
+
|
|
86
|
+
.app-blog .post-card h3 a:hover
|
|
87
|
+
color var(--accent)
|
|
88
|
+
|
|
89
|
+
.app-blog .post-excerpt
|
|
90
|
+
color var(--ink-soft)
|
|
91
|
+
margin 0 0 .8rem
|
|
92
|
+
line-height 1.55
|
|
93
|
+
|
|
94
|
+
.app-blog .post-more a
|
|
95
|
+
font-size .88rem
|
|
96
|
+
font-weight 600
|
|
97
|
+
color var(--accent)
|
|
98
|
+
text-decoration none
|
|
99
|
+
|
|
100
|
+
.app-blog .post-more a:hover
|
|
101
|
+
text-decoration underline
|
|
102
|
+
|
|
103
|
+
.app-blog .empty-note
|
|
104
|
+
color var(--muted)
|
|
105
|
+
font-style italic
|
|
106
|
+
padding 2rem
|
|
107
|
+
text-align center
|
|
108
|
+
border 1px dashed var(--line)
|
|
109
|
+
radius var(--radius)
|
|
110
|
+
|
|
111
|
+
/* The pager — plain links to the generated routes, zero JS. */
|
|
112
|
+
.app-blog .pager
|
|
113
|
+
display flex
|
|
114
|
+
align-items center
|
|
115
|
+
justify-content space-between
|
|
116
|
+
gap 1rem
|
|
117
|
+
margin 2.5rem 0 3rem
|
|
118
|
+
padding-top 1.4rem
|
|
119
|
+
border-top 1px solid var(--line)
|
|
120
|
+
|
|
121
|
+
.app-blog .pager-side
|
|
122
|
+
flex 1
|
|
123
|
+
min-width 0
|
|
124
|
+
|
|
125
|
+
.app-blog .pager-side p
|
|
126
|
+
margin 0
|
|
127
|
+
|
|
128
|
+
.app-blog .pager-side--end
|
|
129
|
+
text-align right
|
|
130
|
+
|
|
131
|
+
.app-blog .pager-now
|
|
132
|
+
font-size .85rem
|
|
133
|
+
font-weight 600
|
|
134
|
+
color var(--muted)
|
|
135
|
+
white-space nowrap
|
|
136
|
+
font-variant-numeric tabular-nums
|
|
137
|
+
|
|
138
|
+
.app-blog .pager-link
|
|
139
|
+
font-weight 600
|
|
140
|
+
color var(--accent)
|
|
141
|
+
text-decoration none
|
|
142
|
+
|
|
143
|
+
.app-blog .pager-link:hover
|
|
144
|
+
text-decoration underline
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
html: true
|
|
3
|
+
title: The Darkmown Blog
|
|
4
|
+
description: Notes on shipping Markdown that runs — collections, feeds, drafts, and zero-JS releases. This whole index is one @loop over a folder.
|
|
5
|
+
image: https://darkmown.com/__wd/media/og.png
|
|
6
|
+
transitions: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
@include /nav.wd
|
|
10
|
+
@include /base.wd
|
|
11
|
+
|
|
12
|
+
<main class="wide app-blog">
|
|
13
|
+
|
|
14
|
+
<header class="blog-head">
|
|
15
|
+
<span class="eyebrow">The Darkmown Blog</span>
|
|
16
|
+
|
|
17
|
+
# Notes from the framework
|
|
18
|
+
|
|
19
|
+
Every post below is a plain `.md` file in `site/pages/blog/`. This page is **one
|
|
20
|
+
`@loop` over that folder** — sorted newest-first, paginated, and 100% static
|
|
21
|
+
HTML. No hand-maintained list, no config, no JavaScript.
|
|
22
|
+
|
|
23
|
+
</header>
|
|
24
|
+
|
|
25
|
+
<section class="post-list">
|
|
26
|
+
|
|
27
|
+
@loop blog into post sort by post.date desc paginate 3
|
|
28
|
+
::: card .post-card
|
|
29
|
+
::: post-meta
|
|
30
|
+
<time>{ post.date }</time>
|
|
31
|
+
@loop post.tags into tag
|
|
32
|
+
<span class="tag">{ tag }</span>
|
|
33
|
+
@endloop
|
|
34
|
+
:::
|
|
35
|
+
|
|
36
|
+
### [{ post.title }]({ post.url })
|
|
37
|
+
|
|
38
|
+
::: post-excerpt
|
|
39
|
+
{ post.excerpt }
|
|
40
|
+
:::
|
|
41
|
+
|
|
42
|
+
::: post-more
|
|
43
|
+
[Read post → ]({ post.url })
|
|
44
|
+
:::
|
|
45
|
+
:::
|
|
46
|
+
@empty
|
|
47
|
+
::: empty-note
|
|
48
|
+
No posts yet. Add a dated `.md` file to `site/pages/blog/`.
|
|
49
|
+
:::
|
|
50
|
+
@endloop
|
|
51
|
+
|
|
52
|
+
</section>
|
|
53
|
+
|
|
54
|
+
::: pager
|
|
55
|
+
::: pager-side
|
|
56
|
+
:if page.prev
|
|
57
|
+
[← Newer posts]({ page.prev }){.pager-link}
|
|
58
|
+
:endif
|
|
59
|
+
:::
|
|
60
|
+
|
|
61
|
+
::: pager-now
|
|
62
|
+
Page { page.current } of { page.total }
|
|
63
|
+
:::
|
|
64
|
+
|
|
65
|
+
::: pager-side .pager-side--end
|
|
66
|
+
:if page.next
|
|
67
|
+
[Older posts →]({ page.next }){.pager-link}
|
|
68
|
+
:endif
|
|
69
|
+
:::
|
|
70
|
+
:::
|
|
71
|
+
|
|
72
|
+
</main>
|
|
73
|
+
|
|
74
|
+
@include /footer.wd
|
|
@@ -3,6 +3,7 @@ title: Shipping feeds without a config file
|
|
|
3
3
|
description: Site identity lives in the home page frontmatter — one site_url field turns on sitemap.xml and rss.xml.
|
|
4
4
|
date: 2026-06-26
|
|
5
5
|
excerpt: One reserved frontmatter field on the home page — site_url — turns on the whole feeds story. No config loader, no plugin.
|
|
6
|
+
tags: [feeds, seo]
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Shipping feeds without a config file
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* Colocated post styling — a .skin next to a .md attaches automatically, so
|
|
2
|
+
these prose pages stay static (zero JS) yet still read on-brand. A .md can't
|
|
3
|
+
@include /base.wd, so this reading skin carries the site's tokens itself.
|
|
4
|
+
Kept in sync with site/_/base.skin; copy it beside each new post. */
|
|
5
|
+
|
|
6
|
+
tokens
|
|
7
|
+
ink #1c1814
|
|
8
|
+
ink-soft #4b4239
|
|
9
|
+
muted #756a5d
|
|
10
|
+
paper #faf8f3
|
|
11
|
+
panel #ffffff
|
|
12
|
+
sunk #efe9dc
|
|
13
|
+
line #e7e1d4
|
|
14
|
+
accent #0f6b5e
|
|
15
|
+
accent-deep #0a4f45
|
|
16
|
+
accent-soft #e3f0ec
|
|
17
|
+
code-bg #1b2420
|
|
18
|
+
code-ink #e9efe7
|
|
19
|
+
code-line #2c3b34
|
|
20
|
+
radius 12px
|
|
21
|
+
|
|
22
|
+
tokens dark
|
|
23
|
+
ink #ece6db
|
|
24
|
+
ink-soft #c3bbab
|
|
25
|
+
muted #9a9081
|
|
26
|
+
paper #14110d
|
|
27
|
+
panel #1e1913
|
|
28
|
+
sunk #221d17
|
|
29
|
+
line #2f281f
|
|
30
|
+
accent #5ccab4
|
|
31
|
+
accent-deep #8fe0cf
|
|
32
|
+
accent-soft #16302a
|
|
33
|
+
code-bg #100d0a
|
|
34
|
+
code-ink #e4ebe2
|
|
35
|
+
code-line #2a231b
|
|
36
|
+
|
|
37
|
+
html
|
|
38
|
+
bg $paper
|
|
39
|
+
|
|
40
|
+
page
|
|
41
|
+
max-width 720px
|
|
42
|
+
margin 0 auto
|
|
43
|
+
padding 3rem 1.5rem 5rem
|
|
44
|
+
font-family ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, Cambria, serif
|
|
45
|
+
font-size 18px
|
|
46
|
+
line-height 1.7
|
|
47
|
+
color $ink
|
|
48
|
+
bg $paper
|
|
49
|
+
-webkit-font-smoothing antialiased
|
|
50
|
+
text-rendering optimizeLegibility
|
|
51
|
+
|
|
52
|
+
h1
|
|
53
|
+
font-size clamp(2rem, 5vw, 2.8rem)
|
|
54
|
+
line-height 1.12
|
|
55
|
+
letter-spacing -.022em
|
|
56
|
+
margin 1rem 0 1.25rem
|
|
57
|
+
color $ink
|
|
58
|
+
|
|
59
|
+
h2
|
|
60
|
+
font-size 1.5rem
|
|
61
|
+
line-height 1.2
|
|
62
|
+
letter-spacing -.012em
|
|
63
|
+
margin 2.4rem 0 .6rem
|
|
64
|
+
color $ink
|
|
65
|
+
|
|
66
|
+
h3
|
|
67
|
+
font-size 1.2rem
|
|
68
|
+
margin 1.8rem 0 .4rem
|
|
69
|
+
color $ink
|
|
70
|
+
|
|
71
|
+
p
|
|
72
|
+
margin 1.1rem 0
|
|
73
|
+
|
|
74
|
+
a
|
|
75
|
+
color $accent
|
|
76
|
+
text-underline-offset 2px
|
|
77
|
+
|
|
78
|
+
a:hover
|
|
79
|
+
color $accent-deep
|
|
80
|
+
|
|
81
|
+
strong
|
|
82
|
+
font-weight 650
|
|
83
|
+
color $ink
|
|
84
|
+
|
|
85
|
+
hr
|
|
86
|
+
border 0
|
|
87
|
+
border-top 1px solid $line
|
|
88
|
+
margin 2.5rem 0
|
|
89
|
+
|
|
90
|
+
ul, ol
|
|
91
|
+
margin 1.1rem 0
|
|
92
|
+
padding-left 1.4rem
|
|
93
|
+
|
|
94
|
+
li
|
|
95
|
+
margin .35rem 0
|
|
96
|
+
|
|
97
|
+
code
|
|
98
|
+
font-family ui-monospace, "SF Mono", Menlo, Consolas, monospace
|
|
99
|
+
font-size .88em
|
|
100
|
+
bg $sunk
|
|
101
|
+
color $accent-deep
|
|
102
|
+
padding .12rem .36rem
|
|
103
|
+
radius 6px
|
|
104
|
+
|
|
105
|
+
pre
|
|
106
|
+
bg $code-bg
|
|
107
|
+
color $code-ink
|
|
108
|
+
padding 1.2rem 1.35rem
|
|
109
|
+
margin 1.5rem 0
|
|
110
|
+
radius $radius
|
|
111
|
+
overflow-x auto
|
|
112
|
+
line-height 1.62
|
|
113
|
+
font-size .87rem
|
|
114
|
+
box-shadow inset 0 0 0 1px $code-line
|
|
115
|
+
|
|
116
|
+
pre code
|
|
117
|
+
bg transparent
|
|
118
|
+
color inherit
|
|
119
|
+
padding 0
|
|
120
|
+
font-size 1em
|
|
121
|
+
|
|
122
|
+
blockquote
|
|
123
|
+
margin 1.6rem 0
|
|
124
|
+
padding .6rem 1.3rem
|
|
125
|
+
border-left 3px solid $accent
|
|
126
|
+
color $ink-soft
|
|
127
|
+
font-style italic
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Typed frontmatter with a four-line schema
|
|
3
|
+
description: Drop a _schema.wd in a collection and every entry is validated at build time — missing fields, wrong types, and typos all fail the build.
|
|
4
|
+
date: 2026-06-29
|
|
5
|
+
excerpt: A typo'd frontmatter key used to ship silently. With a collection schema, titel instead of title fails the build with a file and line.
|
|
6
|
+
tags: [collections, schema, dx]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Typed frontmatter with a four-line schema
|
|
10
|
+
|
|
11
|
+
Frontmatter is convenient until a typo bites. You write `titel:` instead of
|
|
12
|
+
`title:`, the field silently becomes empty, and the bug surfaces as a blank
|
|
13
|
+
heading in production. Darkmown closes that gap with an *optional* schema — one
|
|
14
|
+
file at the collection root, frontmatter-shaped, written exactly like the
|
|
15
|
+
frontmatter it checks:
|
|
16
|
+
|
|
17
|
+
```wd
|
|
18
|
+
---
|
|
19
|
+
title: string
|
|
20
|
+
date: date
|
|
21
|
+
description: string
|
|
22
|
+
excerpt: string?
|
|
23
|
+
tags: string[]?
|
|
24
|
+
---
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The vocabulary is small and closed on purpose: `string`, `number`, `boolean`,
|
|
28
|
+
`date`, and `string[]`, with a trailing `?` to make a field optional. That is
|
|
29
|
+
the whole DSL. An unknown type token is a compile error in the schema file
|
|
30
|
+
itself.
|
|
31
|
+
|
|
32
|
+
## What it catches
|
|
33
|
+
|
|
34
|
+
At build time, every entry in the collection is checked against the rules. A
|
|
35
|
+
missing required field, a value of the wrong type, or an extra field the schema
|
|
36
|
+
never declared each fails the build — and the error names the offending entry
|
|
37
|
+
by path and the field by name, so the fix is obvious:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Missing required field "date" (date) in /blog/example/ (collection "blog").
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Validation is opt-in. No `_schema.wd`, no validation — the same posture as
|
|
44
|
+
`scoped` styles and the `draft` flag. Add the file when you want the guardrails;
|
|
45
|
+
leave it out while you are still sketching.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* Colocated post styling — a .skin next to a .md attaches automatically, so
|
|
2
|
+
these prose pages stay static (zero JS) yet still read on-brand. A .md can't
|
|
3
|
+
@include /base.wd, so this reading skin carries the site's tokens itself.
|
|
4
|
+
Kept in sync with site/_/base.skin; copy it beside each new post. */
|
|
5
|
+
|
|
6
|
+
tokens
|
|
7
|
+
ink #1c1814
|
|
8
|
+
ink-soft #4b4239
|
|
9
|
+
muted #756a5d
|
|
10
|
+
paper #faf8f3
|
|
11
|
+
panel #ffffff
|
|
12
|
+
sunk #efe9dc
|
|
13
|
+
line #e7e1d4
|
|
14
|
+
accent #0f6b5e
|
|
15
|
+
accent-deep #0a4f45
|
|
16
|
+
accent-soft #e3f0ec
|
|
17
|
+
code-bg #1b2420
|
|
18
|
+
code-ink #e9efe7
|
|
19
|
+
code-line #2c3b34
|
|
20
|
+
radius 12px
|
|
21
|
+
|
|
22
|
+
tokens dark
|
|
23
|
+
ink #ece6db
|
|
24
|
+
ink-soft #c3bbab
|
|
25
|
+
muted #9a9081
|
|
26
|
+
paper #14110d
|
|
27
|
+
panel #1e1913
|
|
28
|
+
sunk #221d17
|
|
29
|
+
line #2f281f
|
|
30
|
+
accent #5ccab4
|
|
31
|
+
accent-deep #8fe0cf
|
|
32
|
+
accent-soft #16302a
|
|
33
|
+
code-bg #100d0a
|
|
34
|
+
code-ink #e4ebe2
|
|
35
|
+
code-line #2a231b
|
|
36
|
+
|
|
37
|
+
html
|
|
38
|
+
bg $paper
|
|
39
|
+
|
|
40
|
+
page
|
|
41
|
+
max-width 720px
|
|
42
|
+
margin 0 auto
|
|
43
|
+
padding 3rem 1.5rem 5rem
|
|
44
|
+
font-family ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, Cambria, serif
|
|
45
|
+
font-size 18px
|
|
46
|
+
line-height 1.7
|
|
47
|
+
color $ink
|
|
48
|
+
bg $paper
|
|
49
|
+
-webkit-font-smoothing antialiased
|
|
50
|
+
text-rendering optimizeLegibility
|
|
51
|
+
|
|
52
|
+
h1
|
|
53
|
+
font-size clamp(2rem, 5vw, 2.8rem)
|
|
54
|
+
line-height 1.12
|
|
55
|
+
letter-spacing -.022em
|
|
56
|
+
margin 1rem 0 1.25rem
|
|
57
|
+
color $ink
|
|
58
|
+
|
|
59
|
+
h2
|
|
60
|
+
font-size 1.5rem
|
|
61
|
+
line-height 1.2
|
|
62
|
+
letter-spacing -.012em
|
|
63
|
+
margin 2.4rem 0 .6rem
|
|
64
|
+
color $ink
|
|
65
|
+
|
|
66
|
+
h3
|
|
67
|
+
font-size 1.2rem
|
|
68
|
+
margin 1.8rem 0 .4rem
|
|
69
|
+
color $ink
|
|
70
|
+
|
|
71
|
+
p
|
|
72
|
+
margin 1.1rem 0
|
|
73
|
+
|
|
74
|
+
a
|
|
75
|
+
color $accent
|
|
76
|
+
text-underline-offset 2px
|
|
77
|
+
|
|
78
|
+
a:hover
|
|
79
|
+
color $accent-deep
|
|
80
|
+
|
|
81
|
+
strong
|
|
82
|
+
font-weight 650
|
|
83
|
+
color $ink
|
|
84
|
+
|
|
85
|
+
hr
|
|
86
|
+
border 0
|
|
87
|
+
border-top 1px solid $line
|
|
88
|
+
margin 2.5rem 0
|
|
89
|
+
|
|
90
|
+
ul, ol
|
|
91
|
+
margin 1.1rem 0
|
|
92
|
+
padding-left 1.4rem
|
|
93
|
+
|
|
94
|
+
li
|
|
95
|
+
margin .35rem 0
|
|
96
|
+
|
|
97
|
+
code
|
|
98
|
+
font-family ui-monospace, "SF Mono", Menlo, Consolas, monospace
|
|
99
|
+
font-size .88em
|
|
100
|
+
bg $sunk
|
|
101
|
+
color $accent-deep
|
|
102
|
+
padding .12rem .36rem
|
|
103
|
+
radius 6px
|
|
104
|
+
|
|
105
|
+
pre
|
|
106
|
+
bg $code-bg
|
|
107
|
+
color $code-ink
|
|
108
|
+
padding 1.2rem 1.35rem
|
|
109
|
+
margin 1.5rem 0
|
|
110
|
+
radius $radius
|
|
111
|
+
overflow-x auto
|
|
112
|
+
line-height 1.62
|
|
113
|
+
font-size .87rem
|
|
114
|
+
box-shadow inset 0 0 0 1px $code-line
|
|
115
|
+
|
|
116
|
+
pre code
|
|
117
|
+
bg transparent
|
|
118
|
+
color inherit
|
|
119
|
+
padding 0
|
|
120
|
+
font-size 1em
|
|
121
|
+
|
|
122
|
+
blockquote
|
|
123
|
+
margin 1.6rem 0
|
|
124
|
+
padding .6rem 1.3rem
|
|
125
|
+
border-left 3px solid $accent
|
|
126
|
+
color $ink-soft
|
|
127
|
+
font-style italic
|