@invisibleloop/pulse 0.20.11 → 0.20.20
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/package.json +6 -5
- package/public/.pulse-ui-version +1 -1
- package/scripts/build.js +26 -0
- package/scripts/release-version.js +17 -8
- package/scripts/strip-server.test.js +60 -0
- package/src/agent/checklist.md +8 -12
- package/src/agent/commands/pulse-start.md +2 -0
- package/src/agent/commands/verify.md +30 -8
- package/src/agent/guide-components.md +7 -25
- package/src/agent/guide-routing.md +0 -12
- package/src/agent/guide-server.md +102 -1
- package/src/agent/guide-spec.md +8 -1
- package/src/agent/guide-styles.md +23 -0
- package/src/agent/identity.md +9 -0
- package/src/agent/workflow.md +4 -23
- package/src/cli/check-bundles.js +113 -0
- package/src/cli/check-bundles.test.js +137 -0
- package/src/cli/cli.test.js +105 -0
- package/src/cli/dev.js +48 -3
- package/src/cli/diagnose.js +105 -0
- package/src/cli/diagnose.test.js +124 -0
- package/src/cli/discover.js +9 -2
- package/src/cli/index.js +171 -9
- package/src/cli/process-utils.js +57 -0
- package/src/cli/process-utils.test.js +114 -0
- package/src/cli/review.js +391 -0
- package/src/cli/review.test.js +143 -0
- package/src/cli/scaffold.js +9 -4
- package/src/cli/stamp.js +102 -0
- package/src/cli/stamp.test.js +149 -0
- package/src/cli/start.js +15 -2
- package/src/cli/validate.js +184 -0
- package/src/cli/validate.test.js +153 -0
- package/src/mcp/mcp.test.js +261 -30
- package/src/mcp/server.js +207 -515
- package/src/mcp/validate-worker.js +18 -1
- package/src/runtime/index.js +144 -11
- package/src/runtime/runtime.test.js +199 -0
- package/src/runtime/ssr.js +13 -6
- package/src/runtime/ssr.test.js +31 -0
- package/src/server/index.js +162 -9
- package/src/server/server.test.js +225 -2
- package/src/spec/schema.js +1 -1
- package/src/ui/index.js +0 -5
- package/src/ui/ui.test.js +0 -75
- package/types/ui.d.ts +0 -21
- package/.claude/commands/build-page.md +0 -63
- package/.claude/commands/new-doc-page.md +0 -45
- package/.claude/commands/pulse-start.md +0 -11
- package/.claude/commands/pulse-stop.md +0 -5
- package/.claude/commands/verify.md +0 -118
- package/.claude/pulse-checklist.md +0 -189
- package/.github/workflows/ci.yml +0 -25
- package/.github/workflows/publish.yml +0 -45
- package/.pulse/load-reports/home/1773432711417.json +0 -22
- package/.pulse-verified +0 -1
- package/AGENTS.md +0 -697
- package/CLAUDE.md +0 -697
- package/README.md +0 -95
- package/benchmark/public/dist/counter.boot-PO6SNSJZ.js +0 -8
- package/benchmark/public/dist/home.boot-YK6VAEQI.js +0 -6
- package/benchmark/public/dist/manifest.json +0 -5
- package/benchmark/public/dist/runtime-5Y7ESAIB.js +0 -2
- package/benchmark/public/dist/runtime-KO4BHUQ3.js +0 -49
- package/benchmark/results/2026-03-27-09-20.json +0 -188
- package/benchmark/results/2026-03-27-09-21.json +0 -188
- package/benchmark/results/2026-03-27-09-29.json +0 -188
- package/benchmark/results/2026-03-27-10-14.json +0 -188
- package/benchmark/results/2026-03-27-10-18.json +0 -232
- package/benchmark/src/pages/counter.js +0 -37
- package/benchmark/src/pages/home.js +0 -21
- package/docs/.claude/commands/pulse-build.md +0 -7
- package/docs/.claude/commands/pulse-dev.md +0 -7
- package/docs/.claude/commands/pulse-start.md +0 -9
- package/docs/.claude/commands/pulse-stop.md +0 -5
- package/docs/.claude/commands/verify.md +0 -84
- package/docs/.claude/coverage-check.js +0 -196
- package/docs/.claude/pulse-checklist.md +0 -189
- package/docs/public/.pulse-ui-version +0 -1
- package/docs/public/andypixel.css +0 -237
- package/docs/public/bookworms.css +0 -422
- package/docs/public/coutumes.css +0 -561
- package/docs/public/docs.css +0 -2388
- package/docs/public/favicon.svg +0 -4
- package/docs/public/home-brut.css +0 -982
- 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/img/heic1501a.jpg +0 -0
- package/docs/public/img/placeholder-landscape.svg +0 -18
- package/docs/public/img/placeholder-square.svg +0 -14
- package/docs/public/img/placeholder-tall.svg +0 -22
- package/docs/public/journey.css +0 -528
- package/docs/public/menu.js +0 -327
- package/docs/public/pulse-ui.css +0 -3028
- package/docs/public/pulse-ui.js +0 -257
- package/docs/public/retrowheels.css +0 -425
- package/docs/public/shop.css +0 -269
- package/docs/public/sparkys.css +0 -382
- package/docs/public/suzukibento.css +0 -517
- package/docs/public/swish.css +0 -394
- package/docs/public/theme.css +0 -52
- package/docs/public/themes/lumio.css +0 -50
- package/docs/public/themes/observatory.css +0 -104
- package/docs/server.js +0 -161
- package/docs/src/lib/component-page.js +0 -49
- package/docs/src/lib/highlight.js +0 -2
- package/docs/src/lib/layout.js +0 -190
- package/docs/src/lib/metrics-store.js +0 -6
- package/docs/src/lib/nav.js +0 -171
- package/docs/src/lib/stats.js +0 -81
- package/docs/src/pages/accessibility.js +0 -157
- package/docs/src/pages/actions.js +0 -194
- package/docs/src/pages/auth.js +0 -176
- package/docs/src/pages/caching.js +0 -95
- package/docs/src/pages/components/accordion.js +0 -48
- package/docs/src/pages/components/alert.js +0 -35
- package/docs/src/pages/components/app-badge.js +0 -41
- package/docs/src/pages/components/avatar.js +0 -35
- package/docs/src/pages/components/badge.js +0 -36
- package/docs/src/pages/components/breadcrumbs.js +0 -94
- package/docs/src/pages/components/button.js +0 -84
- package/docs/src/pages/components/card.js +0 -225
- package/docs/src/pages/components/charts.js +0 -283
- package/docs/src/pages/components/checkbox.js +0 -129
- package/docs/src/pages/components/cluster.js +0 -47
- package/docs/src/pages/components/container.js +0 -40
- package/docs/src/pages/components/cta.js +0 -54
- package/docs/src/pages/components/divider.js +0 -37
- package/docs/src/pages/components/empty.js +0 -36
- package/docs/src/pages/components/feature.js +0 -60
- package/docs/src/pages/components/fieldset.js +0 -65
- package/docs/src/pages/components/fileupload.js +0 -128
- package/docs/src/pages/components/footer.js +0 -58
- package/docs/src/pages/components/grid.js +0 -169
- package/docs/src/pages/components/heading.js +0 -107
- package/docs/src/pages/components/hero.js +0 -123
- package/docs/src/pages/components/icons.js +0 -410
- package/docs/src/pages/components/image.js +0 -71
- package/docs/src/pages/components/input.js +0 -51
- package/docs/src/pages/components/media.js +0 -51
- package/docs/src/pages/components/modal.js +0 -111
- package/docs/src/pages/components/nav.js +0 -136
- package/docs/src/pages/components/phone-frame.js +0 -74
- package/docs/src/pages/components/progress.js +0 -102
- package/docs/src/pages/components/prose.js +0 -111
- package/docs/src/pages/components/pullquote.js +0 -71
- package/docs/src/pages/components/radio.js +0 -194
- package/docs/src/pages/components/rating.js +0 -106
- package/docs/src/pages/components/search.js +0 -61
- package/docs/src/pages/components/section.js +0 -98
- package/docs/src/pages/components/segmented.js +0 -121
- package/docs/src/pages/components/select.js +0 -45
- package/docs/src/pages/components/slider.js +0 -115
- package/docs/src/pages/components/spinner.js +0 -73
- package/docs/src/pages/components/stack.js +0 -48
- package/docs/src/pages/components/stat.js +0 -55
- package/docs/src/pages/components/stepper.js +0 -66
- package/docs/src/pages/components/table.js +0 -45
- package/docs/src/pages/components/textarea.js +0 -31
- package/docs/src/pages/components/toggle.js +0 -84
- package/docs/src/pages/components/tooltip.js +0 -48
- package/docs/src/pages/components.js +0 -200
- package/docs/src/pages/config.js +0 -193
- package/docs/src/pages/constraints.js +0 -100
- package/docs/src/pages/deploy.js +0 -233
- package/docs/src/pages/error-pages.js +0 -99
- package/docs/src/pages/extending.js +0 -198
- package/docs/src/pages/faq.js +0 -99
- package/docs/src/pages/forms.js +0 -129
- package/docs/src/pages/getting-started.js +0 -147
- package/docs/src/pages/guard.js +0 -120
- package/docs/src/pages/home.js +0 -350
- package/docs/src/pages/how-it-works.js +0 -244
- package/docs/src/pages/hydration.js +0 -108
- package/docs/src/pages/images.js +0 -120
- package/docs/src/pages/markdown.js +0 -156
- package/docs/src/pages/meta.js +0 -193
- package/docs/src/pages/mutations.js +0 -107
- package/docs/src/pages/navigation.js +0 -85
- package/docs/src/pages/performance.js +0 -157
- package/docs/src/pages/persist.js +0 -89
- package/docs/src/pages/project-structure.js +0 -91
- package/docs/src/pages/prompt-examples.js +0 -186
- package/docs/src/pages/raw-responses.js +0 -122
- package/docs/src/pages/routing.js +0 -120
- package/docs/src/pages/server-api.js +0 -291
- package/docs/src/pages/server-data.js +0 -203
- package/docs/src/pages/slash-commands.js +0 -55
- package/docs/src/pages/spec.js +0 -207
- package/docs/src/pages/state.js +0 -102
- package/docs/src/pages/store.js +0 -203
- package/docs/src/pages/streaming.js +0 -107
- package/docs/src/pages/stripe.js +0 -194
- package/docs/src/pages/supabase.js +0 -324
- package/docs/src/pages/templates/andypixel.js +0 -260
- package/docs/src/pages/templates/blog-post.js +0 -226
- package/docs/src/pages/templates/bookworms.js +0 -227
- package/docs/src/pages/templates/coutumes.js +0 -237
- package/docs/src/pages/templates/journey.js +0 -186
- package/docs/src/pages/templates/mobile-app.js +0 -371
- package/docs/src/pages/templates/retrowheels.js +0 -355
- package/docs/src/pages/templates/shop.js +0 -268
- package/docs/src/pages/templates/sparkys.js +0 -264
- package/docs/src/pages/templates/suzukibento.js +0 -200
- package/docs/src/pages/templates/swish.js +0 -313
- package/docs/src/pages/templates.js +0 -534
- package/docs/src/pages/testing.js +0 -199
- package/docs/src/pages/validation.js +0 -138
- package/examples/.claude/commands/pulse-build.md +0 -7
- package/examples/.claude/commands/pulse-dev.md +0 -7
- package/examples/.claude/commands/pulse-start.md +0 -9
- package/examples/.claude/commands/pulse-stop.md +0 -5
- package/examples/.claude/commands/verify.md +0 -84
- package/examples/.claude/coverage-check.js +0 -196
- package/examples/.claude/pulse-checklist.md +0 -189
- package/examples/andypixel.css +0 -237
- package/examples/andypixel.js +0 -258
- package/examples/bookworms.css +0 -422
- package/examples/bookworms.js +0 -225
- package/examples/contact.js +0 -166
- package/examples/counter.js +0 -94
- package/examples/dev.server.js +0 -110
- package/examples/examples.test.js +0 -394
- package/examples/journey.css +0 -528
- package/examples/journey.js +0 -186
- package/examples/pricing.js +0 -262
- package/examples/products.js +0 -190
- package/examples/public/.pulse-ui-version +0 -1
- package/examples/public/pulse-ui.css +0 -3028
- package/examples/public/pulse-ui.js +0 -257
- package/examples/quiz.js +0 -208
- package/examples/retrowheels.css +0 -425
- package/examples/retrowheels.js +0 -353
- package/examples/shared.js +0 -84
- package/examples/suzukibento.css +0 -513
- package/examples/suzukibento.js +0 -200
- package/examples/swish.css +0 -394
- package/examples/swish.js +0 -311
- package/examples/todos.js +0 -162
- package/src/ui/fieldset.js +0 -37
- package/src/ui/phone-frame.js +0 -40
- package/src/ui/search.js +0 -78
- package/src/ui/segmented.js +0 -50
- package/src/ui/tooltip.js +0 -28
|
@@ -1,313 +0,0 @@
|
|
|
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
|
-
}
|