@zvndev/darkmown 0.20.0 → 1.1.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.
Files changed (57) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +29 -0
  3. package/README.md +125 -5
  4. package/docs/spec-alignment.md +23 -3
  5. package/package.json +3 -3
  6. package/site/_/base.skin +372 -0
  7. package/site/_/base.wd +4 -0
  8. package/site/_/footer.wd +14 -0
  9. package/site/_/metrics.json +13 -0
  10. package/site/_/nav.skin +35 -12
  11. package/site/_/nav.wd +8 -10
  12. package/site/_/products.json +10 -0
  13. package/site/pages/app.wd +1 -1
  14. package/site/pages/carousel/index.js +82 -0
  15. package/site/pages/carousel/index.skin +185 -0
  16. package/site/pages/carousel/index.wd +109 -0
  17. package/site/pages/carousel/slide-1.jpg +0 -0
  18. package/site/pages/carousel/slide-2.jpg +0 -0
  19. package/site/pages/carousel/slide-3.jpg +0 -0
  20. package/site/pages/carousel/slide-4.jpg +0 -0
  21. package/site/pages/carousel/slide-5.jpg +0 -0
  22. package/site/pages/compass/index.skin +247 -0
  23. package/site/pages/compass/index.wd +194 -0
  24. package/site/pages/docs/index.skin +48 -28
  25. package/site/pages/docs/index.wd +72 -5
  26. package/site/pages/folio/-style.skin +375 -0
  27. package/site/pages/folio/-style.wd +6 -0
  28. package/site/pages/folio/cart.skin +309 -0
  29. package/site/pages/folio/cart.wd +201 -0
  30. package/site/pages/folio/index.skin +24 -0
  31. package/site/pages/folio/index.wd +167 -0
  32. package/site/pages/forge/index.skin +676 -0
  33. package/site/pages/forge/index.wd +330 -0
  34. package/site/pages/index.skin +135 -144
  35. package/site/pages/index.wd +17 -5
  36. package/site/pages/ledger/index.skin +257 -0
  37. package/site/pages/ledger/index.wd +183 -0
  38. package/site/pages/media/clip-poster.jpg +0 -0
  39. package/site/pages/media/clip.mp4 +0 -0
  40. package/site/pages/media/index.skin +82 -0
  41. package/site/pages/media/index.wd +84 -0
  42. package/site/pages/media/tone.mp3 +0 -0
  43. package/site/pages/pulse/index.skin +434 -0
  44. package/site/pages/pulse/index.wd +178 -0
  45. package/site/pages/showcase/index.skin +130 -0
  46. package/site/pages/showcase/index.wd +168 -0
  47. package/src/compiler/body.js +89 -3
  48. package/src/compiler/context.js +1 -1
  49. package/src/compiler/directives.js +199 -3
  50. package/src/compiler/format.js +244 -0
  51. package/src/compiler/interpolation.js +36 -1
  52. package/src/compiler/loops.js +95 -24
  53. package/src/compiler/markdown.js +16 -7
  54. package/src/compiler/predicates.js +35 -2
  55. package/src/headers.js +5 -0
  56. package/src/runtime.js +168 -14
  57. package/src/skin.js +9 -1
