@zvndev/darkmown 0.4.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/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to Darkmown are documented here. Versions follow [semver](https://semver.org); pre-1.0 minor versions may contain breaking changes.
4
+
5
+ ## 0.4.0 — 2026-06-11
6
+
7
+ - **Renamed**: the framework is now **Darkmown** (formerly Markie). Package `@zvndev/darkmown` (npm's typosquat guard reserves bare `darkmown` as too similar to `markdown`), CLI `darkmown`. The `.wd` format and `/__wd/` asset paths are unchanged.
8
+ - **License**: MIT. Repository is public.
9
+ - Gold-standard launch hardening: CI (Node 20/22/24 matrix, build check, runtime size budget, dependency audit), release automation on tag push, SECURITY.md, CONTRIBUTING.md, CLAUDE.md, this changelog.
10
+
11
+ ## 0.3.0 — 2026-06-11
12
+
13
+ - `:form action="/url" into reply` — server round-trips against any backend: urlencoded fetch POST with the JSON reply landing in state; degrades to a plain native POST without JS.
14
+ - Dev error overlay: failed rebuilds render the compiler error in the browser and clear on the next good build.
15
+ - `darkmown serve` — preview the built `dist` locally.
16
+ - Dev-only `/__wd/echo` endpoint for round-trip demos.
17
+
18
+ ## 0.2.0 — 2026-06-11
19
+
20
+ - `:fetch name from "url"` (+ `when=visible` lazy loading via IntersectionObserver).
21
+ - `:computed name = expr` — derived state with a compile-time-validated expression grammar.
22
+ - `:form into name`, `:input`, `:submit` — forms that write state with zero backend.
23
+ - `:state x = v persist` — localStorage-backed state.
24
+ - `:if item.path` inside reactive loops (per-row branches).
25
+ - `window.wd` escape hatch for colocated `.js`.
26
+ - `::: section` containers with scoped state; keyed loop reconciliation.
27
+ - View transitions via `transitions: true` frontmatter.
28
+ - Real CommonMark parsing (markdown-it); strict `.md` (directives stay plain text).
29
+ - `@loop … into … @endloop` — the one loop, replacing `@repeat` and `:for`.
30
+ - Dev server rebuilds in a child process (always-fresh modules).
31
+
32
+ ## 0.1.0 — 2026-06-11
33
+
34
+ - Initial prototype: folder router, dot/minus hidden pages, `site/_` include shelf, colocated `.skin`/`.js`, `@include`/`@repeat`, `:state`/`:button`/`:if`/`:for`, sub-1 KB runtime, live dev server.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZVN
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # Darkmown
2
+
3
+ **[darkmown.com](https://darkmown.com)** · markdown, rearranged.
4
+
5
+ Darkmown is a Markdown-native web framework. Two formats, one rule: `.md` stays plain CommonMark forever, and renaming a file to `.wd` ("whateverdown") is what unlocks directives — includes, loops, state, conditionals, and sections. Static pages ship **zero** framework JavaScript; reactive pages share one runtime under 2 KB gzipped.
6
+
7
+ ## Quick start
8
+
9
+ ```sh
10
+ npx @zvndev/darkmown init my-site
11
+ cd my-site
12
+ npm install
13
+ npm run dev
14
+ ```
15
+
16
+ Or add it to an existing project:
17
+
18
+ ```sh
19
+ npm install -D @zvndev/darkmown
20
+ npx darkmown dev
21
+ ```
22
+
23
+ The package is `@zvndev/darkmown`; the command it installs is plain `darkmown`.
24
+
25
+ ## Working from this repo
26
+
27
+ ```sh
28
+ npm install
29
+ npm test
30
+ npm run dev # live demo site — the same site that runs darkmown.com
31
+ ```
32
+
33
+ ## Commands
34
+
35
+ - `darkmown init [dir]` scaffolds a new site.
36
+ - `darkmown dev` starts the live compiler with browser reload and an in-browser error overlay when a build fails.
37
+ - `darkmown build` writes static output to `dist`.
38
+ - `darkmown serve` previews the built `dist` locally.
39
+ - `darkmown help` prints CLI usage.
40
+
41
+ ## Authoring model
42
+
43
+ - `site/pages` is the route tree. `.md` and `.wd` files become pages.
44
+ - `.md` is strict CommonMark (real parser: ordered lists, tables, blockquotes, images, the lot). Directives stay plain text, and the build hints when it spots `.wd` syntax in a `.md` file.
45
+ - Files or folders starting with `.`, `-`, or `_` are hidden from routing.
46
+ - `site/_` is the include shelf for `@include /name.wd`.
47
+ - Matching `page.skin` and `page.js` colocate styling and behavior by basename.
48
+ - Static pages ship zero Darkmown runtime. Reactive pages share `/__wd/runtime.js` (currently ~1.6 KB gzipped).
49
+ - Shelf `.json` files are published at `/__wd/data/` so `:fetch` works on any static host.
50
+
51
+ ## Interpolation
52
+
53
+ One syntax everywhere: `{ name }` or `{ name.path }`.
54
+
55
+ - In-scope static values (include arguments, loop values) resolve at build time.
56
+ - Declared `:state` becomes a live binding.
57
+ - Anything else stays literal text — braces in prose never break a page or pull in the runtime.
58
+
59
+ ## Loops
60
+
61
+ `@loop <things> into <thing>` is the only loop. The source decides the behavior:
62
+
63
+ ```wd
64
+ @loop /features.json into card <- JSON file: unrolled at build time
65
+ @include /feature-card.wd <- includes inherit the loop value
66
+ @endloop
67
+
68
+ :state todos = [{"id": 1, "title": "Route pages"}]
69
+
70
+ @loop todos into todo <- :state list: reactive, patched by key
71
+ - { todo.title }
72
+ @endloop
73
+ ```
74
+
75
+ Loops nest, dotted paths reach into rows, and `@include ... with x={ row.field }` reassigns values Liquid-style.
76
+
77
+ ## Sections
78
+
79
+ ```wd
80
+ ::: section #cart .dark
81
+ :state count = 0
82
+
83
+ Cart has { count } items.
84
+
85
+ :button "Add" -> count++
86
+ :::
87
+ ```
88
+
89
+ State declared inside a section is scoped to it — two sections can both own a `count`. Bindings and actions resolve to the nearest scope.
90
+
91
+ ## Reactive directives
92
+
93
+ ```wd
94
+ :state count = 0
95
+
96
+ Count: { count }
97
+
98
+ :button "Increment" -> count++
99
+
100
+ :if count
101
+ Count has changed.
102
+ :else
103
+ Count is still zero.
104
+ :endif
105
+ ```
106
+
107
+ Directive actions are intentionally narrow and compile-time checked. Arbitrary JavaScript belongs in colocated `.js` files.
108
+
109
+ ## Data, forms, and persistence
110
+
111
+ ```wd
112
+ :fetch team from "/__wd/data/team.json"
113
+
114
+ :if team
115
+ @loop team into member
116
+ - { member.name }
117
+ @endloop
118
+ :else
119
+ Loading…
120
+ :endif
121
+
122
+ :form into profile
123
+ :input name placeholder="Your name" required
124
+ :submit "Save"
125
+ :endform
126
+
127
+ :state cart = [] persist
128
+ ```
129
+
130
+ - `:fetch name from "url"` declares state and fills it from JSON over the network; `name_error` carries failures. Add `when=visible` to defer the request until the spot scrolls into view.
131
+ - `:computed total = items.length * 4` derives state from state with a compile-time-checked expression (names, numbers, arithmetic, comparisons — nothing else).
132
+ - `:form into name` captures submits straight into state (no backend). `:form action="/url"` emits a plain native form instead — zero JS, full progressive enhancement.
133
+ - `:form action="/url" into reply` does both: with JS the submit posts urlencoded via fetch and the JSON reply lands in state `reply` (`reply_error` on failure); without JS it is the same native POST. Darkmown adapts to any backend — it does not own one. `darkmown dev` ships a `/__wd/echo` endpoint for demos.
134
+ - `:state x = [] persist` keeps that state in localStorage across reloads.
135
+ - `:if item.path` works inside reactive loops for per-row branches.
136
+
137
+ ## The escape hatch
138
+
139
+ Reactive pages expose `window.wd` — `wd.get(key)`, `wd.set(key, value)`, `wd.state`, `wd.render()` — so colocated `.js` can do anything the directives can't. Section-scoped keys are addressed as `sectionId:name`.
140
+
141
+ ## Spec status
142
+
143
+ See [docs/spec-alignment.md](docs/spec-alignment.md) for the deep alignment audit against the original vision.
package/docs/cli.md ADDED
@@ -0,0 +1,51 @@
1
+ # Darkmown CLI
2
+
3
+ ## Install locally
4
+
5
+ ```sh
6
+ npm install
7
+ npm link
8
+ darkmown help
9
+ ```
10
+
11
+ `package.json` exposes the executable as `darkmown`.
12
+
13
+ ## Commands
14
+
15
+ ### `darkmown init [dir]`
16
+
17
+ Creates a minimal Darkmown site with:
18
+
19
+ - `site/pages/index.wd`
20
+ - `site/pages/index.skin`
21
+ - `site/_/nav.wd`
22
+ - `README.md`
23
+
24
+ Existing files are not overwritten.
25
+
26
+ After scaffolding:
27
+
28
+ ```sh
29
+ cd my-site
30
+ npm install
31
+ npm run dev
32
+ ```
33
+
34
+ ### `darkmown dev`
35
+
36
+ Runs a live compiler:
37
+
38
+ - builds once at startup
39
+ - watches `site/` and `src/`
40
+ - debounces rebuilds
41
+ - serves cached `dist`
42
+ - injects `/__wd/dev-client.js`
43
+ - reloads the browser through `/__wd/dev-events`
44
+
45
+ ### `darkmown build`
46
+
47
+ Compiles `site/pages` into `dist`.
48
+
49
+ Static pages do not receive `/__wd/runtime.js`; reactive pages do.
50
+
51
+ When a `.md` file contains `.wd` syntax (directives, includes, loops), the build prints a hint suggesting a rename to `.wd` — the syntax stays plain text in `.md` by design.
@@ -0,0 +1,60 @@
1
+ # Spec alignment audit
2
+
3
+ This audit compares the current Darkmown implementation with the original MARROW/WhateverDown vision and the follow-up decisions made on 2026-06-11.
4
+
5
+ ## Fundamentally aligned
6
+
7
+ - **Markdown-first authoring:** `.md` is strict CommonMark rendered by a real parser (markdown-it); `.wd` is the same Markdown plus first-party directives. Renaming the file is the upgrade path — extensions genuinely gate functionality.
8
+ - **Content tree over components:** routing, includes, and repeated fragments are file/document based instead of React-style components.
9
+ - **Tiered output:** static routes emit HTML/CSS and no Darkmown runtime; reactive routes opt into `/__wd/runtime.js` (~1.1 KB gzipped, below the original 5 KB target).
10
+ - **No virtual DOM:** state changes patch direct DOM bindings, conditional regions, and keyed loop regions.
11
+ - **Keyed list patching:** reactive loops reconcile by item key (`id`/`key`/value) instead of re-rendering the region.
12
+ - **Sections with scoped state:** `::: section #id .class` containers scope `:state`; bindings and actions resolve through the nearest scope chain.
13
+ - **`.skin` language:** indentation-based token styling compiles to CSS.
14
+ - **Folder router:** `site/pages` maps to routes, and `.`, `-`, `_` prefixes hide pages.
15
+ - **Colocation:** matching `.skin` and `.js` files attach to the page by basename, for includes too.
16
+ - **Live DX:** dev mode live-compiles and reloads through a small SSE client.
17
+
18
+ ## Stage 3 additions (2026-06-11, same day)
19
+
20
+ - **`:fetch name from "url"`:** declarative data loading into reactive state, with `name_error` for failures. Shelf `.json` publishes to `/__wd/data/` so it works on static hosts.
21
+ - **`:form` two ways:** `:form into name` captures submits into client state with zero backend; `:form action="/url"` emits a plain native form and ships no JS — the progressive-enhancement story from the original doc.
22
+ - **`:state x = v persist`:** localStorage-backed state. This is the honest client-side slice of the original `:cart` primitive; server sync remains future work.
23
+ - **`:if item.path` inside reactive loops:** per-row conditional branches, filled at compile time and re-filled by the keyed patcher.
24
+ - **`window.wd` escape hatch:** `get`/`set`/`state`/`render` exposed to colocated `.js` — the adapted form of the original `<script scope>` "infinite extension" idea, aligned with Darkmown's colocation model instead of inline scripts.
25
+
26
+ ## Deliberate adaptations
27
+
28
+ - **One loop, `@loop … into … @endloop`:** replaces both the original doc's `:for/:endfor` and the earlier `@repeat`. The source decides behavior: JSON file or in-scope value unrolls at build time; a `:state` list compiles to a keyed reactive region. Includes inside a loop inherit the loop value (Liquid-style), and `with x={ row.field }` reassigns.
29
+ - **One interpolation syntax:** `{ name }` / `{ name.path }` everywhere. Static in-scope values resolve at build time, declared state binds live, and unknown names stay literal — braces in prose never pull in the runtime.
30
+ - **`.mdx` removed:** it was a label without semantics. The formats are `.md` (plain) and `.wd` (full surface).
31
+ - **Action safety:** directive actions use a narrow compile-time-checked grammar instead of arbitrary JavaScript. Actions must target declared state.
32
+ - **Name and extension:** MARROW/`.mw` became Darkmown/`.wd` per the follow-up direction.
33
+
34
+ ## Stage 4 additions (2026-06-11, same day)
35
+
36
+ - **View transitions:** `transitions: true` in frontmatter emits `@view-transition { navigation: auto; }` — the original doc's zero-JS MPA navigation polish.
37
+ - **Lazy loading:** `:fetch … when=visible` defers the request until the marker scrolls into view (IntersectionObserver) — the adapted form of `:lazy`.
38
+ - **Computed state:** `:computed name = expr` with a compile-time-validated grammar (state refs, numbers, strings, arithmetic, comparisons; assignment, calls, and prototype walks rejected). Initial values are evaluated at build time.
39
+ - **Dev self-reload:** `darkmown dev` rebuilds in a child process, so changes to the framework's own `src/` always compile with fresh modules.
40
+ - **Packaging:** version 0.2.0 with repository metadata; `darkmown version` reads package.json.
41
+
42
+ ## Stage 5 additions (2026-06-11, same day)
43
+
44
+ - **Adapter-style Tier 2 (decided):** Darkmown does not own a server. `:form action="/url" into reply` posts urlencoded via fetch and lands the JSON reply in state, degrading to a plain native POST without JS. Sessions ride on `:fetch` plus ordinary cookies against any backend. A first-party `site/api/` runtime stays open as a future option if a real project demands it.
45
+ - **Dev error overlay:** failed rebuilds render the compiler error in the browser; the overlay clears on the next good build.
46
+ - **`darkmown serve`:** local preview of the built `dist`, no dev client injected.
47
+ - **Packaging:** v0.3.0, license UNLICENSED pending a licensing decision, scaffold pins the live version, npm pack contents covered by tests.
48
+
49
+ ## Still missing from the full vision
50
+
51
+ - First-party server runtime (`site/api/`), HTML-fragment `swap` semantics, and cart server sync — parked behind the adapter decision until a real project needs them.
52
+ - Nested `:if` over loop items, and `@loop` over fetch sub-paths.
53
+ - `@starting-style` emission and browser fallbacks for older engines.
54
+ - Editor tooling, richer diagnostics, and an actual npm publish (needs a license decision).
55
+
56
+ ## Current claim
57
+
58
+ Darkmown is faithful to the core thesis: Markdown remains the authoring center, static pages ship zero framework JavaScript, and reactive behavior compiles into tiny direct-DOM islands only where declared. The extension is the feature gate, the loop is one concept with progressive disclosure, and state scopes to document structure.
59
+
60
+ It is not yet the full production framework described in the original document.
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@zvndev/darkmown",
3
+ "version": "0.4.0",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "type": "module",
9
+ "description": "Darkmown — the markdown framework. Plain .md stays plain; rename to .wd for loops, state, includes, and tiny reactive islands. Zero JS on static pages.",
10
+ "bin": {
11
+ "darkmown": "./src/cli.js"
12
+ },
13
+ "scripts": {
14
+ "build": "node src/cli.js build",
15
+ "dev": "node src/cli.js dev",
16
+ "test": "node --test"
17
+ },
18
+ "files": [
19
+ "src",
20
+ "site",
21
+ "docs",
22
+ "README.md",
23
+ "LICENSE",
24
+ "CHANGELOG.md"
25
+ ],
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
29
+ "dependencies": {
30
+ "markdown-it": "^14.2.0"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/ZVN-DEV/darkmown.git"
35
+ },
36
+ "homepage": "https://github.com/ZVN-DEV/darkmown#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/ZVN-DEV/darkmown/issues"
39
+ },
40
+ "keywords": [
41
+ "markdown",
42
+ "framework",
43
+ "static-site",
44
+ "wd",
45
+ "whateverdown",
46
+ "zero-js",
47
+ "reactive"
48
+ ],
49
+ "license": "MIT"
50
+ }
@@ -0,0 +1,7 @@
1
+ <section class="feature-card">
2
+
3
+ **{ card.title }**
4
+
5
+ { card.body }
6
+
7
+ </section>
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "title": "Folder routing without config",
4
+ "body": "Pages come from site/pages, and hidden work is just dot or minus prefixed."
5
+ },
6
+ {
7
+ "title": "One loop, @loop",
8
+ "body": "Loop a JSON file, an in-scope value, or a :state list — includes inside inherit the loop value."
9
+ },
10
+ {
11
+ "title": "Colocated behavior",
12
+ "body": "A matching .skin or .js file beside the page is picked up automatically."
13
+ }
14
+ ]
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="6" fill="#18221d"/><text x="16" y="23" text-anchor="middle" font-family="Georgia,serif" font-size="19" font-weight="bold" fill="#f7f3ea">D</text></svg>
@@ -0,0 +1,84 @@
1
+ tokens
2
+ ink #171717
3
+ paper #fbfaf7
4
+ panel #ffffff
5
+ line #dedbd3
6
+ accent #16645a
7
+ warm #a83f2a
8
+ codebg #18221d
9
+ codeink #ecf3ea
10
+ radius 8px
11
+
12
+ .topnav
13
+ display flex
14
+ gap 1rem
15
+ align-items center
16
+ padding 1rem 2rem
17
+ border-bottom 1px solid $line
18
+ bg rgba(251,250,247,.85)
19
+ backdrop-filter blur(8px)
20
+ position sticky
21
+ top 0
22
+ z-index 10
23
+
24
+ .topnav strong
25
+ color $warm
26
+ font-size 1.05rem
27
+ margin-right .5rem
28
+
29
+ .topnav a
30
+ color $accent
31
+ text-decoration none
32
+
33
+ .topnav a:hover
34
+ text-decoration underline
35
+
36
+ code
37
+ bg $panel
38
+ border 1px solid $line
39
+ padding .1rem .32rem
40
+ radius 5px
41
+ font-size .9em
42
+
43
+ h1 code, h2 code, h3 code
44
+ font-size .85em
45
+
46
+ pre
47
+ bg $codebg
48
+ color $codeink
49
+ padding 1.1rem 1.3rem
50
+ margin 1.2rem 0
51
+ radius $radius
52
+ overflow-x auto
53
+ line-height 1.6
54
+ font-size .9rem
55
+ box-shadow inset 0 0 0 1px rgba(255,255,255,.08)
56
+
57
+ pre code
58
+ bg transparent
59
+ border 0
60
+ padding 0
61
+ color inherit
62
+ font-size 1em
63
+
64
+ table
65
+ border-collapse collapse
66
+ bg $panel
67
+ margin 1rem 0
68
+
69
+ th
70
+ text-align left
71
+ border 1px solid $line
72
+ padding .5rem .9rem
73
+ bg #f1ede4
74
+
75
+ td
76
+ border 1px solid $line
77
+ padding .5rem .9rem
78
+
79
+ blockquote
80
+ margin 1.2rem 0
81
+ padding .6rem 1.2rem
82
+ border-left 3px solid $accent
83
+ bg $panel
84
+ radius $radius
package/site/_/nav.wd ADDED
@@ -0,0 +1,8 @@
1
+ <nav class="topnav">
2
+ <strong>Darkmown</strong>
3
+ <a href="/">Home</a>
4
+ <a href="/docs/">Docs</a>
5
+ <a href="/markdown/">Plain Markdown</a>
6
+ <a href="/reactive/">Reactive</a>
7
+ <a href="/data/">Data &amp; Forms</a>
8
+ </nav>
@@ -0,0 +1,6 @@
1
+ [
2
+ "Markdown is the interface.",
3
+ "Static until proven reactive.",
4
+ "Rename the file, unlock the power.",
5
+ "Ship bytes, not frameworks."
6
+ ]
@@ -0,0 +1,11 @@
1
+ ## Long-running session sprinting
2
+
3
+ Darkmown treats sprinting as a visible operating loop, not as hidden recursion inside Markdown.
4
+
5
+ - PM keeps the acceptance checklist and decides whether the demo proves the promise.
6
+ - Development owns compiler, router, runtime, and demo integration.
7
+ - Research checks the theory against prior art and keeps naming or platform drift from leaking in.
8
+ - Testing owns route, include, asset, and browser smoke evidence.
9
+ - Review inspects the final diff and blocks sloppy completion.
10
+
11
+ The standing team size is 4 to 6. If work expands, add a design or architecture lane; if work shrinks, keep PM, development, testing, and review active.
@@ -0,0 +1,7 @@
1
+ [
2
+ { "id": 1, "name": "PM", "role": "Acceptance & demo proof", "lead": true },
3
+ { "id": 2, "name": "Dev", "role": "Compiler, router, runtime", "lead": false },
4
+ { "id": 3, "name": "Research", "role": "Prior art & drift control", "lead": false },
5
+ { "id": 4, "name": "Testing", "role": "Routes, includes, browser smoke", "lead": false },
6
+ { "id": 5, "name": "Review", "role": "Blocks sloppy completion", "lead": false }
7
+ ]
@@ -0,0 +1,3 @@
1
+ # Draft
2
+
3
+ This route should never be emitted.
@@ -0,0 +1,8 @@
1
+ const button = document.querySelector("[data-wd-double]");
2
+
3
+ if (button) {
4
+ button.addEventListener("click", () => {
5
+ const items = wd.get("cart:items") || [];
6
+ wd.set("cart:items", [...items, ...items]);
7
+ });
8
+ }
@@ -0,0 +1,98 @@
1
+ tokens
2
+ ink #171717
3
+ paper #faf8f3
4
+ panel #ffffff
5
+ line #dedbd3
6
+ accent #16645a
7
+ warm #a83f2a
8
+ radius 8px
9
+
10
+ page
11
+ margin 0
12
+ font ui-serif, Georgia, Cambria, serif
13
+ color $ink
14
+ bg $paper
15
+ line-height 1.7
16
+
17
+ main
18
+ max-width 820px
19
+ margin 0 auto
20
+ padding 3rem 2rem 5rem
21
+
22
+ h1
23
+ font-size 3rem
24
+ line-height 1
25
+
26
+ h2
27
+ margin-top 2rem
28
+ border-top 1px solid $line
29
+ padding-top 1.2rem
30
+
31
+ button
32
+ border 1px solid $ink
33
+ bg $ink
34
+ color white
35
+ padding .72rem 1rem
36
+ margin .3rem .5rem .3rem 0
37
+ radius $radius
38
+ font inherit
39
+ cursor pointer
40
+
41
+ button:hover
42
+ bg $accent
43
+ border-color $accent
44
+
45
+ a
46
+ color $accent
47
+
48
+ ul
49
+ border 1px solid $line
50
+ bg $panel
51
+ padding 1rem 1rem 1rem 2rem
52
+ radius $radius
53
+
54
+ .tally-card
55
+ border 1px solid $line
56
+ bg $panel
57
+ padding .25rem 1.2rem 1.2rem
58
+ margin 1rem 0
59
+ radius $radius
60
+
61
+ .tally-card h3
62
+ color $warm
63
+
64
+ .member-card
65
+ border 1px solid $line
66
+ bg $panel
67
+ padding .8rem 1.2rem
68
+ margin .6rem 0
69
+ radius $radius
70
+
71
+ form
72
+ display flex
73
+ gap .6rem
74
+ flex-wrap wrap
75
+ margin 1rem 0
76
+
77
+ input
78
+ padding .7rem .9rem
79
+ border 1px solid $line
80
+ radius $radius
81
+ font inherit
82
+ bg $panel
83
+
84
+ .pulse-button
85
+ border 0
86
+ bg $warm
87
+ color white
88
+ padding .85rem 1rem
89
+ radius $radius
90
+ font inherit
91
+ cursor pointer
92
+
93
+ .tally-card
94
+ border 1px solid $line
95
+ bg $panel
96
+ padding .25rem 1.2rem 1.2rem
97
+ margin 1rem 0
98
+ radius $radius