@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/package.json
CHANGED
package/site/_/base.skin
CHANGED
|
@@ -37,6 +37,13 @@ tokens
|
|
|
37
37
|
radius 12px
|
|
38
38
|
radius-sm 8px
|
|
39
39
|
radius-lg 20px
|
|
40
|
+
field-bg #ffffff
|
|
41
|
+
field-line #d9d2c4
|
|
42
|
+
field-line-strong #c4baa8
|
|
43
|
+
control-shadow 0 1px 2px rgba(28,24,20,.06)
|
|
44
|
+
control-shadow-hover 0 6px 18px -8px rgba(28,24,20,.45)
|
|
45
|
+
accent-press #094339
|
|
46
|
+
on-accent #ffffff
|
|
40
47
|
|
|
41
48
|
tokens dark
|
|
42
49
|
ink #ece6db
|
|
@@ -66,6 +73,13 @@ tokens dark
|
|
|
66
73
|
code-number #f2bb8a
|
|
67
74
|
code-punctuation #b6c2bc
|
|
68
75
|
ring #5ccab4
|
|
76
|
+
field-bg #1a1612
|
|
77
|
+
field-line #38301f
|
|
78
|
+
field-line-strong #4a3f2c
|
|
79
|
+
control-shadow 0 1px 2px rgba(0,0,0,.4)
|
|
80
|
+
control-shadow-hover 0 8px 22px -10px rgba(0,0,0,.7)
|
|
81
|
+
accent-press #b6ecdc
|
|
82
|
+
on-accent #0c1310
|
|
69
83
|
|
|
70
84
|
/* ── Reset & base ───────────────────────────────────────────────── */
|
|
71
85
|
|
|
@@ -156,45 +170,242 @@ main
|
|
|
156
170
|
color $accent
|
|
157
171
|
margin 0 0 .35rem
|
|
158
172
|
|
|
159
|
-
/* ── Buttons
|
|
160
|
-
|
|
161
|
-
.btn
|
|
173
|
+
/* ── Buttons ────────────────────────────────────────────────────────
|
|
174
|
+
Two ways to get a button, ONE consistent look:
|
|
175
|
+
1. The `.btn` class (for link-styled buttons: <a class="btn">).
|
|
176
|
+
2. A bare <button> — what the `:button`/`:submit` directives emit with
|
|
177
|
+
no class. Both share the same sizing, states, and tokens so a demo,
|
|
178
|
+
a scaffold, or a docs page looks designed without per-page styling.
|
|
179
|
+
The bare-button default is the ACCENT (filled) treatment, because a
|
|
180
|
+
directive button is almost always the page's primary action. Opt down
|
|
181
|
+
to a quieter look with class="secondary" / class="ghost".
|
|
182
|
+
─────────────────────────────────────────────────────────────────── */
|
|
183
|
+
|
|
184
|
+
/* Shared geometry + motion for every button-shaped thing. One-line comma
|
|
185
|
+
selector (multi-line comma lists silently drop all but the last). */
|
|
186
|
+
.btn, button, button[data-wd-action], button[data-wd-actions], [type=submit], [type=button], [type=reset]
|
|
162
187
|
display inline-flex
|
|
163
188
|
align-items center
|
|
189
|
+
justify-content center
|
|
164
190
|
gap .5rem
|
|
165
191
|
margin .4rem .5rem .4rem 0
|
|
166
|
-
padding .
|
|
167
|
-
font-
|
|
168
|
-
font-
|
|
169
|
-
|
|
192
|
+
padding .62rem 1.15rem
|
|
193
|
+
font-family system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif
|
|
194
|
+
font-size .94rem
|
|
195
|
+
font-weight 560
|
|
196
|
+
line-height 1.15
|
|
197
|
+
letter-spacing -.005em
|
|
170
198
|
color $ink
|
|
171
199
|
bg $panel
|
|
172
200
|
border 1px solid $line
|
|
173
201
|
radius 999px
|
|
174
202
|
text-decoration none
|
|
175
203
|
cursor pointer
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
.
|
|
179
|
-
transform translateY(-1px)
|
|
180
|
-
box-shadow 0 6px 18px -8px rgba(28,24,20,.35)
|
|
204
|
+
-webkit-appearance none
|
|
205
|
+
appearance none
|
|
206
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease, background .14s ease, border-color .14s ease, color .14s ease
|
|
181
207
|
|
|
182
|
-
|
|
208
|
+
/* The filled accent default — applies to bare directive buttons and to
|
|
209
|
+
the opt-in `.btn.primary` / `.btn.warm` link variants. One-line list. */
|
|
210
|
+
button, button[data-wd-action], button[data-wd-actions], [type=submit], .btn.primary
|
|
183
211
|
bg $accent
|
|
184
|
-
color
|
|
212
|
+
color $on-accent
|
|
185
213
|
border-color $accent
|
|
214
|
+
box-shadow $control-shadow
|
|
186
215
|
|
|
187
|
-
.btn.primary:hover
|
|
216
|
+
button:hover, button[data-wd-action]:hover, button[data-wd-actions]:hover, [type=submit]:hover, .btn.primary:hover
|
|
188
217
|
bg $accent-deep
|
|
189
|
-
color
|
|
218
|
+
border-color $accent-deep
|
|
219
|
+
color $on-accent
|
|
220
|
+
transform translateY(-1px)
|
|
221
|
+
box-shadow $control-shadow-hover
|
|
222
|
+
|
|
223
|
+
button:active, button[data-wd-action]:active, button[data-wd-actions]:active, [type=submit]:active, .btn.primary:active
|
|
224
|
+
bg $accent-press
|
|
225
|
+
border-color $accent-press
|
|
226
|
+
transform translateY(0)
|
|
227
|
+
box-shadow $control-shadow
|
|
228
|
+
|
|
229
|
+
/* Visible keyboard focus on every interactive control — accessibility. */
|
|
230
|
+
.btn:focus-visible, button:focus-visible, [type=submit]:focus-visible, [type=button]:focus-visible, [type=reset]:focus-visible
|
|
231
|
+
outline 2px solid $ring
|
|
232
|
+
outline-offset 2px
|
|
233
|
+
|
|
234
|
+
/* Skip-to-content link (emitted by the page shell) — themed reveal on focus. */
|
|
235
|
+
.wd-skip-link:focus
|
|
236
|
+
bg $accent
|
|
237
|
+
color $on-accent
|
|
238
|
+
border-radius 0 0 $radius-sm 0
|
|
239
|
+
outline 2px solid $ring
|
|
240
|
+
outline-offset 2px
|
|
241
|
+
|
|
242
|
+
button:disabled, [type=submit]:disabled, .btn[aria-disabled=true], [type=button]:disabled
|
|
243
|
+
opacity .5
|
|
244
|
+
cursor not-allowed
|
|
245
|
+
box-shadow none
|
|
246
|
+
transform none
|
|
247
|
+
|
|
248
|
+
/* Quieter, neutral button — a secondary/cancel action. One-line list. */
|
|
249
|
+
.btn, button.secondary, button.ghost, .btn.secondary, .btn.ghost
|
|
250
|
+
bg $panel
|
|
251
|
+
color $ink
|
|
252
|
+
border-color $line
|
|
253
|
+
|
|
254
|
+
.btn:hover
|
|
255
|
+
transform translateY(-1px)
|
|
256
|
+
box-shadow $control-shadow-hover
|
|
257
|
+
|
|
258
|
+
button.secondary:hover, .btn.secondary:hover
|
|
259
|
+
bg $sunk
|
|
260
|
+
border-color $line
|
|
261
|
+
color $ink
|
|
262
|
+
transform translateY(-1px)
|
|
263
|
+
box-shadow $control-shadow-hover
|
|
264
|
+
|
|
265
|
+
button.ghost, .btn.ghost
|
|
266
|
+
bg transparent
|
|
267
|
+
border-color transparent
|
|
268
|
+
box-shadow none
|
|
269
|
+
|
|
270
|
+
button.ghost:hover, .btn.ghost:hover
|
|
271
|
+
bg $sunk
|
|
272
|
+
box-shadow none
|
|
273
|
+
transform none
|
|
190
274
|
|
|
191
275
|
.btn.warm
|
|
192
276
|
bg $warm
|
|
193
277
|
color #fff
|
|
194
278
|
border-color $warm
|
|
195
279
|
|
|
196
|
-
.btn.
|
|
197
|
-
bg
|
|
280
|
+
.btn.warm:hover
|
|
281
|
+
bg $warm
|
|
282
|
+
color #fff
|
|
283
|
+
|
|
284
|
+
/* ── Form controls ──────────────────────────────────────────────────
|
|
285
|
+
`:input`/`:bind`/`:textarea`/`:select` emit bare <input>/<textarea>/
|
|
286
|
+
<select> — style them here so forms look designed by default. ─────── */
|
|
287
|
+
|
|
288
|
+
input, textarea, select
|
|
289
|
+
font-family system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif
|
|
290
|
+
font-size .95rem
|
|
291
|
+
line-height 1.4
|
|
292
|
+
color $ink
|
|
293
|
+
bg $field-bg
|
|
294
|
+
border 1px solid $field-line
|
|
295
|
+
radius $radius-sm
|
|
296
|
+
padding .58rem .8rem
|
|
297
|
+
margin .35rem 0
|
|
298
|
+
width 100%
|
|
299
|
+
max-width 28rem
|
|
300
|
+
-webkit-appearance none
|
|
301
|
+
appearance none
|
|
302
|
+
transition border-color .14s ease, box-shadow .14s ease, background .14s ease
|
|
303
|
+
|
|
304
|
+
input::placeholder, textarea::placeholder
|
|
305
|
+
color $faint
|
|
306
|
+
|
|
307
|
+
input:hover, textarea:hover, select:hover
|
|
308
|
+
border-color $field-line-strong
|
|
309
|
+
|
|
310
|
+
input:focus-visible, textarea:focus-visible, select:focus-visible
|
|
311
|
+
outline none
|
|
312
|
+
border-color $accent
|
|
313
|
+
box-shadow 0 0 0 3px $accent-soft
|
|
314
|
+
|
|
315
|
+
input:disabled, textarea:disabled, select:disabled
|
|
316
|
+
opacity .55
|
|
317
|
+
cursor not-allowed
|
|
318
|
+
bg $sunk
|
|
319
|
+
|
|
320
|
+
textarea
|
|
321
|
+
min-height 6.5rem
|
|
322
|
+
resize vertical
|
|
323
|
+
|
|
324
|
+
/* Native select chevron (the appearance reset hides the default arrow). */
|
|
325
|
+
select
|
|
326
|
+
background-image url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23756a5d' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E")
|
|
327
|
+
background-repeat no-repeat
|
|
328
|
+
background-position right .75rem center
|
|
329
|
+
padding-right 2.25rem
|
|
330
|
+
cursor pointer
|
|
331
|
+
|
|
332
|
+
/* Checkbox / radio — accent-tinted, comfortable hit target. The directive
|
|
333
|
+
emits <label><input type=checkbox|radio> Text</label> in .wd-checkboxes /
|
|
334
|
+
.wd-radios groups. One-line comma list. */
|
|
335
|
+
.wd-checkboxes, .wd-radios
|
|
336
|
+
display flex
|
|
337
|
+
flex-direction column
|
|
338
|
+
gap .55rem
|
|
339
|
+
margin .5rem 0
|
|
340
|
+
|
|
341
|
+
.wd-checkboxes label, .wd-radios label
|
|
342
|
+
display flex
|
|
343
|
+
align-items center
|
|
344
|
+
gap .55rem
|
|
345
|
+
cursor pointer
|
|
346
|
+
|
|
347
|
+
[type=checkbox], [type=radio]
|
|
348
|
+
width 1.1rem
|
|
349
|
+
height 1.1rem
|
|
350
|
+
min-width 1.1rem
|
|
351
|
+
margin 0
|
|
352
|
+
max-width none
|
|
353
|
+
padding 0
|
|
354
|
+
accent-color $accent
|
|
355
|
+
cursor pointer
|
|
356
|
+
|
|
357
|
+
[type=checkbox]:focus-visible, [type=radio]:focus-visible
|
|
358
|
+
outline 2px solid $ring
|
|
359
|
+
outline-offset 2px
|
|
360
|
+
|
|
361
|
+
label
|
|
362
|
+
font-size .95rem
|
|
363
|
+
color $ink-soft
|
|
364
|
+
|
|
365
|
+
/* The range slider (`:slider`) — tint the track to the brand accent. */
|
|
366
|
+
[type=range]
|
|
367
|
+
accent-color $accent
|
|
368
|
+
cursor pointer
|
|
369
|
+
padding 0
|
|
370
|
+
|
|
371
|
+
/* Framework control buttons (`:carousel` injects these at runtime). They
|
|
372
|
+
must NOT inherit the big accent pill above — reset them to a compact,
|
|
373
|
+
neutral round control. Page skins can still override. One-line lists. */
|
|
374
|
+
.wd-carousel-prev, .wd-carousel-next
|
|
375
|
+
width 2.25rem
|
|
376
|
+
height 2.25rem
|
|
377
|
+
padding 0
|
|
378
|
+
margin 0
|
|
379
|
+
bg $panel
|
|
380
|
+
color $ink
|
|
381
|
+
border 1px solid $line
|
|
382
|
+
radius 999px
|
|
383
|
+
box-shadow $control-shadow
|
|
384
|
+
|
|
385
|
+
.wd-carousel-prev:hover, .wd-carousel-next:hover
|
|
386
|
+
bg $panel
|
|
387
|
+
border-color $accent
|
|
388
|
+
color $accent
|
|
389
|
+
transform none
|
|
390
|
+
box-shadow $control-shadow-hover
|
|
391
|
+
|
|
392
|
+
.wd-carousel-dot
|
|
393
|
+
width .6rem
|
|
394
|
+
height .6rem
|
|
395
|
+
padding 0
|
|
396
|
+
margin 0
|
|
397
|
+
border 0
|
|
398
|
+
radius 999px
|
|
399
|
+
bg $line
|
|
400
|
+
box-shadow none
|
|
401
|
+
|
|
402
|
+
.wd-carousel-dot:hover
|
|
403
|
+
bg $faint
|
|
404
|
+
transform none
|
|
405
|
+
box-shadow none
|
|
406
|
+
|
|
407
|
+
.wd-carousel-dot[aria-current=true]
|
|
408
|
+
bg $accent
|
|
198
409
|
|
|
199
410
|
/* ── Cards & surfaces ───────────────────────────────────────────── */
|
|
200
411
|
|
|
@@ -359,6 +570,16 @@ blockquote
|
|
|
359
570
|
margin 0
|
|
360
571
|
radius 0
|
|
361
572
|
|
|
573
|
+
/* ── Motion preference ──────────────────────────────────────────────
|
|
574
|
+
Honour users who ask for less motion: drop the button lift/transition.
|
|
575
|
+
(@media must be indented in a .skin — its rules wrap automatically.) ── */
|
|
576
|
+
|
|
577
|
+
@media (prefers-reduced-motion: reduce)
|
|
578
|
+
.btn, button, [type=submit], [type=button], input, textarea, select
|
|
579
|
+
transition none
|
|
580
|
+
.btn:hover, button:hover, [type=submit]:hover
|
|
581
|
+
transform none
|
|
582
|
+
|
|
362
583
|
/* ── Footer ─────────────────────────────────────────────────────── */
|
|
363
584
|
|
|
364
585
|
.sitefooter
|
package/site/_/base.wd
CHANGED
package/site/_/feature-card.wd
CHANGED
package/site/_/footer.wd
CHANGED
package/site/_/nav.wd
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
html: true
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
<nav class="topnav">
|
|
2
6
|
<a class="brand" href="/">Darkmown</a>
|
|
3
7
|
<span class="nav-links">
|
|
4
8
|
<a href="/docs/">Docs</a>
|
|
9
|
+
<a href="/blog/">Blog</a>
|
|
5
10
|
<a href="/showcase/">Showcase</a>
|
|
6
11
|
<a href="/reactive/">Reactive</a>
|
|
7
12
|
<a href="/data/">Data</a>
|
|
8
|
-
<a href="/highlight/">Highlight</a>
|
|
9
13
|
</span>
|
|
10
14
|
<a class="nav-gh" href="https://github.com/ZVN-DEV/darkmown">GitHub ↗</a>
|
|
11
15
|
</nav>
|
package/site/_/scoped-promo.wd
CHANGED
package/site/_/scoped-stat.wd
CHANGED
package/site/pages/404.wd
CHANGED
package/site/pages/app.skin
CHANGED
|
@@ -57,20 +57,42 @@ h1
|
|
|
57
57
|
color #9a2a2a
|
|
58
58
|
|
|
59
59
|
button
|
|
60
|
+
display inline-flex
|
|
61
|
+
align-items center
|
|
62
|
+
justify-content center
|
|
63
|
+
gap .4rem
|
|
60
64
|
background $accent
|
|
61
65
|
color white
|
|
62
|
-
border
|
|
63
|
-
padding .55rem
|
|
66
|
+
border 1px solid $accent
|
|
67
|
+
padding .55rem 1rem
|
|
64
68
|
margin .25rem .4rem .25rem 0
|
|
65
|
-
border-radius
|
|
66
|
-
font-size .
|
|
69
|
+
border-radius 999px
|
|
70
|
+
font-size .92rem
|
|
71
|
+
font-weight 560
|
|
67
72
|
cursor pointer
|
|
73
|
+
appearance none
|
|
74
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease
|
|
75
|
+
|
|
76
|
+
button:hover
|
|
77
|
+
transform translateY(-1px)
|
|
78
|
+
box-shadow 0 8px 18px -8px rgba(15,107,94,.5)
|
|
79
|
+
|
|
80
|
+
button:focus-visible
|
|
81
|
+
outline 2px solid $accent
|
|
82
|
+
outline-offset 2px
|
|
68
83
|
|
|
69
84
|
input
|
|
70
85
|
font-size 1rem
|
|
71
|
-
padding .
|
|
86
|
+
padding .55rem .75rem
|
|
72
87
|
border 1px solid $line
|
|
73
88
|
border-radius 8px
|
|
74
89
|
width 100%
|
|
75
90
|
max-width 22rem
|
|
76
91
|
margin .3rem 0
|
|
92
|
+
appearance none
|
|
93
|
+
transition border-color .14s ease, box-shadow .14s ease
|
|
94
|
+
|
|
95
|
+
input:focus-visible
|
|
96
|
+
outline none
|
|
97
|
+
border-color $accent
|
|
98
|
+
box-shadow 0 0 0 3px rgba(15,107,94,.18)
|
package/site/pages/app.wd
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
html: true
|
|
2
3
|
title: A live app in one Markdown file
|
|
3
4
|
description: One .wd file — static prose, fetched data, looped results with nested conditionals, a persistent counter, and a form that round-trips to a real server. No JavaScript written, no components, no build config.
|
|
4
5
|
---
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Typed schema for the blog collection. Every post under site/pages/blog/ 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. Append
|
|
5
|
+
# "?" to a type to make the field optional. Remove this file to turn validation
|
|
6
|
+
# off (it is opt-in).
|
|
7
|
+
title: string
|
|
8
|
+
date: date
|
|
9
|
+
description: string
|
|
10
|
+
excerpt: string?
|
|
11
|
+
tags: string[]?
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
The blog collection schema. Posts are plain `.md` files next to this file;
|
|
15
|
+
their frontmatter must match the rules above.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Collections and pagination, with zero ceremony
|
|
3
|
+
description: Any folder under site/pages is a queryable collection. Loop it by name, sort it, paginate it — all at build time, all zero-JS.
|
|
4
|
+
date: 2026-06-28
|
|
5
|
+
excerpt: This very page is an entry in a collection. The list you came from is one @loop over a folder — no content root, no marker file, no config.
|
|
6
|
+
tags: [collections, content]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Collections and pagination, with zero ceremony
|
|
10
|
+
|
|
11
|
+
The page you came from is not a hand-maintained list of links. It is one
|
|
12
|
+
`@loop` over a *collection* — and a collection is just a folder. Every `.md`
|
|
13
|
+
and `.wd` file under `site/pages/blog/` is an entry, queryable by the folder's
|
|
14
|
+
bare name:
|
|
15
|
+
|
|
16
|
+
```wd
|
|
17
|
+
@loop blog where draft == false sort by date desc paginate 3 into post
|
|
18
|
+
- [{ post.title }]({ post.url }) — { post.date }
|
|
19
|
+
@endloop
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
No `content/` root to opt into, no marker file to drop in, no config object.
|
|
23
|
+
A folder of posts *is* the collection. Each entry's frontmatter becomes a row,
|
|
24
|
+
plus three fields the framework derives for you: `url` (the entry's route),
|
|
25
|
+
`slug` (its filename), and `excerpt` (the post's first paragraph when you don't
|
|
26
|
+
write one).
|
|
27
|
+
|
|
28
|
+
## It is the same loop you already know
|
|
29
|
+
|
|
30
|
+
There is no second loop syntax. `where`, `sort by`, `reverse`, `offset`,
|
|
31
|
+
`limit`, and the format pipes all work over a collection exactly as they do over
|
|
32
|
+
a JSON file — because under the hood a collection resolves to the same array of
|
|
33
|
+
rows. The only new word is `paginate N`, which slices the listing into static
|
|
34
|
+
pages: page one keeps this listing's clean route, and pages two and up live at
|
|
35
|
+
`/blog/page/2/`, `/blog/page/3/`, and so on. Every one is plain HTML.
|
|
36
|
+
|
|
37
|
+
A pure listing like this ships **zero** framework JavaScript. The pager links —
|
|
38
|
+
newer, older, numbered — are just `<a href>`s to the generated routes.
|
|
@@ -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
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
title: Drafts that never leak
|
|
3
3
|
description: draft true excludes a page everywhere in a production build — dist, routes.json, sitemap, and rss.
|
|
4
4
|
date: 2026-06-27
|
|
5
|
+
excerpt: Set draft true on a post and it vanishes from a production build — dist, routes.json, the sitemap, and the feed — even with a date.
|
|
6
|
+
tags: [drafts, workflow]
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
# Drafts that never leak
|