@@ -0,0 +1,185 @@
1
+ /* ── Swiper demo — layers on /base.wd. The viewport clips; the track is a flex
2
+ row the colocated index.js slides with a transform. Reuses base tokens. ──── */
3
+
4
+ .app-swiper
5
+ max-width 56rem
6
+ margin 0 auto
7
+ padding 2.5rem 1.25rem 4rem
8
+
9
+ .app-swiper .swiper-head
10
+ margin-bottom 1.75rem
11
+
12
+ .app-swiper .swiper-eyebrow
13
+ font-size .72rem
14
+ font-weight 700
15
+ letter-spacing .14em
16
+ text-transform uppercase
17
+ color $accent
18
+
19
+ .app-swiper h1
20
+ margin .4rem 0 0
21
+ font-size 2.4rem
22
+ letter-spacing -.02em
23
+ color $ink
24
+
25
+ .app-swiper .swiper-tagline
26
+ margin .7rem 0 0
27
+ max-width 42rem
28
+ color $ink-soft
29
+ font-size 1rem
30
+ line-height 1.6
31
+
32
+ .app-swiper .swiper-head code, .app-swiper .swiper-tagline code, .app-swiper .swiper-readout code
33
+ bg $code-bg
34
+ color $code-ink
35
+ padding .06rem .32rem
36
+ radius 6px
37
+ font-size .85em
38
+
39
+ /* Stage */
40
+ .app-swiper .swiper-stage
41
+ margin 0
42
+
43
+ .app-swiper .swiper-viewport
44
+ overflow hidden
45
+ border 1px solid $line-soft
46
+ radius 18px
47
+ background $sunk
48
+ cursor grab
49
+ touch-action pan-y
50
+ outline none
51
+
52
+ .app-swiper .swiper-viewport:focus-visible
53
+ box-shadow 0 0 0 3px $ring
54
+
55
+ .app-swiper .swiper-viewport:active
56
+ cursor grabbing
57
+
58
+ .app-swiper .swiper-track
59
+ display flex
60
+ /* transition is set by index.js so the very first paint doesn't animate */
61
+ will-change transform
62
+
63
+ .app-swiper .swipe-slide
64
+ position relative
65
+ flex 0 0 100%
66
+ margin 0
67
+
68
+ .app-swiper .swipe-slide img
69
+ display block
70
+ width 100%
71
+ height auto
72
+ aspect-ratio 16/9
73
+ object-fit cover
74
+ user-select none
75
+ -webkit-user-drag none
76
+ pointer-events none
77
+
78
+ .app-swiper .swipe-slide figcaption
79
+ position absolute
80
+ left 0
81
+ right 0
82
+ bottom 0
83
+ display flex
84
+ flex-direction column
85
+ gap .15rem
86
+ padding 1.5rem 1.25rem 1rem
87
+ color #fff
88
+ background linear-gradient(to top, rgba(10,8,6,.72), rgba(10,8,6,0))
89
+
90
+ .app-swiper .swipe-slide figcaption strong
91
+ color #fff
92
+ font-size 1.15rem
93
+ letter-spacing -.01em
94
+
95
+ .app-swiper .swipe-slide figcaption span
96
+ font-size .82rem
97
+ opacity .85
98
+ font-variant-numeric tabular-nums
99
+
100
+ /* Controls row: arrows flank the dots */
101
+ .app-swiper .swiper-controls
102
+ display flex
103
+ align-items center
104
+ justify-content center
105
+ gap 1rem
106
+ margin 1.1rem 0 .25rem
107
+
108
+ .app-swiper .swiper-arrow
109
+ flex 0 0 auto
110
+ width 2.6rem
111
+ height 2.6rem
112
+ display inline-flex
113
+ align-items center
114
+ justify-content center
115
+ border 1px solid $line
116
+ background $panel
117
+ color $ink-soft
118
+ font-size 1.4rem
119
+ line-height 1
120
+ radius 999px
121
+ cursor pointer
122
+ transition color .14s ease, border-color .14s ease, transform .14s ease
123
+
124
+ .app-swiper .swiper-arrow:hover
125
+ color $ink
126
+ border-color $accent
127
+
128
+ .app-swiper .swiper-arrow:active
129
+ transform scale(.94)
130
+
131
+ /* Dots — each is a declarative container whose .active is reconciled by the
132
+ framework from `.active when slide == N`. */
133
+ .app-swiper .swiper-dots
134
+ display inline-flex
135
+ align-items center
136
+ gap .4rem
137
+
138
+ .app-swiper .dot button[data-wd-action]
139
+ width 1.6rem
140
+ height 1.6rem
141
+ display inline-flex
142
+ align-items center
143
+ justify-content center
144
+ border 0
145
+ background transparent
146
+ /* collapse the visible digit — it stays the button's accessible name */
147
+ font-size 0
148
+ border-radius 999px
149
+ cursor pointer
150
+ position relative
151
+
152
+ .app-swiper .dot button[data-wd-action]::after
153
+ content ""
154
+ position absolute
155
+ width .5rem
156
+ height .5rem
157
+ border-radius 999px
158
+ background $line
159
+ transition background .18s ease, transform .18s ease
160
+
161
+ .app-swiper .dot.active button[data-wd-action]::after
162
+ background $accent
163
+ transform scale(1.5)
164
+
165
+ .app-swiper .dot button[data-wd-action]:hover::after
166
+ background $accent-deep
167
+
168
+ .app-swiper .swiper-readout
169
+ margin 1rem 0 0
170
+ text-align center
171
+ font-size .9rem
172
+ color $muted
173
+
174
+ .app-swiper .swiper-readout strong
175
+ color $ink
176
+
177
+ .app-swiper .swiper-readout kbd
178
+ font-size .8em
179
+
180
+ .app-swiper .source-reveal
181
+ margin-top 2rem
182
+
183
+ @media (prefers-reduced-motion: reduce)
184
+ .app-swiper .swiper-track
185
+ transition none !important
@@ -0,0 +1,109 @@
1
+ ---
2
+ title: Swiper — a draggable carousel wired to wd.subscribe
3
+ description: An image carousel with arrow-key nav, pointer drag / swipe, and a sliding transition — the things a deliberately tiny framework leaves to you. Darkmown owns the reactive slide state, the dots, and the readout declaratively; a colocated index.js adds the gestures through window.wd.subscribe and window.wd.set.
4
+ image: https://darkmown.com/__wd/media/og-showcase.png
5
+ transitions: true
6
+ ---
7
+
8
+ @include /nav.wd
9
+ @include /base.wd
10
+
11
+ <main class="wide app-swiper">
12
+
13
+ <!-- No ::: section wrapper: `slide` stays a top-level state key, so the
14
+ declarative dots/readout and the colocated index.js share one `slide`. -->
15
+ :state slide = 0
16
+ :computed human = slide + 1
17
+
18
+ ::: swiper-head
19
+ ::: eyebrow .swiper-eyebrow
20
+ The escape hatch
21
+ :::
22
+
23
+ # A *swiper*, wired to `wd.subscribe`
24
+
25
+ ::: swiper-tagline
26
+ Darkmown owns the reactive `slide` state, the indicator dots, and the live readout —
27
+ declaratively, with no hand-written JavaScript. The things a framework this small
28
+ deliberately *doesn't* ship — arrow-key navigation, pointer **drag / swipe**, and the
29
+ sliding transition — live in a colocated `index.js` that talks to the very same state
30
+ through `window.wd.subscribe` and `window.wd.set`.
31
+ :::
32
+ :::
33
+
34
+ ::: swiper-stage
35
+
36
+ <div class="swiper-viewport" data-swiper tabindex="0" role="group" aria-roledescription="carousel" aria-label="Project gallery — drag, or use the arrow keys">
37
+ <div class="swiper-track" data-swiper-track>
38
+ <figure class="swipe-slide"><img src="/carousel/slide-1.jpg" alt="Warm ember-to-charcoal gradient" width="1200" height="675"><figcaption><strong>Ember</strong><span>Warm dusk · #b8462e</span></figcaption></figure>
39
+ <figure class="swipe-slide"><img src="/carousel/slide-2.jpg" alt="Teal-to-ink gradient" width="1200" height="675"><figcaption><strong>Pulse</strong><span>Signal teal · #18b89a</span></figcaption></figure>
40
+ <figure class="swipe-slide"><img src="/carousel/slide-3.jpg" alt="Violet-to-midnight gradient" width="1200" height="675"><figcaption><strong>Forge</strong><span>Electric violet · #6d5ae6</span></figcaption></figure>
41
+ <figure class="swipe-slide"><img src="/carousel/slide-4.jpg" alt="Amber-to-umber gradient" width="1200" height="675"><figcaption><strong>Compass</strong><span>Trail amber · #e0894a</span></figcaption></figure>
42
+ <figure class="swipe-slide"><img src="/carousel/slide-5.jpg" alt="Gold-to-olive gradient" width="1200" height="675"><figcaption><strong>Ledger</strong><span>Ledger gold · #c79a3a</span></figcaption></figure>
43
+ </div>
44
+ </div>
45
+
46
+ ::: swiper-controls
47
+ <button class="swiper-arrow" data-swiper-prev type="button" aria-label="Previous slide">‹</button>
48
+
49
+ ::: swiper-dots
50
+ ::: dot .active when slide == 0
51
+ :button "1" -> slide = 0
52
+ :::
53
+ ::: dot .active when slide == 1
54
+ :button "2" -> slide = 1
55
+ :::
56
+ ::: dot .active when slide == 2
57
+ :button "3" -> slide = 2
58
+ :::
59
+ ::: dot .active when slide == 3
60
+ :button "4" -> slide = 3
61
+ :::
62
+ ::: dot .active when slide == 4
63
+ :button "5" -> slide = 4
64
+ :::
65
+ :::
66
+
67
+ <button class="swiper-arrow" data-swiper-next type="button" aria-label="Next slide">›</button>
68
+ :::
69
+
70
+ ::: swiper-readout
71
+ Slide **{ human }** of 5 — drag it, press <kbd>←</kbd> / <kbd>→</kbd>, or tap a dot.
72
+ :::
73
+
74
+ :::
75
+
76
+ <details class="source-reveal">
77
+ <summary>View source — declarative core + a colocated behavior</summary>
78
+
79
+ ```wd
80
+ :state slide = 0
81
+ :computed human = slide + 1
82
+
83
+ # the dots are declarative — Darkmown reconciles `.active` for you
84
+ ::: dot .active when slide == 0
85
+ :button "1" -> slide = 0
86
+ :::
87
+
88
+ # … the readout, too
89
+ Slide { human } of 5
90
+ ```
91
+
92
+ ```js
93
+ // index.js, colocated next to the page. Loads after the runtime, so window.wd
94
+ // is ready. Drag / keyboard / transition — the gestural layer the framework
95
+ // deliberately leaves to you — all driven through the same `slide` state.
96
+ window.wd.subscribe("slide", (i) => {
97
+ track.style.transform = `translateX(${-i * 100}%)`;
98
+ });
99
+ prevButton.onclick = () => window.wd.set("slide", current - 1);
100
+ ```
101
+
102
+ The framework reconciles state, classes, and text; the escape hatch owns the
103
+ gestures. They meet at one shared `slide` value.
104
+
105
+ </details>
106
+
107
+ </main>
108
+
109
+ @include /footer.wd
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,247 @@
1
+ /* ===================================================================
2
+ Compass — a guided product-finder quiz.
3
+ Identity: playful & warm. A big friendly rounded card centered on a
4
+ soft gradient backdrop, large question type, satisfying pill answers,
5
+ a celebratory result. Distinct accent ($brand, warm amber) scoped to
6
+ .app-compass so it never leaks into the shared design system. Inherits
7
+ tokens + components from base.skin; dark overrides verified below.
8
+ =================================================================== */
9
+
10
+ .app-compass
11
+ --brand #e0894a
12
+ --brand-deep #c46f33
13
+ --brand-soft #fbeede
14
+ --glow-1 #fcefe0
15
+ --glow-2 #f6e7f1
16
+ --glow-3 #f9f4ea
17
+
18
+ @media (prefers-color-scheme: dark)
19
+ .app-compass
20
+ --brand #f0a868
21
+ --brand-deep #f6c08c
22
+ --brand-soft #2e2014
23
+ --glow-1 #1b1610
24
+ --glow-2 #181219
25
+ --glow-3 #14110d
26
+
27
+ /* ── The stage: a soft gradient backdrop, card floated dead center ──── */
28
+
29
+ .app-compass
30
+ max-width 100%
31
+ padding 0
32
+
33
+ .compass-stage
34
+ min-height calc(100vh - 60px)
35
+ display flex
36
+ align-items center
37
+ justify-content center
38
+ padding 3rem 1.25rem 4rem
39
+ background radial-gradient(120% 90% at 15% 0%, var(--glow-1) 0%, transparent 55%), radial-gradient(110% 90% at 95% 10%, var(--glow-2) 0%, transparent 50%), radial-gradient(140% 120% at 50% 110%, var(--glow-3) 0%, transparent 60%)
40
+
41
+ /* ── The card ───────────────────────────────────────────────────────── */
42
+
43
+ .compass-card
44
+ width 100%
45
+ max-width 560px
46
+ background $panel
47
+ border 1px solid $line
48
+ border-radius 28px
49
+ padding 2.75rem 2.5rem 2.5rem
50
+ box-shadow 0 1px 0 rgba(255,255,255,.6) inset, 0 24px 60px -28px rgba(28,24,20,.45), 0 8px 22px -16px rgba(28,24,20,.3)
51
+ text-align center
52
+
53
+ @media (prefers-color-scheme: dark)
54
+ .compass-card
55
+ box-shadow 0 1px 0 rgba(255,255,255,.04) inset, 0 28px 70px -30px rgba(0,0,0,.7)
56
+
57
+ @media (max-width: 540px)
58
+ .compass-card
59
+ padding 2rem 1.4rem 1.75rem
60
+ border-radius 22px
61
+
62
+ /* ── Top: eyebrow + progress rail ───────────────────────────────────── */
63
+
64
+ .compass-top
65
+ margin-bottom 2rem
66
+
67
+ .compass-eyebrow
68
+ display inline-block
69
+ text-transform uppercase
70
+ letter-spacing .16em
71
+ font-size .72rem
72
+ font-weight 700
73
+ color var(--brand-deep)
74
+ margin 0 0 1rem
75
+
76
+ .compass-eyebrow.is-result
77
+ color var(--brand-deep)
78
+
79
+ .compass-rail
80
+ position relative
81
+ height 8px
82
+ border-radius 999px
83
+ background $sunk
84
+ overflow hidden
85
+
86
+ .compass-rail::after
87
+ content ""
88
+ position absolute
89
+ inset 0
90
+ width 25%
91
+ border-radius 999px
92
+ background linear-gradient(90deg, var(--brand) 0%, var(--brand-deep) 100%)
93
+ transition width .45s cubic-bezier(.34, 1.56, .64, 1)
94
+
95
+ .compass-rail.at-1::after
96
+ width 25%
97
+
98
+ .compass-rail.at-2::after
99
+ width 50%
100
+
101
+ .compass-rail.at-3::after
102
+ width 75%
103
+
104
+ .compass-rail.at-4::after
105
+ width 100%
106
+
107
+ /* ── The question ───────────────────────────────────────────────────── */
108
+
109
+ .compass-card h2
110
+ font-size clamp(1.7rem, 5vw, 2.3rem)
111
+ line-height 1.12
112
+ letter-spacing -.02em
113
+ margin .2rem 0 0
114
+
115
+ .compass-lead
116
+ color $muted
117
+ font-size 1.02rem
118
+ margin .85rem auto 0
119
+ max-width 38ch
120
+
121
+ /* ── Answers: big satisfying pills ──────────────────────────────────── */
122
+
123
+ .compass-answers
124
+ display flex
125
+ flex-direction column
126
+ gap .7rem
127
+ margin 1.9rem 0 0
128
+
129
+ .compass-answers button
130
+ display block
131
+ width 100%
132
+ margin 0
133
+ padding 1.05rem 1.3rem
134
+ font-family system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
135
+ font-size 1.05rem
136
+ font-weight 550
137
+ line-height 1.3
138
+ text-align left
139
+ color $ink
140
+ background $paper-2
141
+ border 1.5px solid $line
142
+ border-radius 16px
143
+ cursor pointer
144
+ transition transform .14s cubic-bezier(.34,1.56,.64,1), box-shadow .14s ease, border-color .14s ease, background .14s ease
145
+
146
+ .compass-answers button:hover
147
+ transform translateY(-2px)
148
+ border-color var(--brand)
149
+ background var(--brand-soft)
150
+ box-shadow 0 12px 26px -16px rgba(196,111,51,.55)
151
+
152
+ .compass-answers button:active
153
+ transform translateY(0) scale(.985)
154
+
155
+ .compass-answers button:focus-visible
156
+ outline 3px solid var(--brand)
157
+ outline-offset 2px
158
+
159
+ /* ── Result: a result that feels earned ─────────────────────────────── */
160
+
161
+ .compass-result
162
+ padding .4rem 0 .2rem
163
+
164
+ .compass-burst
165
+ font-size 3.4rem
166
+ line-height 1
167
+ margin 0 0 .4rem
168
+ animation pop .55s cubic-bezier(.34,1.56,.64,1) both
169
+
170
+ .compass-result h1
171
+ font-size clamp(2rem, 7vw, 2.9rem)
172
+ line-height 1.05
173
+ letter-spacing -.025em
174
+ margin .15rem 0 0
175
+ background linear-gradient(100deg, var(--brand-deep), var(--brand))
176
+ -webkit-background-clip text
177
+ background-clip text
178
+ color transparent
179
+
180
+ .compass-why
181
+ color $ink-soft
182
+ font-size 1.06rem
183
+ line-height 1.6
184
+ margin 1rem auto 0
185
+ max-width 40ch
186
+
187
+ .compass-why strong
188
+ color var(--brand-deep)
189
+
190
+ /* ── CTA: the styled shop link ──────────────────────────────────────── */
191
+
192
+ .compass-cta
193
+ display inline-flex
194
+ align-items center
195
+ gap .45rem
196
+ margin 1.7rem 0 0
197
+ padding .95rem 1.7rem
198
+ font-family system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
199
+ font-size 1.02rem
200
+ font-weight 650
201
+ color #fff
202
+ text-decoration none
203
+ background linear-gradient(100deg, var(--brand) 0%, var(--brand-deep) 100%)
204
+ border-radius 999px
205
+ box-shadow 0 14px 30px -12px rgba(196,111,51,.6)
206
+ transition transform .14s ease, box-shadow .14s ease
207
+
208
+ .compass-cta:hover
209
+ color #fff
210
+ transform translateY(-2px)
211
+ box-shadow 0 20px 38px -14px rgba(196,111,51,.7)
212
+
213
+ .compass-cta:focus-visible
214
+ outline 3px solid var(--brand-deep)
215
+ outline-offset 3px
216
+
217
+ /* ── Start over ─────────────────────────────────────────────────────── */
218
+
219
+ .compass-restart
220
+ margin 1.7rem 0 0
221
+
222
+ .compass-restart button
223
+ margin 0
224
+ padding .55rem 1.1rem
225
+ font-family system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
226
+ font-size .9rem
227
+ font-weight 550
228
+ color $muted
229
+ background transparent
230
+ border 1.5px solid $line
231
+ border-radius 999px
232
+ cursor pointer
233
+ transition color .14s ease, border-color .14s ease
234
+
235
+ .compass-restart button:hover
236
+ color var(--brand-deep)
237
+ border-color var(--brand)
238
+
239
+ .compass-restart button:focus-visible
240
+ outline 3px solid var(--brand)
241
+ outline-offset 2px
242
+
243
+ /* ── Source reveal sits below the card, full-width-ish, calm ────────── */
244
+
245
+ .app-compass .source-reveal
246
+ max-width 560px
247
+ margin 1.75rem auto 0