@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,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse — Swish example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates:
|
|
5
|
+
* - Editorial light-theme branding (fashion / luxury ecommerce)
|
|
6
|
+
* - Composing nav, section, container, grid, pullquote, button, footer, input
|
|
7
|
+
* - Custom component helpers (look card, strip cell, journal card)
|
|
8
|
+
* - Google Fonts integration via meta.styles
|
|
9
|
+
* - Full-bleed hero with CSS background image + gradient overlay
|
|
10
|
+
* - Responsive grid, lookbook strip, image-led journal cards
|
|
11
|
+
* - Newsletter sign-up form using the input() component
|
|
12
|
+
*
|
|
13
|
+
* Run: node examples/dev.server.js → http://localhost:3001/swish
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
nav,
|
|
18
|
+
section,
|
|
19
|
+
container,
|
|
20
|
+
grid,
|
|
21
|
+
pullquote,
|
|
22
|
+
button,
|
|
23
|
+
footer,
|
|
24
|
+
input,
|
|
25
|
+
} from '../../../../src/ui/index.js'
|
|
26
|
+
import { asset } from '../../lib/layout.js'
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Card / cell helpers
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
const look = ({ name, season, price, src }) => `
|
|
33
|
+
<a class="swish-look" href="/collections/${encodeURIComponent(name.toLowerCase().replace(/\s+/g, '-'))}">
|
|
34
|
+
<div class="swish-look__frame">
|
|
35
|
+
<img src="${src}" alt="${name} — ${season}" loading="lazy" />
|
|
36
|
+
</div>
|
|
37
|
+
<div class="swish-look__name">${name}</div>
|
|
38
|
+
<div class="swish-look__meta">${season} · ${price}</div>
|
|
39
|
+
</a>
|
|
40
|
+
`
|
|
41
|
+
|
|
42
|
+
const stripCell = ({ src, alt, label }) => `
|
|
43
|
+
<figure class="swish-strip__cell">
|
|
44
|
+
<img src="${src}" alt="${alt}" loading="lazy" />
|
|
45
|
+
<figcaption class="swish-strip__label">${label}</figcaption>
|
|
46
|
+
</figure>
|
|
47
|
+
`
|
|
48
|
+
|
|
49
|
+
const journalCard = ({ category, title, deck, byline, src }) => `
|
|
50
|
+
<article class="swish-journal-card">
|
|
51
|
+
<div class="swish-journal-card__frame">
|
|
52
|
+
<img src="${src}" alt="" loading="lazy" />
|
|
53
|
+
</div>
|
|
54
|
+
<div class="swish-journal-card__category">${category}</div>
|
|
55
|
+
<h3 class="swish-journal-card__title">${title}</h3>
|
|
56
|
+
<p class="swish-journal-card__deck">${deck}</p>
|
|
57
|
+
<div class="swish-journal-card__byline">${byline}</div>
|
|
58
|
+
</article>
|
|
59
|
+
`
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Static data
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
const featured = [
|
|
66
|
+
{
|
|
67
|
+
name: 'The Marlene Coat',
|
|
68
|
+
season: 'AW26',
|
|
69
|
+
price: '£1,290',
|
|
70
|
+
src: '/images/swish/1539109136881-3be0616acf4b.jpg',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'Atelier Knit',
|
|
74
|
+
season: 'AW26',
|
|
75
|
+
price: '£480',
|
|
76
|
+
src: '/images/swish/1485518882345-15568b007407.jpg',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'Bias Slip Dress',
|
|
80
|
+
season: 'AW26',
|
|
81
|
+
price: '£695',
|
|
82
|
+
src: '/images/swish/1572804013309-59a88b7e92f1.jpg',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'Wide-Leg Trouser',
|
|
86
|
+
season: 'AW26',
|
|
87
|
+
price: '£420',
|
|
88
|
+
src: '/images/swish/1551803091-e20673f15770.jpg',
|
|
89
|
+
},
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
const lookbook = [
|
|
93
|
+
{
|
|
94
|
+
src: '/images/swish/1483985988355-763728e1935b.jpg',
|
|
95
|
+
alt: 'Tailored editorial look in muted neutrals',
|
|
96
|
+
label: 'Tailoring',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
src: '/images/swish/1469334031218-e382a71b716b.jpg',
|
|
100
|
+
alt: 'Soft knitwear styled with wide trousers',
|
|
101
|
+
label: 'Knitwear',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
src: '/images/swish/1502716119720-b23a93e5fe1b.jpg',
|
|
105
|
+
alt: 'Evening dress under low light',
|
|
106
|
+
label: 'Evening',
|
|
107
|
+
},
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
const journal = [
|
|
111
|
+
{
|
|
112
|
+
category: 'Atelier',
|
|
113
|
+
title: 'Inside the Florence workshop',
|
|
114
|
+
deck: 'Where every Marlene coat is cut, basted and finished by hand over twenty-three days.',
|
|
115
|
+
byline: 'Words by Inez Vasquez',
|
|
116
|
+
src: '/images/swish/1558769132-cb1aea458c5e.jpg',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
category: 'Conversation',
|
|
120
|
+
title: 'A new vocabulary for womenswear',
|
|
121
|
+
deck: 'Creative director Lila Chen on quiet power, ease, and clothes that outlive a season.',
|
|
122
|
+
byline: 'Interview by The Editors',
|
|
123
|
+
src: '/images/swish/1496747611176-843222e1e57c.jpg',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
category: 'Care',
|
|
127
|
+
title: 'On keeping wool well',
|
|
128
|
+
deck: 'A short guide to brushing, airing and storing the pieces meant to be with you for years.',
|
|
129
|
+
byline: 'Words by Marta Engel',
|
|
130
|
+
src: '/images/swish/1490481651871-ab68de25d43d.jpg',
|
|
131
|
+
},
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Spec
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
export default {
|
|
139
|
+
route: '/templates/swish',
|
|
140
|
+
|
|
141
|
+
meta: {
|
|
142
|
+
title: 'Swish — Considered womenswear, season by season',
|
|
143
|
+
description: 'Swish is a ready-to-wear house designing collection-led womenswear, handfinished in Florence and Porto. Demonstrates editorial branding with Pulse.',
|
|
144
|
+
theme: 'light',
|
|
145
|
+
styles: [
|
|
146
|
+
'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600&display=swap',
|
|
147
|
+
'/pulse-ui.css',
|
|
148
|
+
'/swish.css',
|
|
149
|
+
],
|
|
150
|
+
ogTitle: 'Swish — Autumn/Winter 2026',
|
|
151
|
+
ogImage: '/images/swish/1490481651871-ab68de25d43d.jpg',
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
view: () => `
|
|
155
|
+
<a href="#main-content" class="skip-link">Skip to main content</a>
|
|
156
|
+
|
|
157
|
+
${nav({
|
|
158
|
+
sticky: true,
|
|
159
|
+
logo: `<span class="swish-mark">Swish</span>`,
|
|
160
|
+
logoHref: '/swish',
|
|
161
|
+
links: [
|
|
162
|
+
{ label: 'Collections', href: '/collections' },
|
|
163
|
+
{ label: 'Lookbook', href: '/lookbook' },
|
|
164
|
+
{ label: 'Atelier', href: '/atelier' },
|
|
165
|
+
{ label: 'Journal', href: '/journal' },
|
|
166
|
+
{ label: 'Stores', href: '/stores' },
|
|
167
|
+
],
|
|
168
|
+
action: button({ label: 'Bag (0)', href: '/bag', variant: 'ghost', size: 'sm' }),
|
|
169
|
+
})}
|
|
170
|
+
|
|
171
|
+
<main id="main-content">
|
|
172
|
+
<section class="swish-hero" aria-label="Autumn Winter 2026 collection">
|
|
173
|
+
<div class="swish-hero__inner">
|
|
174
|
+
<div class="swish-hero__eyebrow">AW26 · Issue Nº07</div>
|
|
175
|
+
<h1 class="swish-hero__title">Quiet power, cut to last.</h1>
|
|
176
|
+
<p class="swish-hero__deck">The Autumn/Winter collection — a study in tailoring, weight and restraint. Forty-two pieces, made in small runs across Florence and Porto.</p>
|
|
177
|
+
<div class="u-flex u-gap-3 u-flex-wrap">
|
|
178
|
+
${button({ label: 'Shop the collection', href: '/collections/aw26', variant: 'primary', size: 'lg' })}
|
|
179
|
+
${button({ label: 'View lookbook', href: '/lookbook', variant: 'ghost', size: 'lg' })}
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</section>
|
|
183
|
+
|
|
184
|
+
${section({
|
|
185
|
+
padding: 'lg',
|
|
186
|
+
class: 'swish-quote-section',
|
|
187
|
+
content: container({
|
|
188
|
+
size: 'md',
|
|
189
|
+
content: pullquote({
|
|
190
|
+
quote: 'A new vocabulary for modern womenswear — disciplined, quietly luxurious, refreshingly unfussy.',
|
|
191
|
+
cite: 'British Vogue',
|
|
192
|
+
size: 'lg',
|
|
193
|
+
}),
|
|
194
|
+
}),
|
|
195
|
+
})}
|
|
196
|
+
|
|
197
|
+
${section({
|
|
198
|
+
padding: 'lg',
|
|
199
|
+
content: container({
|
|
200
|
+
content: `
|
|
201
|
+
<div class="swish-issue">Featured pieces · AW26</div>
|
|
202
|
+
${grid({
|
|
203
|
+
cols: 4,
|
|
204
|
+
gap: 'lg',
|
|
205
|
+
content: featured.map(look).join(''),
|
|
206
|
+
})}
|
|
207
|
+
`,
|
|
208
|
+
}),
|
|
209
|
+
})}
|
|
210
|
+
|
|
211
|
+
${section({
|
|
212
|
+
variant: 'alt',
|
|
213
|
+
padding: 'lg',
|
|
214
|
+
content: container({
|
|
215
|
+
content: `
|
|
216
|
+
<div class="swish-split">
|
|
217
|
+
<div class="swish-split__media">
|
|
218
|
+
<img
|
|
219
|
+
src="/images/swish/1558769132-cb1aea458c5e.jpg"
|
|
220
|
+
alt="A tailor working at the Florence atelier" />
|
|
221
|
+
</div>
|
|
222
|
+
<div class="swish-split__body">
|
|
223
|
+
<div class="swish-issue swish-issue--left">The Atelier</div>
|
|
224
|
+
<h2 class="swish-split__title">Made slowly, in two small houses.</h2>
|
|
225
|
+
<p>Tailoring is cut and finished in a converted convent in Oltrarno, Florence. Knitwear is shaped by a family-run mill outside Porto that has been spinning yarn for four generations.</p>
|
|
226
|
+
<p>Each collection is produced in runs of fewer than three hundred pieces — quiet, deliberate, and intended to be lived in for years.</p>
|
|
227
|
+
${button({ label: 'Visit the atelier', href: '/atelier', variant: 'secondary' })}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
`,
|
|
231
|
+
}),
|
|
232
|
+
})}
|
|
233
|
+
|
|
234
|
+
${section({
|
|
235
|
+
padding: 'lg',
|
|
236
|
+
content: container({
|
|
237
|
+
content: `
|
|
238
|
+
<div class="swish-issue">Lookbook</div>
|
|
239
|
+
<div class="swish-strip">${lookbook.map(stripCell).join('')}</div>
|
|
240
|
+
`,
|
|
241
|
+
}),
|
|
242
|
+
})}
|
|
243
|
+
|
|
244
|
+
${section({
|
|
245
|
+
variant: 'alt',
|
|
246
|
+
padding: 'lg',
|
|
247
|
+
content: container({
|
|
248
|
+
content: `
|
|
249
|
+
<div class="swish-issue">From the journal</div>
|
|
250
|
+
${grid({
|
|
251
|
+
cols: 3,
|
|
252
|
+
gap: 'lg',
|
|
253
|
+
content: journal.map(journalCard).join(''),
|
|
254
|
+
})}
|
|
255
|
+
`,
|
|
256
|
+
}),
|
|
257
|
+
})}
|
|
258
|
+
|
|
259
|
+
${section({
|
|
260
|
+
padding: 'lg',
|
|
261
|
+
content: container({
|
|
262
|
+
size: 'md',
|
|
263
|
+
content: `
|
|
264
|
+
<div class="swish-newsletter">
|
|
265
|
+
<h2 class="swish-newsletter__title">Letters from Swish</h2>
|
|
266
|
+
<p class="swish-newsletter__deck">Collection previews, atelier notes and private sale invitations — sent six times a year, never more.</p>
|
|
267
|
+
<form class="swish-newsletter__form" action="/subscribe" method="post" novalidate>
|
|
268
|
+
${input({ name: 'email', label: 'Email address', type: 'email', placeholder: 'your@email.com', required: true, attrs: { autocomplete: 'email' } })}
|
|
269
|
+
${button({ label: 'Subscribe', type: 'submit', variant: 'primary' })}
|
|
270
|
+
</form>
|
|
271
|
+
</div>
|
|
272
|
+
`,
|
|
273
|
+
}),
|
|
274
|
+
})}
|
|
275
|
+
</main>
|
|
276
|
+
|
|
277
|
+
${footer({
|
|
278
|
+
logo: `<span class="swish-mark">Swish</span>`,
|
|
279
|
+
logoHref: '/swish',
|
|
280
|
+
columns: [
|
|
281
|
+
{
|
|
282
|
+
title: 'Shop',
|
|
283
|
+
links: [
|
|
284
|
+
{ label: 'AW26 Collection', href: '/collections/aw26' },
|
|
285
|
+
{ label: 'Tailoring', href: '/collections/tailoring' },
|
|
286
|
+
{ label: 'Knitwear', href: '/collections/knitwear' },
|
|
287
|
+
{ label: 'Evening', href: '/collections/evening' },
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
title: 'House',
|
|
292
|
+
links: [
|
|
293
|
+
{ label: 'Atelier', href: '/atelier' },
|
|
294
|
+
{ label: 'Journal', href: '/journal' },
|
|
295
|
+
{ label: 'Stores', href: '/stores' },
|
|
296
|
+
{ label: 'Careers', href: '/careers' },
|
|
297
|
+
],
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
title: 'Help',
|
|
301
|
+
links: [
|
|
302
|
+
{ label: 'Contact', href: '/contact' },
|
|
303
|
+
{ label: 'Shipping', href: '/shipping' },
|
|
304
|
+
{ label: 'Returns', href: '/returns' },
|
|
305
|
+
{ label: 'Care', href: '/care' },
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
legal: '© 2026 Swish Maison. All rights reserved.',
|
|
310
|
+
})}
|
|
311
|
+
<script src="${asset('/pulse-ui.js')}" defer></script>
|
|
312
|
+
`,
|
|
313
|
+
}
|
|
@@ -163,7 +163,7 @@ const TEMPLATES = [
|
|
|
163
163
|
name: 'Mobile App Landing Page',
|
|
164
164
|
route: '/templates/mobile-app',
|
|
165
165
|
description: 'A complete marketing site for a mobile app — nav, hero with phone mockup, feature grid, social proof stats, testimonials, pricing cards, FAQ accordion, download CTA, and footer.',
|
|
166
|
-
tags: ['Landing Page', 'Marketing'],
|
|
166
|
+
tags: ['Landing Page', 'Marketing', 'SaaS'],
|
|
167
167
|
preview: mobileAppPreview,
|
|
168
168
|
stats: {
|
|
169
169
|
'Accessibility': '100',
|
|
@@ -187,16 +187,326 @@ const TEMPLATES = [
|
|
|
187
187
|
'JS': '~4.2 kB',
|
|
188
188
|
},
|
|
189
189
|
},
|
|
190
|
+
{
|
|
191
|
+
name: 'Sparkys — Local Trades',
|
|
192
|
+
route: '/templates/sparkys',
|
|
193
|
+
description: 'A sharp, trustworthy landing page for a local electrical contractor. Navy + amber palette with a split hero, stats panel, services grid, why-us section, testimonial, and contact strip.',
|
|
194
|
+
tags: ['Local Business', 'Trades', 'Light Theme'],
|
|
195
|
+
preview: `<div style="background:#f7f9fc;padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
196
|
+
<div style="height:8px;background:#062247;border-radius:3px;width:100%;margin-bottom:0.75rem"></div>
|
|
197
|
+
<div style="display:grid;grid-template-columns:1.2fr 1fr;gap:0.75rem;min-height:80px">
|
|
198
|
+
<div style="display:flex;flex-direction:column;gap:5px;justify-content:center">
|
|
199
|
+
<div style="height:5px;background:#0a3160;border-radius:3px;width:50%"></div>
|
|
200
|
+
<div style="height:10px;background:rgba(15,23,42,0.8);border-radius:3px;width:90%"></div>
|
|
201
|
+
<div style="height:10px;background:rgba(15,23,42,0.8);border-radius:3px;width:75%"></div>
|
|
202
|
+
<div style="height:6px;background:rgba(15,23,42,0.3);border-radius:3px;width:80%;margin-bottom:4px"></div>
|
|
203
|
+
<div style="display:flex;gap:5px">
|
|
204
|
+
<div style="height:18px;background:#0a3160;border-radius:4px;width:60px"></div>
|
|
205
|
+
<div style="height:18px;background:rgba(10,49,96,0.15);border:1px solid rgba(10,49,96,0.3);border-radius:4px;width:50px"></div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
<div style="background:linear-gradient(160deg,#0a3160,#062247);border-radius:6px;padding:0.6rem;display:grid;grid-template-columns:1fr 1fr;gap:5px">
|
|
209
|
+
${[1,2,3,4].map(() => `<div style="display:flex;flex-direction:column;gap:2px">
|
|
210
|
+
<div style="height:9px;background:rgba(255,255,255,0.7);border-radius:2px;width:70%"></div>
|
|
211
|
+
<div style="height:5px;background:rgba(244,167,33,0.5);border-radius:2px;width:90%"></div>
|
|
212
|
+
</div>`).join('')}
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:5px;margin-top:0.75rem">
|
|
216
|
+
${[1,2,3].map(() => `<div style="background:#fff;border:1px solid #d6deea;border-radius:5px;padding:0.4rem;display:flex;flex-direction:column;gap:3px">
|
|
217
|
+
<div style="width:18px;height:18px;background:#e3ecf7;border-radius:3px"></div>
|
|
218
|
+
<div style="height:5px;background:rgba(15,23,42,0.6);border-radius:2px;width:80%"></div>
|
|
219
|
+
<div style="height:4px;background:rgba(15,23,42,0.25);border-radius:2px;width:100%"></div>
|
|
220
|
+
<div style="height:4px;background:rgba(15,23,42,0.2);border-radius:2px;width:70%"></div>
|
|
221
|
+
</div>`).join('')}
|
|
222
|
+
</div>
|
|
223
|
+
</div>`,
|
|
224
|
+
stats: {
|
|
225
|
+
'Accessibility': '100',
|
|
226
|
+
'Best Practices': '100',
|
|
227
|
+
'SEO': '100',
|
|
228
|
+
'CLS': '0.00',
|
|
229
|
+
'Theme': 'light',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'Local Business',
|
|
234
|
+
route: '/templates/local-business',
|
|
235
|
+
description: 'A warm, photo-forward landing page for a local service business — dog groomer, bakery, physio, tradesperson. Services grid, about section, testimonials, hours, and booking contact form.',
|
|
236
|
+
tags: ['Local Business', 'Services', 'Light Theme'],
|
|
237
|
+
preview: `<div style="background:linear-gradient(135deg,#fdf6ee,#fdebd0);padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
238
|
+
<div style="height:10px;background:#e8956d;border-radius:6px;width:60%;margin-bottom:0.75rem"></div>
|
|
239
|
+
<div style="height:22px;background:rgba(61,43,30,0.85);border-radius:4px;width:85%;margin-bottom:0.5rem"></div>
|
|
240
|
+
<div style="height:10px;background:rgba(61,43,30,0.35);border-radius:4px;width:75%;margin-bottom:1rem"></div>
|
|
241
|
+
<div style="display:flex;gap:0.5rem;margin-bottom:1.25rem">
|
|
242
|
+
<div style="height:30px;background:#c4632a;border-radius:14px;width:100px;flex-shrink:0"></div>
|
|
243
|
+
<div style="height:30px;background:rgba(61,43,30,0.15);border:1px solid rgba(61,43,30,0.2);border-radius:14px;width:90px;flex-shrink:0"></div>
|
|
244
|
+
</div>
|
|
245
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.5rem">
|
|
246
|
+
${[1,2,3].map(() => `<div style="background:#fff;border-radius:10px;padding:0.5rem">
|
|
247
|
+
<div style="height:8px;background:#e8956d;border-radius:4px;width:70%;margin-bottom:0.35rem"></div>
|
|
248
|
+
<div style="height:6px;background:rgba(0,0,0,0.12);border-radius:3px;width:90%"></div>
|
|
249
|
+
<div style="height:6px;background:rgba(0,0,0,0.08);border-radius:3px;width:70%;margin-top:0.25rem"></div>
|
|
250
|
+
</div>`).join('')}
|
|
251
|
+
</div>
|
|
252
|
+
</div>`,
|
|
253
|
+
stats: {
|
|
254
|
+
'Vibe': 'warm',
|
|
255
|
+
'Theme': 'light',
|
|
256
|
+
'CLS': '0.00',
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: 'Portfolio',
|
|
261
|
+
route: '/templates/portfolio',
|
|
262
|
+
description: 'A minimal dark portfolio for a creative professional — designer, developer, photographer, or architect. Work grid, about section with skill badges, and a clean contact panel.',
|
|
263
|
+
tags: ['Portfolio', 'Personal', 'Minimal'],
|
|
264
|
+
preview: `<div style="background:#0d0d10;padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
265
|
+
<div style="height:12px;background:rgba(255,255,255,0.7);border-radius:3px;width:55%;margin-bottom:0.75rem;font-size:0;"></div>
|
|
266
|
+
<div style="height:20px;background:rgba(255,255,255,0.85);border-radius:3px;width:80%;margin-bottom:0.5rem"></div>
|
|
267
|
+
<div style="height:8px;background:rgba(255,255,255,0.25);border-radius:3px;width:65%;margin-bottom:1.25rem"></div>
|
|
268
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.5rem">
|
|
269
|
+
${[1,2,3].map(i => `<div style="background:rgba(255,255,255,0.05);border-radius:6px;overflow:hidden">
|
|
270
|
+
<div style="height:48px;background:rgba(255,255,255,0.08);"></div>
|
|
271
|
+
<div style="padding:0.4rem">
|
|
272
|
+
<div style="height:7px;background:rgba(255,255,255,0.4);border-radius:3px;width:80%;margin-bottom:0.3rem"></div>
|
|
273
|
+
<div style="height:6px;background:rgba(255,255,255,0.2);border-radius:3px;width:60%"></div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>`).join('')}
|
|
276
|
+
</div>
|
|
277
|
+
</div>`,
|
|
278
|
+
stats: {
|
|
279
|
+
'Vibe': 'minimal',
|
|
280
|
+
'Theme': 'dark',
|
|
281
|
+
'CLS': '0.00',
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'Event / Conference',
|
|
286
|
+
route: '/templates/event',
|
|
287
|
+
description: 'A high-energy landing page for a conference, festival, or summit — speaker grid, day-by-day schedule accordion, venue section, sponsor logo marquee, and ticket CTA.',
|
|
288
|
+
tags: ['Event', 'Conference', 'Bold'],
|
|
289
|
+
preview: `<div style="background:linear-gradient(180deg,#05050a,#0d0829);padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
290
|
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1.25rem">
|
|
291
|
+
<div style="height:10px;background:rgba(255,255,255,0.8);border-radius:3px;width:60px"></div>
|
|
292
|
+
<div style="height:24px;background:#6366f1;border-radius:6px;width:80px"></div>
|
|
293
|
+
</div>
|
|
294
|
+
<div style="height:8px;background:rgba(99,102,241,0.6);border-radius:4px;width:50%;margin-bottom:0.5rem"></div>
|
|
295
|
+
<div style="height:20px;background:rgba(255,255,255,0.9);border-radius:4px;width:85%;margin-bottom:0.75rem"></div>
|
|
296
|
+
<div style="height:8px;background:rgba(255,255,255,0.3);border-radius:4px;width:70%;margin-bottom:1.25rem"></div>
|
|
297
|
+
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.4rem">
|
|
298
|
+
${[1,2,3,4].map(() => `<div style="background:rgba(255,255,255,0.06);border-radius:6px;padding:0.4rem;text-align:center">
|
|
299
|
+
<div style="height:14px;background:rgba(99,102,241,0.8);border-radius:3px;width:70%;margin:0 auto 0.25rem"></div>
|
|
300
|
+
<div style="height:6px;background:rgba(255,255,255,0.2);border-radius:3px;width:80%;margin:0 auto"></div>
|
|
301
|
+
</div>`).join('')}
|
|
302
|
+
</div>
|
|
303
|
+
</div>`,
|
|
304
|
+
stats: {
|
|
305
|
+
'Vibe': 'bold',
|
|
306
|
+
'Theme': 'dark',
|
|
307
|
+
'CLS': '0.00',
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: 'Editorial / Magazine',
|
|
312
|
+
route: '/templates/editorial',
|
|
313
|
+
description: 'A publication homepage — featured article hero, pullquote, article grid with category badges, newsletter signup, and footer. Serif typography throughout via the editorial vibe.',
|
|
314
|
+
tags: ['Editorial', 'Publication', 'Blog'],
|
|
315
|
+
preview: `<div style="background:#0a0a0c;padding:1.5rem;border-radius:8px;font-family:Georgia,serif">
|
|
316
|
+
<div style="height:8px;background:rgba(180,100,60,0.7);border-radius:2px;width:40%;margin-bottom:0.75rem"></div>
|
|
317
|
+
<div style="height:20px;background:rgba(255,255,255,0.9);border-radius:2px;width:90%;margin-bottom:0.35rem"></div>
|
|
318
|
+
<div style="height:20px;background:rgba(255,255,255,0.75);border-radius:2px;width:70%;margin-bottom:0.75rem"></div>
|
|
319
|
+
<div style="height:8px;background:rgba(255,255,255,0.3);border-radius:2px;width:80%;margin-bottom:1.25rem"></div>
|
|
320
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.5rem">
|
|
321
|
+
${[1,2,3].map(() => `<div style="background:rgba(255,255,255,0.04);border-radius:2px;overflow:hidden">
|
|
322
|
+
<div style="height:44px;background:rgba(255,255,255,0.08);"></div>
|
|
323
|
+
<div style="padding:0.4rem">
|
|
324
|
+
<div style="height:6px;background:rgba(180,100,60,0.6);border-radius:2px;width:40%;margin-bottom:0.3rem"></div>
|
|
325
|
+
<div style="height:8px;background:rgba(255,255,255,0.5);border-radius:2px;width:90%;margin-bottom:0.25rem"></div>
|
|
326
|
+
<div style="height:8px;background:rgba(255,255,255,0.3);border-radius:2px;width:70%"></div>
|
|
327
|
+
</div>
|
|
328
|
+
</div>`).join('')}
|
|
329
|
+
</div>
|
|
330
|
+
</div>`,
|
|
331
|
+
stats: {
|
|
332
|
+
'Vibe': 'editorial',
|
|
333
|
+
'Theme': 'dark',
|
|
334
|
+
'CLS': '0.00',
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
name: 'Swish — Fashion Ecommerce',
|
|
339
|
+
route: '/templates/swish',
|
|
340
|
+
description: 'An editorial light-theme fashion brand — full-bleed hero with gradient overlay, lookbook grid, product strip, journal cards, and newsletter. Composites nav, section, grid, pullquote, and input.',
|
|
341
|
+
tags: ['Ecommerce', 'Fashion', 'Editorial', 'Light Theme'],
|
|
342
|
+
preview: `<div style="background:#faf9f7;padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
343
|
+
<div style="height:80px;background:linear-gradient(160deg,#2a2118,#5c4a3a);border-radius:6px;margin-bottom:0.75rem;display:flex;align-items:flex-end;padding:0.5rem">
|
|
344
|
+
<div>
|
|
345
|
+
<div style="height:6px;background:rgba(255,255,255,0.5);border-radius:2px;width:60px;margin-bottom:4px"></div>
|
|
346
|
+
<div style="height:12px;background:#fff;border-radius:2px;width:120px;margin-bottom:6px"></div>
|
|
347
|
+
<div style="height:20px;background:rgba(255,255,255,0.9);border-radius:14px;width:80px"></div>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.4rem">
|
|
351
|
+
${[1,2,3].map(() => `<div style="background:#ede9e3;border-radius:4px;overflow:hidden">
|
|
352
|
+
<div style="height:44px;background:rgba(90,70,50,0.15);"></div>
|
|
353
|
+
<div style="padding:0.3rem">
|
|
354
|
+
<div style="height:6px;background:rgba(0,0,0,0.4);border-radius:2px;width:80%;margin-bottom:3px"></div>
|
|
355
|
+
<div style="height:5px;background:rgba(0,0,0,0.2);border-radius:2px;width:50%"></div>
|
|
356
|
+
</div>
|
|
357
|
+
</div>`).join('')}
|
|
358
|
+
</div>
|
|
359
|
+
</div>`,
|
|
360
|
+
stats: {
|
|
361
|
+
'Vibe': 'editorial',
|
|
362
|
+
'Theme': 'light',
|
|
363
|
+
'CLS': '0.00',
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: 'BookWorms — Independent Bookshop',
|
|
368
|
+
route: '/templates/bookworms',
|
|
369
|
+
description: 'A warm editorial site for an independent bookshop — staff picks grid, press pullquote, two-column features, event list, and a centred CTA. Fraunces serif headings, light cream palette.',
|
|
370
|
+
tags: ['Local Business', 'Retail', 'Editorial', 'Light Theme'],
|
|
371
|
+
preview: `<div style="background:#fdf8f2;padding:1.5rem;border-radius:8px;font-family:Georgia,serif">
|
|
372
|
+
<div style="height:8px;background:#8b4513;border-radius:2px;width:30%;margin-bottom:0.75rem"></div>
|
|
373
|
+
<div style="height:18px;background:rgba(60,30,10,0.85);border-radius:2px;width:85%;margin-bottom:0.5rem"></div>
|
|
374
|
+
<div style="height:8px;background:rgba(60,30,10,0.3);border-radius:2px;width:70%;margin-bottom:1rem"></div>
|
|
375
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:0.4rem">
|
|
376
|
+
${[1,2,3,4].map(() => `<div style="background:#f0e8da;border-radius:3px;overflow:hidden">
|
|
377
|
+
<div style="height:50px;background:rgba(139,69,19,0.2);"></div>
|
|
378
|
+
<div style="padding:0.3rem">
|
|
379
|
+
<div style="height:5px;background:rgba(60,30,10,0.5);border-radius:2px;width:90%;margin-bottom:3px"></div>
|
|
380
|
+
<div style="height:4px;background:rgba(60,30,10,0.2);border-radius:2px;width:70%"></div>
|
|
381
|
+
</div>
|
|
382
|
+
</div>`).join('')}
|
|
383
|
+
</div>
|
|
384
|
+
</div>`,
|
|
385
|
+
stats: {
|
|
386
|
+
'Vibe': 'paper',
|
|
387
|
+
'Theme': 'light',
|
|
388
|
+
'CLS': '0.00',
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: 'Retro Wheels Co. — Classic Cars',
|
|
393
|
+
route: '/templates/retrowheels',
|
|
394
|
+
description: 'A vintage 1970s enamel-sign aesthetic for a classic car dealer. Stat strip, testimonials, async booking enquiry form with form state, and Rockwell system fonts. Zero extra HTTP requests.',
|
|
395
|
+
tags: ['Retro', 'Automotive', 'Form', 'Light Theme'],
|
|
396
|
+
preview: `<div style="background:#f5f0e8;padding:1.5rem;border-radius:8px;font-family:'Courier New',monospace">
|
|
397
|
+
<div style="height:70px;background:linear-gradient(160deg,#1a1a2e,#2d2d4e);border-radius:4px;margin-bottom:0.75rem;display:flex;align-items:center;justify-content:center">
|
|
398
|
+
<div style="height:14px;background:rgba(205,160,60,0.9);border-radius:2px;width:100px"></div>
|
|
399
|
+
</div>
|
|
400
|
+
<div style="background:#1a1a2e;border-radius:4px;padding:0.5rem;display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.4rem;margin-bottom:0.75rem">
|
|
401
|
+
${[1,2,3].map(() => `<div style="text-align:center">
|
|
402
|
+
<div style="height:10px;background:rgba(205,160,60,0.8);border-radius:2px;width:60%;margin:0 auto 3px"></div>
|
|
403
|
+
<div style="height:6px;background:rgba(255,255,255,0.3);border-radius:2px;width:80%;margin:0 auto"></div>
|
|
404
|
+
</div>`).join('')}
|
|
405
|
+
</div>
|
|
406
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.4rem">
|
|
407
|
+
${[1,2].map(() => `<div style="background:#fff;border:2px solid #1a1a2e;border-radius:3px;padding:0.4rem">
|
|
408
|
+
<div style="height:7px;background:rgba(26,26,46,0.7);border-radius:2px;width:90%;margin-bottom:4px"></div>
|
|
409
|
+
<div style="height:5px;background:rgba(26,26,46,0.3);border-radius:2px;width:70%"></div>
|
|
410
|
+
</div>`).join('')}
|
|
411
|
+
</div>
|
|
412
|
+
</div>`,
|
|
413
|
+
stats: {
|
|
414
|
+
'Vibe': 'retro',
|
|
415
|
+
'Theme': 'light',
|
|
416
|
+
'CLS': '0.00',
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: 'Susuki Bento — Artist Portfolio',
|
|
421
|
+
route: '/templates/suzukibento',
|
|
422
|
+
description: 'A dark brutalist gallery for a fine-art painter. Zero border-radius, asymmetric 12-column works grid, exhibition table, and press list. Pure HTML — no Pulse UI components. Mode B creative override.',
|
|
423
|
+
tags: ['Portfolio', 'Art', 'Brutalist', 'Dark Theme'],
|
|
424
|
+
preview: `<div style="background:#0e0e0e;padding:1.5rem;border-radius:0;font-family:system-ui,sans-serif;border:1px solid #222">
|
|
425
|
+
<div style="height:10px;background:#f2efe8;width:80%;margin-bottom:0.75rem"></div>
|
|
426
|
+
<div style="height:6px;background:#cc3300;width:40%;margin-bottom:1rem"></div>
|
|
427
|
+
<div style="display:grid;grid-template-columns:1.5fr 1fr 1fr;grid-template-rows:60px 40px;gap:3px">
|
|
428
|
+
<div style="background:rgba(242,239,232,0.1);grid-row:1/3"></div>
|
|
429
|
+
<div style="background:rgba(242,239,232,0.08)"></div>
|
|
430
|
+
<div style="background:rgba(204,51,0,0.15)"></div>
|
|
431
|
+
<div style="background:rgba(242,239,232,0.06);grid-column:2/4"></div>
|
|
432
|
+
</div>
|
|
433
|
+
<div style="margin-top:0.75rem;display:flex;gap:0.4rem">
|
|
434
|
+
${[1,2,3].map(() => `<div style="height:4px;background:rgba(242,239,232,0.2);border-radius:0;flex:1"></div>`).join('')}
|
|
435
|
+
</div>
|
|
436
|
+
</div>`,
|
|
437
|
+
stats: {
|
|
438
|
+
'Vibe': 'brutalist',
|
|
439
|
+
'Theme': 'dark',
|
|
440
|
+
'CLS': '0.00',
|
|
441
|
+
'Mode': 'B — raw HTML',
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: 'Andy Pixel — Dev Blog',
|
|
446
|
+
route: '/templates/andypixel',
|
|
447
|
+
description: 'An editorial blog article page — prose-width reading column, badge, avatar, custom code block, blockquote, author bio, and async newsletter subscribe. Orange accent on light cream.',
|
|
448
|
+
tags: ['Blog', 'Editorial', 'Article', 'Light Theme'],
|
|
449
|
+
preview: `<div style="background:#fdfaf6;padding:1.5rem;border-radius:8px;font-family:Georgia,serif">
|
|
450
|
+
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.75rem">
|
|
451
|
+
<div style="width:24px;height:24px;background:#e65c00;border-radius:50%;flex-shrink:0"></div>
|
|
452
|
+
<div style="height:6px;background:rgba(0,0,0,0.3);border-radius:3px;width:80px"></div>
|
|
453
|
+
<div style="height:14px;background:#e65c00;border-radius:10px;width:40px;margin-left:auto"></div>
|
|
454
|
+
</div>
|
|
455
|
+
<div style="height:18px;background:rgba(0,0,0,0.8);border-radius:2px;width:90%;margin-bottom:0.5rem"></div>
|
|
456
|
+
<div style="height:18px;background:rgba(0,0,0,0.6);border-radius:2px;width:70%;margin-bottom:0.75rem"></div>
|
|
457
|
+
<div style="background:#1a1a1a;border-radius:6px;padding:0.5rem;margin-bottom:0.75rem">
|
|
458
|
+
<div style="height:5px;background:rgba(230,92,0,0.5);border-radius:2px;width:50%;margin-bottom:5px"></div>
|
|
459
|
+
${[1,2,3].map(() => `<div style="height:4px;background:rgba(255,255,255,0.3);border-radius:2px;width:${70+Math.random()*20|0}%;margin-bottom:3px"></div>`).join('')}
|
|
460
|
+
</div>
|
|
461
|
+
<div style="height:6px;background:rgba(0,0,0,0.2);border-radius:2px;width:85%"></div>
|
|
462
|
+
</div>`,
|
|
463
|
+
stats: {
|
|
464
|
+
'Vibe': 'editorial',
|
|
465
|
+
'Theme': 'light',
|
|
466
|
+
'CLS': '0.00',
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
name: 'Journey — App Landing Page',
|
|
471
|
+
route: '/templates/journey',
|
|
472
|
+
description: 'A dark split-screen app landing page — sticky hero left, scrollable timeline right, frosted glass nav, gold accent, App Store badge. Pure HTML throughout. Mode B creative override.',
|
|
473
|
+
tags: ['Landing Page', 'App', 'Dark Theme', 'Split-screen'],
|
|
474
|
+
preview: `<div style="background:#0a0a0f;padding:0;border-radius:8px;overflow:hidden;display:grid;grid-template-columns:1fr 1fr;min-height:130px">
|
|
475
|
+
<div style="padding:1rem;display:flex;flex-direction:column;justify-content:center;gap:0.4rem;border-right:1px solid rgba(201,169,110,0.2)">
|
|
476
|
+
<div style="height:5px;background:rgba(201,169,110,0.5);border-radius:2px;width:50%;margin-bottom:2px"></div>
|
|
477
|
+
<div style="height:14px;background:rgba(240,237,232,0.9);border-radius:2px;width:90%"></div>
|
|
478
|
+
<div style="height:14px;background:rgba(240,237,232,0.6);border-radius:2px;width:70%"></div>
|
|
479
|
+
<div style="height:8px;background:rgba(240,237,232,0.25);border-radius:2px;width:80%;margin-top:2px"></div>
|
|
480
|
+
<div style="height:20px;background:rgba(201,169,110,0.8);border-radius:4px;width:80px;margin-top:4px"></div>
|
|
481
|
+
</div>
|
|
482
|
+
<div style="padding:0.75rem;display:flex;flex-direction:column;gap:0.4rem">
|
|
483
|
+
${[1,2,3].map(() => `<div style="background:rgba(255,255,255,0.04);border-radius:4px;padding:0.35rem;border-left:2px solid rgba(201,169,110,0.4)">
|
|
484
|
+
<div style="height:5px;background:rgba(201,169,110,0.5);border-radius:2px;width:40%;margin-bottom:3px"></div>
|
|
485
|
+
<div style="height:4px;background:rgba(240,237,232,0.2);border-radius:2px;width:85%"></div>
|
|
486
|
+
</div>`).join('')}
|
|
487
|
+
</div>
|
|
488
|
+
</div>`,
|
|
489
|
+
stats: {
|
|
490
|
+
'Vibe': 'bold',
|
|
491
|
+
'Theme': 'dark',
|
|
492
|
+
'CLS': '0.00',
|
|
493
|
+
'Mode': 'B — raw HTML',
|
|
494
|
+
},
|
|
495
|
+
},
|
|
190
496
|
]
|
|
191
497
|
|
|
192
498
|
const content = `
|
|
193
499
|
${h1('Templates')}
|
|
194
|
-
${lead('Production-ready Pulse templates
|
|
500
|
+
${lead('Production-ready Pulse templates — from local business to editorial to app landing pages. Every template ships with 100/100/100 Lighthouse scores and zero CLS.')}
|
|
195
501
|
|
|
196
502
|
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1.5rem;margin-top:2rem">
|
|
197
503
|
${TEMPLATES.map(templateCard).join('')}
|
|
198
504
|
</div>
|
|
199
505
|
|
|
506
|
+
${section('design-directions', 'Design directions')}
|
|
507
|
+
<p>Templates give you the structure. The <a href="/meta#vibe">meta.vibe</a> system gives you the personality. Combine a template with a vibe to get a page that feels right for the product — not just correct.</p>
|
|
508
|
+
<p style="margin-top:0.75rem">When building a new site, ask: <em>what kind of business is this?</em> A dog groomer and a SaaS dashboard have the same sections but completely different aesthetics. Use the design directions guide (<code>pulse://guide/design-references</code>) to make that decision consciously.</p>
|
|
509
|
+
|
|
200
510
|
${section('adding', 'Suggesting a template')}
|
|
201
511
|
<p>Have an idea for a template? <a href="https://github.com/invisibleloop/pulse-framework/issues" target="_blank" rel="noopener noreferrer">Open an issue on GitHub</a> with the layout you have in mind and we'll consider adding it to the library.</p>
|
|
202
512
|
`
|
|
@@ -207,6 +517,7 @@ export default {
|
|
|
207
517
|
meta: {
|
|
208
518
|
title: 'Templates — Pulse',
|
|
209
519
|
description: 'Production-ready Pulse templates. Every template uses only Pulse UI components and ships with 100/100/100 Lighthouse scores.',
|
|
520
|
+
theme: 'dark',
|
|
210
521
|
styles: ['/pulse-ui.css', '/docs.css'],
|
|
211
522
|
},
|
|
212
523
|
|