@zvndev/darkmown 1.0.0 → 1.2.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 +25 -0
- package/README.md +64 -3
- package/docs/cli.md +46 -16
- package/docs/spec-alignment.md +16 -3
- package/package.json +2 -2
- package/site/_/roadmap.json +3 -1
- package/site/pages/app.wd +1 -1
- package/site/pages/data.wd +2 -2
- package/site/pages/docs/index.wd +22 -0
- package/site/pages/folio/cart.wd +2 -2
- package/site/pages/forge/index.wd +1 -1
- package/site/pages/interactions/index.skin +113 -0
- package/site/pages/interactions/index.wd +86 -0
- package/site/pages/ledger/index.wd +10 -1
- package/src/api-runner.js +219 -0
- package/src/behaviors/carousel.js +153 -0
- package/src/behaviors/sortable.js +175 -0
- package/src/builder.js +199 -4
- package/src/cli.js +79 -54
- package/src/compiler/body.js +78 -3
- package/src/compiler/context.js +12 -1
- package/src/compiler/directives.js +123 -0
- package/src/compiler/interpolation.js +15 -14
- package/src/compiler/loops.js +57 -8
- package/src/compiler/page.js +9 -3
- package/src/deploy.js +174 -0
- package/src/headers.js +4 -0
- package/src/runtime.js +3 -1
- package/src/scaffold.js +79 -147
- package/src/templates/blog/README.md +25 -0
- package/src/templates/blog/site/_/nav.wd +6 -0
- package/src/templates/blog/site/_/posts.json +16 -0
- package/src/templates/blog/site/pages/index.skin +51 -0
- package/src/templates/blog/site/pages/index.wd +24 -0
- package/src/templates/blog/site/pages/posts/hello-darkmown.md +16 -0
- package/src/templates/blog/site/pages/posts/markdown-native.md +20 -0
- package/src/templates/dashboard/README.md +25 -0
- package/src/templates/dashboard/api/metrics.js +15 -0
- package/src/templates/dashboard/site/_/nav.wd +6 -0
- package/src/templates/dashboard/site/pages/index.skin +74 -0
- package/src/templates/dashboard/site/pages/index.wd +75 -0
- package/src/templates/landing/README.md +25 -0
- package/src/templates/landing/site/_/nav.wd +6 -0
- package/src/templates/landing/site/pages/index.skin +140 -0
- package/src/templates/landing/site/pages/index.wd +54 -0
- package/src/templates/starter/README.md +32 -0
- package/src/templates/starter/site/_/nav.wd +7 -0
- package/src/templates/starter/site/pages/about.md +10 -0
- package/src/templates/starter/site/pages/index.skin +47 -0
- package/src/templates/starter/site/pages/index.wd +27 -0
- package/src/templates/store/README.md +28 -0
- package/src/templates/store/api/checkout.js +18 -0
- package/src/templates/store/site/_/nav.wd +6 -0
- package/src/templates/store/site/_/products.json +8 -0
- package/src/templates/store/site/pages/index.skin +100 -0
- package/src/templates/store/site/pages/index.wd +83 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
tokens
|
|
2
|
+
ink #14161c
|
|
3
|
+
paper #ffffff
|
|
4
|
+
soft #f4f5f8
|
|
5
|
+
line #e6e8ee
|
|
6
|
+
accent #5b4bf5
|
|
7
|
+
|
|
8
|
+
page
|
|
9
|
+
margin 0
|
|
10
|
+
font ui-sans-serif, system-ui, sans-serif
|
|
11
|
+
color $ink
|
|
12
|
+
bg $paper
|
|
13
|
+
line-height 1.6
|
|
14
|
+
|
|
15
|
+
main
|
|
16
|
+
max-width 900px
|
|
17
|
+
margin 0 auto
|
|
18
|
+
padding 1rem 1.5rem 4rem
|
|
19
|
+
|
|
20
|
+
nav
|
|
21
|
+
display flex
|
|
22
|
+
align-items center
|
|
23
|
+
justify-content space-between
|
|
24
|
+
max-width 900px
|
|
25
|
+
margin 0 auto
|
|
26
|
+
padding 1.25rem 1.5rem
|
|
27
|
+
|
|
28
|
+
nav strong
|
|
29
|
+
font-size 1.15rem
|
|
30
|
+
|
|
31
|
+
nav a
|
|
32
|
+
color $accent
|
|
33
|
+
text-decoration none
|
|
34
|
+
|
|
35
|
+
.hero
|
|
36
|
+
text-align center
|
|
37
|
+
padding 3rem 1rem 2rem
|
|
38
|
+
|
|
39
|
+
.hero h1
|
|
40
|
+
font-size 2.75rem
|
|
41
|
+
margin 0 0 .5rem
|
|
42
|
+
|
|
43
|
+
.lede
|
|
44
|
+
font-size 1.2rem
|
|
45
|
+
color #555c6b
|
|
46
|
+
margin 0 0 1.5rem
|
|
47
|
+
|
|
48
|
+
.cta
|
|
49
|
+
display inline-block
|
|
50
|
+
bg $accent
|
|
51
|
+
color white
|
|
52
|
+
text-decoration none
|
|
53
|
+
padding .8rem 1.4rem
|
|
54
|
+
radius 10px
|
|
55
|
+
font-weight 600
|
|
56
|
+
|
|
57
|
+
.features
|
|
58
|
+
display grid
|
|
59
|
+
grid-template-columns repeat(auto-fit, minmax(220px, 1fr))
|
|
60
|
+
gap 1rem
|
|
61
|
+
margin 2rem 0
|
|
62
|
+
|
|
63
|
+
.feature
|
|
64
|
+
background $soft
|
|
65
|
+
border 1px solid $line
|
|
66
|
+
radius 12px
|
|
67
|
+
padding 1.25rem
|
|
68
|
+
|
|
69
|
+
.feature h3
|
|
70
|
+
margin .25rem 0 .5rem
|
|
71
|
+
|
|
72
|
+
.wd-carousel
|
|
73
|
+
position relative
|
|
74
|
+
margin 1rem 0 2rem
|
|
75
|
+
|
|
76
|
+
.wd-carousel-track
|
|
77
|
+
display flex
|
|
78
|
+
overflow-x auto
|
|
79
|
+
scroll-snap-type x mandatory
|
|
80
|
+
scroll-behavior smooth
|
|
81
|
+
gap 1rem
|
|
82
|
+
|
|
83
|
+
.wd-carousel-track .quote
|
|
84
|
+
flex 0 0 100%
|
|
85
|
+
scroll-snap-align start
|
|
86
|
+
background $soft
|
|
87
|
+
border 1px solid $line
|
|
88
|
+
radius 12px
|
|
89
|
+
padding 1.75rem
|
|
90
|
+
font-size 1.2rem
|
|
91
|
+
|
|
92
|
+
.quote .who
|
|
93
|
+
margin .5rem 0 0
|
|
94
|
+
color #555c6b
|
|
95
|
+
font-size .95rem
|
|
96
|
+
|
|
97
|
+
.wd-carousel-prev
|
|
98
|
+
position absolute
|
|
99
|
+
left -.5rem
|
|
100
|
+
top 40%
|
|
101
|
+
|
|
102
|
+
.wd-carousel-next
|
|
103
|
+
position absolute
|
|
104
|
+
right -.5rem
|
|
105
|
+
top 40%
|
|
106
|
+
|
|
107
|
+
.wd-carousel-prev, .wd-carousel-next
|
|
108
|
+
bg white
|
|
109
|
+
color $ink
|
|
110
|
+
border 1px solid $line
|
|
111
|
+
radius 999px
|
|
112
|
+
width 2.25rem
|
|
113
|
+
height 2.25rem
|
|
114
|
+
cursor pointer
|
|
115
|
+
|
|
116
|
+
.wd-carousel-dots
|
|
117
|
+
display flex
|
|
118
|
+
justify-content center
|
|
119
|
+
gap .5rem
|
|
120
|
+
margin-top .75rem
|
|
121
|
+
|
|
122
|
+
.wd-carousel-dot
|
|
123
|
+
width .6rem
|
|
124
|
+
height .6rem
|
|
125
|
+
padding 0
|
|
126
|
+
border 0
|
|
127
|
+
radius 999px
|
|
128
|
+
bg $line
|
|
129
|
+
cursor pointer
|
|
130
|
+
|
|
131
|
+
.wd-carousel-dot[aria-current="true"]
|
|
132
|
+
bg $accent
|
|
133
|
+
|
|
134
|
+
.cta-band
|
|
135
|
+
text-align center
|
|
136
|
+
background $soft
|
|
137
|
+
border 1px solid $line
|
|
138
|
+
radius 16px
|
|
139
|
+
padding 2.5rem
|
|
140
|
+
margin-top 2rem
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Acme — ship faster
|
|
3
|
+
description: A marketing one-pager built with Darkmown. Zero framework JS on the static parts, with a testimonials carousel.
|
|
4
|
+
transitions: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
@include /nav.wd
|
|
8
|
+
|
|
9
|
+
<main>
|
|
10
|
+
|
|
11
|
+
::: hero
|
|
12
|
+
# Ship faster with *Acme*
|
|
13
|
+
<p class="lede">The all-in-one toolkit your team actually enjoys using.</p>
|
|
14
|
+
<a class="cta" href="#start">Get started</a>
|
|
15
|
+
:::
|
|
16
|
+
|
|
17
|
+
::: features
|
|
18
|
+
::: feature
|
|
19
|
+
### ⚡ Fast
|
|
20
|
+
Static pages, zero framework JavaScript. Instant loads, perfect Lighthouse.
|
|
21
|
+
:::
|
|
22
|
+
::: feature
|
|
23
|
+
### 🧩 Simple
|
|
24
|
+
Write Markdown. Add a directive only on the pages that need interactivity.
|
|
25
|
+
:::
|
|
26
|
+
::: feature
|
|
27
|
+
### 🚀 Deploy anywhere
|
|
28
|
+
One command to Vercel or Cloudflare — your API comes along for free.
|
|
29
|
+
:::
|
|
30
|
+
:::
|
|
31
|
+
|
|
32
|
+
## What teams say
|
|
33
|
+
|
|
34
|
+
:carousel
|
|
35
|
+
::: quote
|
|
36
|
+
> "Acme cut our launch time in half."
|
|
37
|
+
<p class="who">— Riley, Head of Product</p>
|
|
38
|
+
:::
|
|
39
|
+
::: quote
|
|
40
|
+
> "The simplest stack we've ever shipped on."
|
|
41
|
+
<p class="who">— Sam, CTO</p>
|
|
42
|
+
:::
|
|
43
|
+
::: quote
|
|
44
|
+
> "Our marketing pages load instantly now."
|
|
45
|
+
<p class="who">— Jordan, Growth</p>
|
|
46
|
+
:::
|
|
47
|
+
:endcarousel
|
|
48
|
+
|
|
49
|
+
::: cta-band #start
|
|
50
|
+
## Ready to build?
|
|
51
|
+
<a class="cta" href="/">Start now</a>
|
|
52
|
+
:::
|
|
53
|
+
|
|
54
|
+
</main>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# My Darkmown site
|
|
2
|
+
|
|
3
|
+
Run `npm install` and `npm run dev` to start the live compiler.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `npm run dev` — live compiler on http://localhost:5173 with hot reload.
|
|
8
|
+
- `npm run build` — compile the site to `dist/`.
|
|
9
|
+
- `npm run preview` — serve the built `dist/` locally.
|
|
10
|
+
|
|
11
|
+
## Deploy
|
|
12
|
+
|
|
13
|
+
One command (it builds, then prints your live URL — log in if prompted):
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npx darkmown deploy vercel # or: npx darkmown deploy cloudflare
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or one-click, after pushing this repo to GitHub (replace `YOUR_REPO_URL`):
|
|
20
|
+
|
|
21
|
+
[](https://vercel.com/new/clone?repository-url=YOUR_REPO_URL)
|
|
22
|
+
|
|
23
|
+
[](https://deploy.workers.cloudflare.com/?url=YOUR_REPO_URL)
|
|
24
|
+
|
|
25
|
+
## Layout
|
|
26
|
+
|
|
27
|
+
- Pages live in `site/pages` — `.md` stays plain Markdown, `.wd` adds directives.
|
|
28
|
+
- Shared includes live in `site/_`.
|
|
29
|
+
- Backend functions live in `api/` — plain Web-standard `(request) => Response`.
|
|
30
|
+
They run in `darkmown dev` and on Vercel/Cloudflare with no extra config.
|
|
31
|
+
- Hidden route files start with `.` or `-`.
|
|
32
|
+
- Colocated `index.skin` and `index.js` attach to a page automatically.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
tokens
|
|
2
|
+
ink #172026
|
|
3
|
+
paper #f7f2e8
|
|
4
|
+
accent #0f8b8d
|
|
5
|
+
|
|
6
|
+
page
|
|
7
|
+
margin 0
|
|
8
|
+
font ui-sans-serif, system-ui, sans-serif
|
|
9
|
+
color $ink
|
|
10
|
+
bg $paper
|
|
11
|
+
|
|
12
|
+
main
|
|
13
|
+
max-width 760px
|
|
14
|
+
margin 0 auto
|
|
15
|
+
padding 3rem 1.5rem
|
|
16
|
+
|
|
17
|
+
nav
|
|
18
|
+
display flex
|
|
19
|
+
align-items center
|
|
20
|
+
justify-content space-between
|
|
21
|
+
gap 1rem
|
|
22
|
+
max-width 760px
|
|
23
|
+
margin 0 auto
|
|
24
|
+
padding 1.25rem 1.5rem
|
|
25
|
+
|
|
26
|
+
nav strong
|
|
27
|
+
font-size 1.1rem
|
|
28
|
+
|
|
29
|
+
nav .links
|
|
30
|
+
display flex
|
|
31
|
+
gap 1.25rem
|
|
32
|
+
|
|
33
|
+
nav a
|
|
34
|
+
color $ink
|
|
35
|
+
text-decoration none
|
|
36
|
+
opacity .8
|
|
37
|
+
|
|
38
|
+
nav a:hover
|
|
39
|
+
opacity 1
|
|
40
|
+
color $accent
|
|
41
|
+
|
|
42
|
+
button
|
|
43
|
+
bg $accent
|
|
44
|
+
color white
|
|
45
|
+
border 0
|
|
46
|
+
padding .75rem 1rem
|
|
47
|
+
radius 8px
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: My Darkmown site
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
@include /nav.wd
|
|
6
|
+
|
|
7
|
+
<main>
|
|
8
|
+
|
|
9
|
+
# My Darkmown site
|
|
10
|
+
|
|
11
|
+
Plain Markdown works. Rename to `.wd` when you want directives.
|
|
12
|
+
|
|
13
|
+
:state count = 0
|
|
14
|
+
|
|
15
|
+
Count: { count }
|
|
16
|
+
|
|
17
|
+
:button "Increment" -> count++
|
|
18
|
+
|
|
19
|
+
:state todos = ["Write a page"]
|
|
20
|
+
|
|
21
|
+
@loop todos into todo
|
|
22
|
+
- { todo }
|
|
23
|
+
@endloop
|
|
24
|
+
|
|
25
|
+
:button "Add" -> todos += "Add a loop"
|
|
26
|
+
|
|
27
|
+
</main>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Store — a Darkmown template
|
|
2
|
+
|
|
3
|
+
A storefront in Markdown: a product grid with live search, a shared `:store` cart,
|
|
4
|
+
and a checkout that POSTs to a serverless function.
|
|
5
|
+
|
|
6
|
+
- **Static catalog** — `site/pages/index.wd` loops `site/_/products.json`.
|
|
7
|
+
- **Cart** — a `:store` shared across the page; add/remove with `:button`.
|
|
8
|
+
- **Checkout** — `:form action="/api/checkout"` posts to `api/checkout.js`, a plain
|
|
9
|
+
Web-standard `(request) => Response` function that runs in `darkmown dev` and on
|
|
10
|
+
every host. Replace its body with your real payment + fulfilment logic.
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
- `npm run dev` — live compiler + the local `/api/*` runner on http://localhost:5173.
|
|
15
|
+
- `npm run build` — compile to `dist/`.
|
|
16
|
+
- `npm run preview` — serve the built `dist/` locally (static only; use `dev` for `/api`).
|
|
17
|
+
|
|
18
|
+
## Deploy
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npx darkmown deploy vercel # or: npx darkmown deploy cloudflare
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Your `api/checkout.js` deploys automatically alongside the static site.
|
|
25
|
+
|
|
26
|
+
[](https://vercel.com/new/clone?repository-url=YOUR_REPO_URL)
|
|
27
|
+
|
|
28
|
+
[](https://deploy.workers.cloudflare.com/?url=YOUR_REPO_URL)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Demo checkout endpoint. A plain Web-standard `(request) => Response` handler:
|
|
2
|
+
// it runs in `darkmown dev` and on Vercel Edge / Cloudflare Pages with no extra
|
|
3
|
+
// config. Echoes the order back with an id — swap in your real payment and
|
|
4
|
+
// fulfilment logic here (validate input, charge, write to a DB, send email).
|
|
5
|
+
export const config = { runtime: "edge" };
|
|
6
|
+
|
|
7
|
+
export default async function (request) {
|
|
8
|
+
if (request.method !== "POST") {
|
|
9
|
+
return Response.json({ ok: false, error: "POST only" }, { status: 405 });
|
|
10
|
+
}
|
|
11
|
+
const order = Object.fromEntries(await request.formData());
|
|
12
|
+
return Response.json({
|
|
13
|
+
ok: true,
|
|
14
|
+
received: order,
|
|
15
|
+
orderId: `ORD-${Date.now().toString(36).toUpperCase()}`,
|
|
16
|
+
at: new Date().toISOString()
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "name": "Ceramic Mug", "emoji": "☕️", "price": 18 },
|
|
3
|
+
{ "name": "Linen Tote", "emoji": "👜", "price": 32 },
|
|
4
|
+
{ "name": "Pocket Notebook", "emoji": "📓", "price": 12 },
|
|
5
|
+
{ "name": "Desk Lamp", "emoji": "💡", "price": 64 },
|
|
6
|
+
{ "name": "Plant Pot", "emoji": "🪴", "price": 24 },
|
|
7
|
+
{ "name": "Wall Clock", "emoji": "🕰️", "price": 48 }
|
|
8
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
tokens
|
|
2
|
+
ink #1a1f2b
|
|
3
|
+
paper #fbfbf9
|
|
4
|
+
line #e6e4dd
|
|
5
|
+
accent #146b54
|
|
6
|
+
|
|
7
|
+
page
|
|
8
|
+
margin 0
|
|
9
|
+
font ui-sans-serif, system-ui, sans-serif
|
|
10
|
+
color $ink
|
|
11
|
+
bg $paper
|
|
12
|
+
line-height 1.55
|
|
13
|
+
|
|
14
|
+
main
|
|
15
|
+
max-width 860px
|
|
16
|
+
margin 0 auto
|
|
17
|
+
padding 2rem 1.5rem
|
|
18
|
+
|
|
19
|
+
nav
|
|
20
|
+
display flex
|
|
21
|
+
align-items center
|
|
22
|
+
justify-content space-between
|
|
23
|
+
max-width 860px
|
|
24
|
+
margin 0 auto
|
|
25
|
+
padding 1.25rem 1.5rem
|
|
26
|
+
|
|
27
|
+
nav strong
|
|
28
|
+
font-size 1.1rem
|
|
29
|
+
|
|
30
|
+
nav a
|
|
31
|
+
color $accent
|
|
32
|
+
text-decoration none
|
|
33
|
+
|
|
34
|
+
input
|
|
35
|
+
font inherit
|
|
36
|
+
padding .6rem .75rem
|
|
37
|
+
border 1px solid $line
|
|
38
|
+
radius 8px
|
|
39
|
+
width 100%
|
|
40
|
+
max-width 320px
|
|
41
|
+
|
|
42
|
+
.catalog
|
|
43
|
+
display grid
|
|
44
|
+
grid-template-columns repeat(auto-fill, minmax(160px, 1fr))
|
|
45
|
+
gap 1rem
|
|
46
|
+
margin 1.5rem 0
|
|
47
|
+
|
|
48
|
+
.product
|
|
49
|
+
border 1px solid $line
|
|
50
|
+
radius 12px
|
|
51
|
+
padding 1rem
|
|
52
|
+
text-align center
|
|
53
|
+
background white
|
|
54
|
+
|
|
55
|
+
.emoji
|
|
56
|
+
font-size 2.25rem
|
|
57
|
+
|
|
58
|
+
.pname
|
|
59
|
+
font-weight 600
|
|
60
|
+
margin .35rem 0 .15rem
|
|
61
|
+
|
|
62
|
+
.price
|
|
63
|
+
color $accent
|
|
64
|
+
font-weight 600
|
|
65
|
+
margin-bottom .6rem
|
|
66
|
+
|
|
67
|
+
button
|
|
68
|
+
font inherit
|
|
69
|
+
bg $accent
|
|
70
|
+
color white
|
|
71
|
+
border 0
|
|
72
|
+
padding .55rem .9rem
|
|
73
|
+
radius 8px
|
|
74
|
+
cursor pointer
|
|
75
|
+
|
|
76
|
+
.line
|
|
77
|
+
display flex
|
|
78
|
+
align-items center
|
|
79
|
+
justify-content space-between
|
|
80
|
+
gap 1rem
|
|
81
|
+
border 1px solid $line
|
|
82
|
+
radius 10px
|
|
83
|
+
padding .6rem .9rem
|
|
84
|
+
margin .4rem 0
|
|
85
|
+
background white
|
|
86
|
+
|
|
87
|
+
.line button
|
|
88
|
+
bg transparent
|
|
89
|
+
color $ink
|
|
90
|
+
border 1px solid $line
|
|
91
|
+
|
|
92
|
+
.cart-foot
|
|
93
|
+
font-size 1.1rem
|
|
94
|
+
margin .75rem 0
|
|
95
|
+
|
|
96
|
+
.receipt
|
|
97
|
+
border 1px solid $accent
|
|
98
|
+
radius 10px
|
|
99
|
+
padding 1rem
|
|
100
|
+
background #ecf6f1
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shop — built with Darkmown
|
|
3
|
+
description: A storefront in Markdown — live search, a shared cart, and a checkout that posts to a serverless function.
|
|
4
|
+
transitions: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
@include /nav.wd
|
|
8
|
+
|
|
9
|
+
<main>
|
|
10
|
+
|
|
11
|
+
::: section #shop
|
|
12
|
+
|
|
13
|
+
:store cart = []
|
|
14
|
+
:state q = ""
|
|
15
|
+
:computed itemCount = count(cart)
|
|
16
|
+
:computed subtotal = sum(cart, price)
|
|
17
|
+
|
|
18
|
+
# Shop
|
|
19
|
+
|
|
20
|
+
::: shop-search
|
|
21
|
+
:bind q placeholder="Search products…"
|
|
22
|
+
:::
|
|
23
|
+
|
|
24
|
+
::: catalog
|
|
25
|
+
@loop /products.json into p where p.name contains q
|
|
26
|
+
::: card .product
|
|
27
|
+
::: emoji
|
|
28
|
+
{ p.emoji }
|
|
29
|
+
:::
|
|
30
|
+
::: pname
|
|
31
|
+
{ p.name }
|
|
32
|
+
:::
|
|
33
|
+
::: price
|
|
34
|
+
{ p.price | money }
|
|
35
|
+
:::
|
|
36
|
+
:button "Add to cart" -> cart += p
|
|
37
|
+
:::
|
|
38
|
+
@empty
|
|
39
|
+
::: card .empty
|
|
40
|
+
Nothing matches "**{ q }**".
|
|
41
|
+
:::
|
|
42
|
+
@endloop
|
|
43
|
+
:::
|
|
44
|
+
|
|
45
|
+
## Your cart ({ itemCount })
|
|
46
|
+
|
|
47
|
+
@loop cart into item
|
|
48
|
+
::: card .line
|
|
49
|
+
{ item.emoji } **{ item.name }** — { item.price | money }
|
|
50
|
+
:button "Remove" -> cart remove item
|
|
51
|
+
:::
|
|
52
|
+
@empty
|
|
53
|
+
Your cart is empty — add something above.
|
|
54
|
+
@endloop
|
|
55
|
+
|
|
56
|
+
:if itemCount
|
|
57
|
+
::: cart-foot
|
|
58
|
+
**Subtotal: { subtotal | money }**
|
|
59
|
+
:::
|
|
60
|
+
|
|
61
|
+
### Checkout
|
|
62
|
+
|
|
63
|
+
This form posts to `api/checkout.js` — a plain serverless function that runs in
|
|
64
|
+
`darkmown dev` and on Vercel/Cloudflare alike. With JavaScript off it degrades to
|
|
65
|
+
a native POST.
|
|
66
|
+
|
|
67
|
+
:form action="/api/checkout" into receipt
|
|
68
|
+
:input name placeholder="Full name" required
|
|
69
|
+
:input email type=email placeholder="you@example.com" required
|
|
70
|
+
:submit "Place order"
|
|
71
|
+
:endform
|
|
72
|
+
|
|
73
|
+
:if receipt
|
|
74
|
+
::: card .receipt
|
|
75
|
+
**Order placed ✓** — thanks { receipt.received.name }. A confirmation is on its way
|
|
76
|
+
to { receipt.received.email }. Order { receipt.orderId }.
|
|
77
|
+
:::
|
|
78
|
+
:endif
|
|
79
|
+
:endif
|
|
80
|
+
|
|
81
|
+
:::
|
|
82
|
+
|
|
83
|
+
</main>
|