@invisibleloop/pulse 0.12.3 → 0.14.1
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/.claude/commands/verify.md +9 -5
- package/.claude/pulse-checklist.md +21 -1
- package/.github/workflows/publish.yml +1 -0
- package/.pulse-verified +1 -0
- package/CLAUDE.md +56 -15
- package/docs/.claude/commands/verify.md +4 -0
- package/docs/.claude/pulse-checklist.md +20 -2
- package/docs/public/.pulse-ui-version +1 -1
- package/docs/public/andypixel.css +237 -0
- package/docs/public/bookworms.css +422 -0
- package/docs/public/coutumes.css +584 -0
- package/docs/public/images/bookworms/hero-books.jpg +0 -0
- package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
- package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
- package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
- package/docs/public/images/journey/178.jpg +0 -0
- package/docs/public/images/journey/29.jpg +0 -0
- package/docs/public/images/retrowheels/hero.jpg +0 -0
- package/docs/public/images/shop/devbook-pro.jpg +0 -0
- package/docs/public/images/suzukibento/1015.jpg +0 -0
- package/docs/public/images/suzukibento/1025.jpg +0 -0
- package/docs/public/images/suzukibento/1043.jpg +0 -0
- package/docs/public/images/suzukibento/1059.jpg +0 -0
- package/docs/public/images/suzukibento/1062.jpg +0 -0
- package/docs/public/images/suzukibento/1084.jpg +0 -0
- package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
- package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
- package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
- package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
- package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
- package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
- package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
- package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
- package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
- package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
- package/docs/public/journey.css +528 -0
- package/docs/public/pulse-ui.css +1036 -22
- package/docs/public/retrowheels.css +402 -0
- package/docs/public/shop.css +235 -0
- package/docs/public/sparkys.css +349 -0
- package/docs/public/suzukibento.css +517 -0
- package/docs/public/swish.css +394 -0
- package/docs/server.js +19 -4
- package/docs/src/lib/nav.js +14 -3
- package/docs/src/pages/components/decorate.js +74 -0
- package/docs/src/pages/components/gallery.js +102 -0
- package/docs/src/pages/components/hero.js +1 -0
- package/docs/src/pages/components/marquee.js +76 -0
- package/docs/src/pages/components/photo-card.js +74 -0
- package/docs/src/pages/components/section.js +40 -1
- package/docs/src/pages/meta.js +26 -0
- package/docs/src/pages/templates/andypixel.js +260 -0
- package/docs/src/pages/templates/bookworms.js +227 -0
- package/docs/src/pages/templates/coutumes.js +226 -0
- package/docs/src/pages/templates/journey.js +186 -0
- package/docs/src/pages/templates/retrowheels.js +342 -0
- package/docs/src/pages/templates/shop.js +257 -0
- package/docs/src/pages/templates/sparkys.js +249 -0
- package/docs/src/pages/templates/suzukibento.js +200 -0
- package/docs/src/pages/templates/swish.js +313 -0
- package/docs/src/pages/templates.js +313 -2
- package/docs/src/pages/testing.js +2 -1
- package/examples/andypixel.css +237 -0
- package/examples/andypixel.js +258 -0
- package/examples/bookworms.css +422 -0
- package/examples/bookworms.js +225 -0
- package/examples/contact.js +1 -0
- package/examples/counter.js +1 -0
- package/examples/dev.server.js +17 -0
- package/examples/journey.css +528 -0
- package/examples/journey.js +186 -0
- package/examples/pricing.js +1 -0
- package/examples/quiz.js +1 -0
- package/examples/retrowheels.css +402 -0
- package/examples/retrowheels.js +340 -0
- package/examples/shared.js +6 -0
- package/examples/suzukibento.css +513 -0
- package/examples/suzukibento.js +200 -0
- package/examples/swish.css +394 -0
- package/examples/swish.js +311 -0
- package/examples/todos.js +1 -0
- package/package.json +2 -3
- package/public/.pulse-ui-version +1 -1
- package/public/images/bookworms/hero-books.jpg +0 -0
- package/public/images/bookworms/how-we-read-now.jpg +0 -0
- package/public/images/bookworms/lantern-keepers.jpg +0 -0
- package/public/images/bookworms/provincial-bookseller.jpg +0 -0
- package/public/images/journey/178.jpg +0 -0
- package/public/images/journey/29.jpg +0 -0
- package/public/images/retrowheels/hero.jpg +0 -0
- package/public/images/shop/devbook-pro.jpg +0 -0
- package/public/images/suzukibento/1015.jpg +0 -0
- package/public/images/suzukibento/1025.jpg +0 -0
- package/public/images/suzukibento/1043.jpg +0 -0
- package/public/images/suzukibento/1059.jpg +0 -0
- package/public/images/suzukibento/1062.jpg +0 -0
- package/public/images/suzukibento/1084.jpg +0 -0
- package/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
- package/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
- package/public/images/swish/1485518882345-15568b007407.jpg +0 -0
- package/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
- package/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
- package/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
- package/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
- package/public/images/swish/1551803091-e20673f15770.jpg +0 -0
- package/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
- package/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
- package/public/pulse-ui.css +1036 -22
- package/scripts/build.js +72 -18
- package/src/agent/checklist.md +20 -2
- package/src/agent/commands/verify.md +4 -0
- package/src/agent/guide-components.md +145 -7
- package/src/agent/guide-design-gallery.md +288 -0
- package/src/agent/guide-design-references.md +172 -0
- package/src/agent/guide-explore.md +313 -0
- package/src/agent/guide-styles.md +197 -11
- package/src/agent/guide-templates.md +220 -10
- package/src/agent/guide.md +9 -4
- package/src/agent/workflow.md +208 -58
- package/src/cli/dev.js +39 -7
- package/src/cli/fmt.js +113 -0
- package/src/cli/index.js +63 -30
- package/src/cli/scaffold.js +2 -2
- package/src/html.js +6 -1
- package/src/mcp/server.js +2415 -101
- package/src/mcp/validate-worker.js +34 -5
- package/src/runtime/ssr.js +4 -1
- package/src/server/index.js +22 -2
- package/src/spec/schema.js +5 -0
- package/src/testing/html.js +130 -4
- package/src/testing/testing.test.js +58 -0
- package/src/ui/banner.js +24 -9
- package/src/ui/brand-statement.js +58 -0
- package/src/ui/button.js +12 -1
- package/src/ui/card.js +20 -1
- package/src/ui/colophon.js +29 -0
- package/src/ui/contact-row.js +41 -0
- package/src/ui/cta.js +18 -4
- package/src/ui/decorate.js +55 -0
- package/src/ui/display-heading.js +44 -0
- package/src/ui/drop-cap.js +29 -0
- package/src/ui/editorial-hero.js +63 -0
- package/src/ui/entry-list.js +36 -0
- package/src/ui/feature.js +20 -3
- package/src/ui/footer.js +78 -9
- package/src/ui/gallery.js +56 -0
- package/src/ui/hero.js +68 -18
- package/src/ui/image-pair.js +72 -0
- package/src/ui/index.js +20 -0
- package/src/ui/input.js +1 -0
- package/src/ui/marquee.js +48 -0
- package/src/ui/nav.js +19 -9
- package/src/ui/photo-card.js +44 -0
- package/src/ui/product-card.js +65 -0
- package/src/ui/pullquote.js +14 -3
- package/src/ui/search.js +1 -0
- package/src/ui/section-label.js +38 -0
- package/src/ui/section.js +1 -1
- package/src/ui/service-card.js +47 -0
- package/src/ui/stat-grid.js +40 -0
- package/src/ui/stat.js +16 -2
- package/src/ui/testimonial.js +17 -6
- package/src/ui/textarea.js +1 -0
- package/src/ui/top-strip.js +47 -0
- package/src/ui/trust-list.js +46 -0
- package/src/ui/ui.test.js +35 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* bookworms.css — BookWorms independent bookshop example
|
|
3
|
+
*
|
|
4
|
+
* Palette: warm off-white, deep ink, oxblood accent.
|
|
5
|
+
* Font: Fraunces (display headings, loaded via Google Fonts in meta.styles)
|
|
6
|
+
*
|
|
7
|
+
* Split into two logical sections:
|
|
8
|
+
* 1. Token overrides — hex values, palette definitions ([data-theme="light"])
|
|
9
|
+
* 2. Component styles — layout, typography, custom elements (var() only)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ==========================================================================
|
|
13
|
+
1. Token overrides (hex values live here)
|
|
14
|
+
========================================================================== */
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Override --font and --font-display so --ui-font / --ui-font-display
|
|
18
|
+
* cascade to Fraunces throughout components and custom elements.
|
|
19
|
+
*/
|
|
20
|
+
:root {
|
|
21
|
+
--font: 'Fraunces', Georgia, 'Times New Roman', serif;
|
|
22
|
+
--font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
[data-theme="light"] {
|
|
26
|
+
--ui-bg: #f6f2ec;
|
|
27
|
+
--ui-surface: #ffffff;
|
|
28
|
+
--ui-surface-2: #efe9df;
|
|
29
|
+
--ui-border: #d8cfc1;
|
|
30
|
+
--ui-text: #1a1a1a;
|
|
31
|
+
--ui-muted: #5a5043;
|
|
32
|
+
--ui-accent: #6b2a2a;
|
|
33
|
+
--ui-accent-hover: #4f1d1d;
|
|
34
|
+
--ui-accent-dim: #f0e3e3;
|
|
35
|
+
--ui-accent-text: #ffffff;
|
|
36
|
+
|
|
37
|
+
/* BookWorms-specific token */
|
|
38
|
+
--bw-bg-alt: #efe9df;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* ==========================================================================
|
|
42
|
+
2. Component styles (var() only — no hex values below this line)
|
|
43
|
+
========================================================================== */
|
|
44
|
+
|
|
45
|
+
body { line-height: 1.6; }
|
|
46
|
+
|
|
47
|
+
/* --------------------------------------------------------------------------
|
|
48
|
+
Shell (centred max-width wrapper)
|
|
49
|
+
-------------------------------------------------------------------------- */
|
|
50
|
+
.bw-shell {
|
|
51
|
+
max-width: 1180px;
|
|
52
|
+
margin: 0 auto;
|
|
53
|
+
padding: 0 var(--ui-space-6);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* --------------------------------------------------------------------------
|
|
57
|
+
Hero
|
|
58
|
+
-------------------------------------------------------------------------- */
|
|
59
|
+
.bw-hero {
|
|
60
|
+
padding: var(--ui-space-20) 0 var(--ui-space-16);
|
|
61
|
+
border-bottom: 1px solid var(--ui-border);
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.bw-hero__inner {
|
|
66
|
+
display: grid;
|
|
67
|
+
grid-template-columns: 1fr 1fr;
|
|
68
|
+
gap: var(--ui-space-12);
|
|
69
|
+
align-items: center;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.bw-hero__content {
|
|
73
|
+
min-width: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.bw-hero__image {
|
|
77
|
+
position: relative;
|
|
78
|
+
border-radius: var(--ui-radius-lg);
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
aspect-ratio: 3 / 4;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.bw-hero__image img {
|
|
84
|
+
width: 100%;
|
|
85
|
+
height: 100%;
|
|
86
|
+
object-fit: cover;
|
|
87
|
+
display: block;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@media (max-width: 700px) {
|
|
91
|
+
.bw-hero__inner {
|
|
92
|
+
grid-template-columns: 1fr;
|
|
93
|
+
}
|
|
94
|
+
.bw-hero__image {
|
|
95
|
+
aspect-ratio: 4 / 3;
|
|
96
|
+
order: -1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.bw-eyebrow {
|
|
101
|
+
font-size: var(--ui-text-xs);
|
|
102
|
+
letter-spacing: 0.18em;
|
|
103
|
+
text-transform: uppercase;
|
|
104
|
+
color: var(--ui-muted);
|
|
105
|
+
margin-bottom: var(--ui-space-6);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.bw-hero-title {
|
|
109
|
+
font-family: var(--ui-font-display);
|
|
110
|
+
font-size: clamp(2.5rem, 7vw, 5.5rem);
|
|
111
|
+
font-weight: 500;
|
|
112
|
+
line-height: 1.02;
|
|
113
|
+
letter-spacing: -0.025em;
|
|
114
|
+
color: var(--ui-text);
|
|
115
|
+
margin-bottom: var(--ui-space-8);
|
|
116
|
+
max-width: 18ch;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.bw-hero-lede {
|
|
120
|
+
font-size: var(--ui-text-xl);
|
|
121
|
+
line-height: 1.55;
|
|
122
|
+
color: var(--ui-muted);
|
|
123
|
+
max-width: 60ch;
|
|
124
|
+
margin-bottom: var(--ui-space-8);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* --------------------------------------------------------------------------
|
|
128
|
+
Section template
|
|
129
|
+
-------------------------------------------------------------------------- */
|
|
130
|
+
.bw-section {
|
|
131
|
+
padding: var(--ui-space-16) 0;
|
|
132
|
+
border-bottom: 1px solid var(--ui-border);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.bw-section-label {
|
|
136
|
+
font-size: var(--ui-text-xs);
|
|
137
|
+
letter-spacing: 0.18em;
|
|
138
|
+
text-transform: uppercase;
|
|
139
|
+
color: var(--ui-muted);
|
|
140
|
+
margin-bottom: var(--ui-space-8);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.bw-section-title {
|
|
144
|
+
font-family: var(--ui-font-display);
|
|
145
|
+
font-size: clamp(1.75rem, 3.2vw, 2.5rem);
|
|
146
|
+
font-weight: 500;
|
|
147
|
+
line-height: 1.1;
|
|
148
|
+
letter-spacing: -0.02em;
|
|
149
|
+
color: var(--ui-text);
|
|
150
|
+
margin-bottom: var(--ui-space-10);
|
|
151
|
+
max-width: 24ch;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* --------------------------------------------------------------------------
|
|
155
|
+
Staff picks grid
|
|
156
|
+
-------------------------------------------------------------------------- */
|
|
157
|
+
.bw-picks {
|
|
158
|
+
display: grid;
|
|
159
|
+
grid-template-columns: repeat(3, 1fr);
|
|
160
|
+
gap: var(--ui-space-10);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@media (max-width: 800px) {
|
|
164
|
+
.bw-picks {
|
|
165
|
+
grid-template-columns: 1fr;
|
|
166
|
+
gap: var(--ui-space-8);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.bw-pick-cover-wrap {
|
|
171
|
+
position: relative;
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
margin-bottom: var(--ui-space-6);
|
|
174
|
+
box-shadow: 0 2px 12px rgb(0 0 0 / 0.08);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.bw-pick-cover {
|
|
178
|
+
display: block;
|
|
179
|
+
width: 100%;
|
|
180
|
+
height: auto;
|
|
181
|
+
aspect-ratio: 2 / 3;
|
|
182
|
+
object-fit: cover;
|
|
183
|
+
background: var(--bw-bg-alt);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.bw-pick-overlay {
|
|
187
|
+
position: absolute;
|
|
188
|
+
inset: 0;
|
|
189
|
+
display: flex;
|
|
190
|
+
flex-direction: column;
|
|
191
|
+
justify-content: flex-end;
|
|
192
|
+
padding: var(--ui-space-6);
|
|
193
|
+
background: linear-gradient(to top, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.2) 50%, transparent 100%);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* First book only — text at top */
|
|
197
|
+
.bw-picks article:first-child .bw-pick-overlay {
|
|
198
|
+
justify-content: flex-start;
|
|
199
|
+
background: linear-gradient(to bottom, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.2) 50%, transparent 100%);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.bw-picks article:first-child .bw-pick-overlay-title {
|
|
203
|
+
font-family: Arial, sans-serif;
|
|
204
|
+
font-weight: 700;
|
|
205
|
+
text-transform: uppercase;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* Second book — solid semi-transparent block behind text */
|
|
209
|
+
.bw-picks article:nth-child(2) .bw-pick-overlay {
|
|
210
|
+
inset: auto 0 0 0;
|
|
211
|
+
background: rgba(0, 0, 0, 0.55);
|
|
212
|
+
padding: var(--ui-space-5) var(--ui-space-6);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* Third book — Arial, no italic on author */
|
|
216
|
+
.bw-picks article:nth-child(3) .bw-pick-overlay-title {
|
|
217
|
+
font-family: Arial, sans-serif;
|
|
218
|
+
font-size: 3.75rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.bw-picks article:nth-child(3) .bw-pick-overlay-author {
|
|
222
|
+
font-family: Arial, sans-serif;
|
|
223
|
+
font-style: normal;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.bw-pick-overlay-tag {
|
|
227
|
+
font-size: var(--ui-text-xs);
|
|
228
|
+
letter-spacing: 0.14em;
|
|
229
|
+
text-transform: uppercase;
|
|
230
|
+
color: rgba(255,255,255,0.7);
|
|
231
|
+
margin-bottom: var(--ui-space-2);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.bw-pick-overlay-title {
|
|
235
|
+
font-family: var(--ui-font);
|
|
236
|
+
font-size: var(--ui-text-2xl);
|
|
237
|
+
font-weight: 600;
|
|
238
|
+
line-height: 1.15;
|
|
239
|
+
color: #fff;
|
|
240
|
+
margin-bottom: var(--ui-space-1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.bw-pick-overlay-author {
|
|
244
|
+
font-style: italic;
|
|
245
|
+
font-family: var(--ui-font);
|
|
246
|
+
font-size: var(--ui-text-sm);
|
|
247
|
+
color: rgba(255,255,255,0.7);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.bw-pick-meta {
|
|
251
|
+
font-size: var(--ui-text-xs);
|
|
252
|
+
letter-spacing: 0.14em;
|
|
253
|
+
text-transform: uppercase;
|
|
254
|
+
color: var(--ui-accent);
|
|
255
|
+
margin-bottom: var(--ui-space-3);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.bw-pick-title {
|
|
259
|
+
font-family: var(--ui-font-display);
|
|
260
|
+
font-size: var(--ui-text-2xl);
|
|
261
|
+
font-weight: 500;
|
|
262
|
+
line-height: 1.2;
|
|
263
|
+
margin-bottom: var(--ui-space-2);
|
|
264
|
+
color: var(--ui-text);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.bw-pick-author {
|
|
268
|
+
font-style: italic;
|
|
269
|
+
color: var(--ui-muted);
|
|
270
|
+
margin-bottom: var(--ui-space-4);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.bw-pick-note {
|
|
274
|
+
font-size: var(--ui-text-base);
|
|
275
|
+
line-height: 1.6;
|
|
276
|
+
color: var(--ui-text);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* --------------------------------------------------------------------------
|
|
280
|
+
Pull-quote
|
|
281
|
+
-------------------------------------------------------------------------- */
|
|
282
|
+
.bw-pull {
|
|
283
|
+
padding: var(--ui-space-20) 0;
|
|
284
|
+
text-align: center;
|
|
285
|
+
border-bottom: 1px solid var(--ui-border);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.bw-pull-quote {
|
|
289
|
+
font-family: var(--ui-font-display);
|
|
290
|
+
font-size: clamp(1.75rem, 4vw, 2.75rem);
|
|
291
|
+
font-weight: 400;
|
|
292
|
+
font-style: italic;
|
|
293
|
+
line-height: 1.25;
|
|
294
|
+
letter-spacing: -0.015em;
|
|
295
|
+
color: var(--ui-text);
|
|
296
|
+
max-width: 32ch;
|
|
297
|
+
margin: 0 auto var(--ui-space-6);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.bw-pull-cite {
|
|
301
|
+
font-size: var(--ui-text-sm);
|
|
302
|
+
letter-spacing: 0.14em;
|
|
303
|
+
text-transform: uppercase;
|
|
304
|
+
color: var(--ui-muted);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* --------------------------------------------------------------------------
|
|
308
|
+
Feature grid (what we do)
|
|
309
|
+
-------------------------------------------------------------------------- */
|
|
310
|
+
.bw-features {
|
|
311
|
+
display: grid;
|
|
312
|
+
grid-template-columns: 1fr 1fr;
|
|
313
|
+
gap: var(--ui-space-12) var(--ui-space-16);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
@media (max-width: 800px) {
|
|
317
|
+
.bw-features {
|
|
318
|
+
grid-template-columns: 1fr;
|
|
319
|
+
gap: var(--ui-space-10);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.bw-feature-title {
|
|
324
|
+
font-family: var(--ui-font-display);
|
|
325
|
+
font-size: var(--ui-text-xl);
|
|
326
|
+
font-weight: 500;
|
|
327
|
+
margin-bottom: var(--ui-space-2);
|
|
328
|
+
color: var(--ui-text);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.bw-feature-body {
|
|
332
|
+
color: var(--ui-muted);
|
|
333
|
+
line-height: 1.6;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* --------------------------------------------------------------------------
|
|
337
|
+
Events list
|
|
338
|
+
-------------------------------------------------------------------------- */
|
|
339
|
+
.bw-events {
|
|
340
|
+
display: flex;
|
|
341
|
+
flex-direction: column;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.bw-event {
|
|
345
|
+
display: grid;
|
|
346
|
+
grid-template-columns: 140px 1fr auto;
|
|
347
|
+
align-items: baseline;
|
|
348
|
+
gap: var(--ui-space-8);
|
|
349
|
+
padding: var(--ui-space-6) 0;
|
|
350
|
+
border-top: 1px solid var(--ui-border);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.bw-event:last-child {
|
|
354
|
+
border-bottom: 1px solid var(--ui-border);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@media (max-width: 700px) {
|
|
358
|
+
.bw-event {
|
|
359
|
+
grid-template-columns: 1fr;
|
|
360
|
+
gap: var(--ui-space-2);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.bw-event-date {
|
|
365
|
+
font-family: var(--ui-font-display);
|
|
366
|
+
font-size: var(--ui-text-lg);
|
|
367
|
+
color: var(--ui-accent);
|
|
368
|
+
letter-spacing: 0.02em;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.bw-event-title {
|
|
372
|
+
font-family: var(--ui-font-display);
|
|
373
|
+
font-size: var(--ui-text-xl);
|
|
374
|
+
font-weight: 500;
|
|
375
|
+
color: var(--ui-text);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.bw-event-host {
|
|
379
|
+
color: var(--ui-muted);
|
|
380
|
+
font-style: italic;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* --------------------------------------------------------------------------
|
|
384
|
+
CTA section
|
|
385
|
+
-------------------------------------------------------------------------- */
|
|
386
|
+
.bw-cta {
|
|
387
|
+
padding: var(--ui-space-24) 0;
|
|
388
|
+
text-align: center;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.bw-cta-title {
|
|
392
|
+
font-family: var(--ui-font-display);
|
|
393
|
+
font-size: clamp(2rem, 5vw, 3.5rem);
|
|
394
|
+
font-weight: 500;
|
|
395
|
+
line-height: 1.05;
|
|
396
|
+
letter-spacing: -0.02em;
|
|
397
|
+
margin-bottom: var(--ui-space-6);
|
|
398
|
+
color: var(--ui-text);
|
|
399
|
+
max-width: 20ch;
|
|
400
|
+
margin-left: auto;
|
|
401
|
+
margin-right: auto;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.bw-cta-sub {
|
|
405
|
+
color: var(--ui-muted);
|
|
406
|
+
font-size: var(--ui-text-lg);
|
|
407
|
+
margin-bottom: var(--ui-space-8);
|
|
408
|
+
max-width: 50ch;
|
|
409
|
+
margin-left: auto;
|
|
410
|
+
margin-right: auto;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.bw-cta-actions {
|
|
414
|
+
display: flex;
|
|
415
|
+
gap: var(--ui-space-4);
|
|
416
|
+
justify-content: center;
|
|
417
|
+
flex-wrap: wrap;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.bw-cta-actions--start {
|
|
421
|
+
justify-content: flex-start;
|
|
422
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse — BookWorms example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates:
|
|
5
|
+
* - Light-theme editorial branding for a physical/independent business
|
|
6
|
+
* - nav() with logo text, anchor links, and a CTA action button
|
|
7
|
+
* - Pure HTML sections — no Pulse section/container wrappers (shows the
|
|
8
|
+
* framework is just strings; you can write vanilla HTML where it fits)
|
|
9
|
+
* - Book card grid (staff picks) with cover images from picsum.photos
|
|
10
|
+
* - Pull-quote / press quote breakout
|
|
11
|
+
* - Two-column feature grid
|
|
12
|
+
* - Bordered event list with date / title / host columns
|
|
13
|
+
* - Centred CTA section
|
|
14
|
+
* - footer() with flat link list
|
|
15
|
+
* - Google Fonts (Fraunces) for display headings
|
|
16
|
+
*
|
|
17
|
+
* Run: node examples/dev.server.js → http://localhost:3001/bookworms
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { nav, button, footer } from '../src/ui/index.js'
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Static data
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
const picks = [
|
|
27
|
+
{
|
|
28
|
+
tag: 'Fiction · Staff pick',
|
|
29
|
+
title: 'The Lantern Keepers',
|
|
30
|
+
author: 'Iris Aldenmoor',
|
|
31
|
+
note: 'A quiet novel about three sisters, an unreliable lighthouse, and the long winter that follows a single decision.',
|
|
32
|
+
cover: '/images/bookworms/lantern-keepers.jpg',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
tag: 'Non-fiction · This week',
|
|
36
|
+
title: 'How We Read Now',
|
|
37
|
+
author: 'Dr. Samuel Okafor',
|
|
38
|
+
note: 'A short, generous history of attention — from monastic libraries to the open browser tab.',
|
|
39
|
+
cover: '/images/bookworms/how-we-read-now.jpg',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
tag: 'Rare find · Just arrived',
|
|
43
|
+
title: 'Letters from a Provincial Bookseller',
|
|
44
|
+
author: 'M. Halloran (1927)',
|
|
45
|
+
note: 'First edition, lightly foxed. A wry account of running a shop through a quiet decade.',
|
|
46
|
+
cover: '/images/bookworms/provincial-bookseller.jpg',
|
|
47
|
+
},
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const features = [
|
|
51
|
+
{
|
|
52
|
+
title: 'Curated, not algorithmic',
|
|
53
|
+
body: 'Every recommendation comes from one of our booksellers. No bestseller lists, no engagement metrics — just books we\'ve read and would lend you ourselves.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
title: 'Browse by mood',
|
|
57
|
+
body: 'Looking for something restorative? Something difficult? Our shelves are arranged the way readers actually think.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
title: 'Author evenings & book clubs',
|
|
61
|
+
body: 'Small, considered events with writers we admire. Wine, conversation, and signed copies — no panels of four.',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
title: 'Free delivery over £25',
|
|
65
|
+
body: 'Wrapped by hand, delivered locally by bicycle. Posted further afield by the next morning.',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
title: 'Gift cards & reader subscriptions',
|
|
69
|
+
body: 'A monthly book chosen for one specific reader, with a note from the bookseller who picked it.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
title: 'Open seven days',
|
|
73
|
+
body: 'Old Market Lane, opposite the bakery. Coffee from nine, books from ten.',
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
const events = [
|
|
78
|
+
{ date: '12 June', title: 'An evening with Iris Aldenmoor', host: 'In conversation with Priya Joshi' },
|
|
79
|
+
{ date: '21 June', title: 'Slow Reading Club — Middlemarch, part one', host: 'Hosted by the BookWorms team' },
|
|
80
|
+
{ date: '03 July', title: 'New voices in non-fiction', host: 'Three debut authors read and answer questions' },
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
const navLinks = [
|
|
84
|
+
{ label: 'Browse', href: '#browse' },
|
|
85
|
+
{ label: 'Staff picks', href: '#picks' },
|
|
86
|
+
{ label: 'Events', href: '#events' },
|
|
87
|
+
{ label: 'Visit', href: '#visit' },
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Spec
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
export default {
|
|
95
|
+
route: '/bookworms',
|
|
96
|
+
|
|
97
|
+
meta: {
|
|
98
|
+
title: 'BookWorms — an independent bookstore for curious readers',
|
|
99
|
+
description: 'Handpicked fiction, non-fiction, and rare finds. Weekly staff picks, author evenings, and a slow reading club. Open seven days on Old Market Lane.',
|
|
100
|
+
theme: 'light',
|
|
101
|
+
styles: [
|
|
102
|
+
'https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,500;1,9..144,400&display=swap',
|
|
103
|
+
'/pulse-ui.css',
|
|
104
|
+
'/examples/bookworms.css',
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
view: () => `
|
|
109
|
+
${nav({
|
|
110
|
+
logo: 'BookWorms',
|
|
111
|
+
links: navLinks,
|
|
112
|
+
action: button({ label: 'Visit the shop', href: '#visit', variant: 'primary', size: 'sm' }),
|
|
113
|
+
sticky: true,
|
|
114
|
+
})}
|
|
115
|
+
|
|
116
|
+
<main id="main-content">
|
|
117
|
+
<section class="bw-hero">
|
|
118
|
+
<div class="bw-shell bw-hero__inner">
|
|
119
|
+
<div class="bw-hero__content">
|
|
120
|
+
<p class="bw-eyebrow">Independent bookstore · Old Market Lane</p>
|
|
121
|
+
<h1 class="bw-hero-title">Books, handpicked for the curious.</h1>
|
|
122
|
+
<p class="bw-hero-lede">
|
|
123
|
+
BookWorms is a small shop with a long memory. Every title on our shelves has been read by
|
|
124
|
+
someone who works here — and is waiting for the right reader to find it.
|
|
125
|
+
</p>
|
|
126
|
+
<div class="bw-cta-actions bw-cta-actions--start">
|
|
127
|
+
${button({ label: 'See this week\u2019s picks', href: '#picks', variant: 'primary' })}
|
|
128
|
+
${button({ label: 'Upcoming events', href: '#events', variant: 'secondary' })}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="bw-hero__image" aria-hidden="true">
|
|
132
|
+
<img src="/images/bookworms/hero-books.jpg" alt="" width="820" height="1080" />
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</section>
|
|
136
|
+
|
|
137
|
+
<section class="bw-section" id="picks">
|
|
138
|
+
<div class="bw-shell">
|
|
139
|
+
<p class="bw-section-label">This week</p>
|
|
140
|
+
<h2 class="bw-section-title">Three books we\u2019re pressing into people\u2019s hands.</h2>
|
|
141
|
+
<div class="bw-picks">
|
|
142
|
+
${picks.map((p, i) => `
|
|
143
|
+
<article>
|
|
144
|
+
<div class="bw-pick-cover-wrap">
|
|
145
|
+
<img class="bw-pick-cover" src="${p.cover}" alt="Cover of ${p.title} by ${p.author}" width="400" height="600" loading="lazy" />
|
|
146
|
+
<div class="bw-pick-overlay">
|
|
147
|
+
<h3 class="bw-pick-overlay-title">${p.title}</h3>
|
|
148
|
+
<p class="bw-pick-overlay-author">${i === 2 ? p.author.replace(/\s*\(\d{4}\)/, '') : 'by ' + p.author}</p>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<p class="bw-pick-meta">${p.tag}</p>
|
|
152
|
+
<h3 class="bw-pick-title">${p.title}</h3>
|
|
153
|
+
<p class="bw-pick-author">by ${p.author}</p>
|
|
154
|
+
<p class="bw-pick-note">${p.note}</p>
|
|
155
|
+
</article>
|
|
156
|
+
`).join('')}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</section>
|
|
160
|
+
|
|
161
|
+
<section class="bw-pull">
|
|
162
|
+
<div class="bw-shell">
|
|
163
|
+
<p class="bw-pull-quote">\u201cThe kind of shop where you walk in for one book and leave with three you didn\u2019t know you needed.\u201d</p>
|
|
164
|
+
<p class="bw-pull-cite">\u2014 The Quarterly Review</p>
|
|
165
|
+
</div>
|
|
166
|
+
</section>
|
|
167
|
+
|
|
168
|
+
<section class="bw-section" id="browse">
|
|
169
|
+
<div class="bw-shell">
|
|
170
|
+
<p class="bw-section-label">What we do</p>
|
|
171
|
+
<h2 class="bw-section-title">A bookstore that still reads.</h2>
|
|
172
|
+
<div class="bw-features">
|
|
173
|
+
${features.map(f => `
|
|
174
|
+
<div>
|
|
175
|
+
<h3 class="bw-feature-title">${f.title}</h3>
|
|
176
|
+
<p class="bw-feature-body">${f.body}</p>
|
|
177
|
+
</div>
|
|
178
|
+
`).join('')}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</section>
|
|
182
|
+
|
|
183
|
+
<section class="bw-section" id="events">
|
|
184
|
+
<div class="bw-shell">
|
|
185
|
+
<p class="bw-section-label">Upcoming</p>
|
|
186
|
+
<h2 class="bw-section-title">Evenings worth turning up for.</h2>
|
|
187
|
+
<div class="bw-events">
|
|
188
|
+
${events.map(e => `
|
|
189
|
+
<article class="bw-event">
|
|
190
|
+
<p class="bw-event-date">${e.date}</p>
|
|
191
|
+
<h3 class="bw-event-title">${e.title}</h3>
|
|
192
|
+
<p class="bw-event-host">${e.host}</p>
|
|
193
|
+
</article>
|
|
194
|
+
`).join('')}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</section>
|
|
198
|
+
|
|
199
|
+
<section class="bw-cta" id="visit">
|
|
200
|
+
<div class="bw-shell">
|
|
201
|
+
<h2 class="bw-cta-title">Come and browse.</h2>
|
|
202
|
+
<p class="bw-cta-sub">
|
|
203
|
+
14 Old Market Lane, opposite the bakery. Open Monday to Sunday, 10am\u20137pm.
|
|
204
|
+
Or sign up to receive one handpicked book each month.
|
|
205
|
+
</p>
|
|
206
|
+
<div class="bw-cta-actions">
|
|
207
|
+
${button({ label: 'Plan a visit', href: '#visit', variant: 'primary' })}
|
|
208
|
+
${button({ label: 'Reader subscription', href: '#visit', variant: 'secondary' })}
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</section>
|
|
212
|
+
</main>
|
|
213
|
+
|
|
214
|
+
${footer({
|
|
215
|
+
logo: 'BookWorms',
|
|
216
|
+
links: [
|
|
217
|
+
{ label: 'Browse', href: '#browse' },
|
|
218
|
+
{ label: 'Events', href: '#events' },
|
|
219
|
+
{ label: 'Visit', href: '#visit' },
|
|
220
|
+
{ label: 'Contact', href: '#visit' },
|
|
221
|
+
],
|
|
222
|
+
legal: '\u00a9 2026 BookWorms \u00b7 14 Old Market Lane',
|
|
223
|
+
})}
|
|
224
|
+
`,
|
|
225
|
+
}
|
package/examples/contact.js
CHANGED
package/examples/counter.js
CHANGED
package/examples/dev.server.js
CHANGED
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* http://localhost:3001/quiz — multi-step state machine
|
|
13
13
|
* http://localhost:3001/products — server data + search/filter/sort
|
|
14
14
|
* http://localhost:3001/pricing — landing page components + billing toggle
|
|
15
|
+
* http://localhost:3001/swish — editorial fashion branding + light theme
|
|
16
|
+
* http://localhost:3001/bookworms — independent bookshop, Fraunces font, picsum covers
|
|
17
|
+
* http://localhost:3001/retrowheels — vintage bicycle shop, slab-serif, booking form
|
|
18
|
+
* http://localhost:3001/suzukibento — artist portfolio, dark brutalist, asymmetric grid
|
|
19
|
+
* http://localhost:3001/andypixel — blog article, editorial light, newsletter action
|
|
20
|
+
* http://localhost:3001/journey — app landing page, dark split-screen, sticky timeline
|
|
15
21
|
*/
|
|
16
22
|
|
|
17
23
|
import fs from 'fs'
|
|
@@ -81,6 +87,12 @@ await createServer(
|
|
|
81
87
|
new URL('./quiz.js', import.meta.url),
|
|
82
88
|
new URL('./products.js', import.meta.url),
|
|
83
89
|
new URL('./pricing.js', import.meta.url),
|
|
90
|
+
new URL('./swish.js', import.meta.url),
|
|
91
|
+
new URL('./bookworms.js', import.meta.url),
|
|
92
|
+
new URL('./retrowheels.js', import.meta.url),
|
|
93
|
+
new URL('./suzukibento.js', import.meta.url),
|
|
94
|
+
new URL('./andypixel.js', import.meta.url),
|
|
95
|
+
new URL('./journey.js', import.meta.url),
|
|
84
96
|
],
|
|
85
97
|
{
|
|
86
98
|
port: 3001,
|
|
@@ -89,5 +101,10 @@ await createServer(
|
|
|
89
101
|
root: new URL('..', import.meta.url), // pulse2/ — specs served at /examples/*
|
|
90
102
|
onRequest: staticHandler,
|
|
91
103
|
extraBody: themeScript,
|
|
104
|
+
csp: {
|
|
105
|
+
'style-src': ['https://fonts.googleapis.com'],
|
|
106
|
+
'font-src': ['https://fonts.gstatic.com'],
|
|
107
|
+
'img-src': ['https://images.unsplash.com', 'https://picsum.photos', 'https://fastly.picsum.photos'],
|
|
108
|
+
},
|
|
92
109
|
}
|
|
93
110
|
)
|