@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,534 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pulse Docs — Templates
|
|
3
|
-
*
|
|
4
|
-
* Showcase of ready-made Pulse templates.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { renderLayout, h1, lead, section } from '../lib/layout.js'
|
|
8
|
-
import { prevNext } from '../lib/nav.js'
|
|
9
|
-
import { badge } from '../../../src/ui/index.js'
|
|
10
|
-
|
|
11
|
-
function esc(s) {
|
|
12
|
-
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function statPill(label, value) {
|
|
16
|
-
return `<span style="display:inline-flex;align-items:center;gap:0.3rem;padding:0.2rem 0.6rem;border-radius:999px;font-size:0.72rem;font-weight:500;background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.14);color:rgba(255,255,255,0.7)">
|
|
17
|
-
<span style="color:#a78bfa">●</span> ${esc(label)}: <strong style="color:#fff;font-weight:600">${esc(String(value))}</strong>
|
|
18
|
-
</span>`
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function templateCard({ name, route, description, tags = [], stats = {}, preview = '' }) {
|
|
22
|
-
const tagHtml = tags.map(t =>
|
|
23
|
-
`<span style="display:inline-block;padding:0.2rem 0.6rem;background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.16);border-radius:4px;font-size:0.72rem;font-weight:500;color:rgba(255,255,255,0.7)">${esc(t)}</span>`
|
|
24
|
-
).join('')
|
|
25
|
-
|
|
26
|
-
const statHtml = Object.entries(stats).map(([k, v]) => statPill(k, v)).join('')
|
|
27
|
-
|
|
28
|
-
// This card is a deliberately dark, self-contained surface (macOS-style
|
|
29
|
-
// preview window + dark body) regardless of the page's own theme — it
|
|
30
|
-
// mirrors the code-window pattern elsewhere. Uses a real dark background
|
|
31
|
-
// (not a translucent white tint over the page background), so it reads
|
|
32
|
-
// correctly on both the light and dark docs themes and keeps the
|
|
33
|
-
// white-tinted text/borders it was designed with at proper contrast.
|
|
34
|
-
return `
|
|
35
|
-
<div style="border:1px solid rgba(255,255,255,0.1);border-radius:0.875rem;overflow:hidden;display:flex;flex-direction:column;background:#171512">
|
|
36
|
-
|
|
37
|
-
<a href="${esc(route)}" aria-label="Open ${esc(name)} template" style="display:block;background:#0d0d12;border-bottom:1px solid rgba(255,255,255,0.08);text-decoration:none;overflow:hidden">
|
|
38
|
-
<div style="padding:0.5rem 0.75rem;background:#16161e;display:flex;align-items:center;gap:0.375rem;border-bottom:1px solid rgba(255,255,255,0.06)">
|
|
39
|
-
<span style="width:8px;height:8px;border-radius:50%;background:#ff5f57;display:inline-block"></span>
|
|
40
|
-
<span style="width:8px;height:8px;border-radius:50%;background:#febc2e;display:inline-block"></span>
|
|
41
|
-
<span style="width:8px;height:8px;border-radius:50%;background:#28c840;display:inline-block"></span>
|
|
42
|
-
<span style="flex:1;height:14px;background:rgba(255,255,255,0.06);border-radius:3px;margin-left:0.5rem"></span>
|
|
43
|
-
</div>
|
|
44
|
-
<div style="padding:1rem 1.25rem 1.25rem;min-height:180px">
|
|
45
|
-
${preview}
|
|
46
|
-
</div>
|
|
47
|
-
</a>
|
|
48
|
-
|
|
49
|
-
<div style="padding:1.25rem 1.25rem 1rem;flex:1;display:flex;flex-direction:column;gap:0.875rem;color:#fff">
|
|
50
|
-
<div>
|
|
51
|
-
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.5rem;margin-bottom:0.5rem">
|
|
52
|
-
<h2 style="margin:0;font-size:1rem;font-weight:700;line-height:1.3;color:#fff">${esc(name)}</h2>
|
|
53
|
-
${tagHtml}
|
|
54
|
-
</div>
|
|
55
|
-
<p style="margin:0;font-size:0.85rem;line-height:1.6;color:rgba(255,255,255,0.7)">${esc(description)}</p>
|
|
56
|
-
</div>
|
|
57
|
-
<div style="display:flex;flex-wrap:wrap;gap:0.3rem;padding-top:0.625rem;border-top:1px solid rgba(255,255,255,0.08)">${statHtml}</div>
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
<div style="padding:0.875rem 1.25rem;border-top:1px solid rgba(255,255,255,0.08)">
|
|
61
|
-
<a href="${esc(route)}" style="display:inline-flex;align-items:center;gap:0.375rem;padding:0.5rem 1.1rem;background:#7c3aed;border-radius:0.4rem;font-size:0.85rem;font-weight:600;text-decoration:none !important;color:#fff !important">
|
|
62
|
-
View template
|
|
63
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" aria-hidden="true"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
|
64
|
-
</a>
|
|
65
|
-
</div>
|
|
66
|
-
</div>`
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Mini CSS page-layout previews ────────────────────────────────────────────────
|
|
70
|
-
|
|
71
|
-
const bar = (w, h, bg = 'rgba(255,255,255,0.12)', r = 3) =>
|
|
72
|
-
`<div style="width:${w};height:${h}px;background:${bg};border-radius:${r}px;flex-shrink:0"></div>`
|
|
73
|
-
|
|
74
|
-
const mobileAppPreview = `
|
|
75
|
-
<div style="display:flex;flex-direction:column;gap:6px;font-size:0">
|
|
76
|
-
|
|
77
|
-
<!-- nav -->
|
|
78
|
-
<div style="display:flex;align-items:center;gap:6px">
|
|
79
|
-
${bar('28px', 6, 'var(--color-accent)')}
|
|
80
|
-
${bar('40px', 4)} ${bar('30px', 4)} ${bar('30px', 4)}
|
|
81
|
-
<div style="flex:1"></div>
|
|
82
|
-
${bar('48px', 18, 'var(--color-accent)', 4)}
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<!-- hero -->
|
|
86
|
-
<div style="display:flex;gap:10px;margin-top:4px;align-items:center">
|
|
87
|
-
<div style="flex:1;display:flex;flex-direction:column;gap:5px">
|
|
88
|
-
${bar('50px', 3, 'var(--color-accent)')}
|
|
89
|
-
${bar('100%', 10, 'rgba(255,255,255,0.18)', 2)}
|
|
90
|
-
${bar('85%', 10, 'rgba(255,255,255,0.18)', 2)}
|
|
91
|
-
${bar('70%', 6, 'rgba(255,255,255,0.08)', 2)}
|
|
92
|
-
<div style="display:flex;gap:5px;margin-top:4px">
|
|
93
|
-
${bar('52px', 16, 'rgba(255,255,255,0.15)', 4)}
|
|
94
|
-
${bar('52px', 16, 'rgba(255,255,255,0.15)', 4)}
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
<div style="width:52px;height:90px;background:rgba(255,255,255,0.08);border-radius:8px;border:1px solid rgba(255,255,255,0.12);flex-shrink:0;display:flex;flex-direction:column;gap:4px;padding:6px">
|
|
98
|
-
${bar('100%', 20, 'var(--color-accent)', 4)}
|
|
99
|
-
${bar('100%', 12, 'rgba(255,255,255,0.1)', 3)}
|
|
100
|
-
${bar('100%', 12, 'rgba(255,255,255,0.1)', 3)}
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
|
|
104
|
-
<!-- stats row -->
|
|
105
|
-
<div style="display:flex;gap:5px;margin-top:2px">
|
|
106
|
-
${[1,2,3,4].map(() => `<div style="flex:1;height:22px;background:rgba(255,255,255,0.06);border-radius:3px"></div>`).join('')}
|
|
107
|
-
</div>
|
|
108
|
-
|
|
109
|
-
<!-- features grid -->
|
|
110
|
-
<div style="display:flex;gap:5px">
|
|
111
|
-
${[1,2,3].map(() => `<div style="flex:1;height:36px;background:rgba(255,255,255,0.05);border-radius:3px;padding:5px;display:flex;flex-direction:column;gap:3px">${bar('40%',4,'var(--color-accent)')}${bar('80%',3)}${bar('90%',3)}</div>`).join('')}
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
<!-- pricing row -->
|
|
115
|
-
<div style="display:flex;gap:5px">
|
|
116
|
-
${[1,2,3].map((i) => `<div style="flex:1;height:42px;background:${i===2?'rgba(139,92,246,0.2)':'rgba(255,255,255,0.05)'};border-radius:3px;border:${i===2?'1px solid rgba(139,92,246,0.5)':'1px solid transparent'};padding:5px;display:flex;flex-direction:column;gap:3px">${bar('60%',4,i===2?'var(--color-accent)':'rgba(255,255,255,0.15)')}${bar('40%',6,i===2?'rgba(255,255,255,0.6)':'rgba(255,255,255,0.2)')}${bar('80%',3)}</div>`).join('')}
|
|
117
|
-
</div>
|
|
118
|
-
</div>`
|
|
119
|
-
|
|
120
|
-
const blogPostPreview = `
|
|
121
|
-
<div style="display:flex;flex-direction:column;gap:6px;font-size:0;background:#0a2239;border-radius:4px;padding:8px">
|
|
122
|
-
|
|
123
|
-
<!-- nav -->
|
|
124
|
-
<div style="display:flex;align-items:center;gap:6px">
|
|
125
|
-
${bar('20px', 6, '#53a2be')}
|
|
126
|
-
${bar('36px', 4, '#53a2be', 2)}
|
|
127
|
-
<div style="flex:1"></div>
|
|
128
|
-
${bar('28px', 4)} ${bar('28px', 4)} ${bar('28px', 4)}
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
<!-- hero -->
|
|
132
|
-
<div style="display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 0 6px;background:linear-gradient(160deg,#0a2239 0%,#132e32 100%);border-radius:3px;margin-top:2px">
|
|
133
|
-
${bar('40px', 3, '#53a2be')}
|
|
134
|
-
${bar('90%', 8, 'rgba(255,255,255,0.7)', 2)}
|
|
135
|
-
${bar('75%', 8, 'rgba(255,255,255,0.7)', 2)}
|
|
136
|
-
${bar('65%', 4, 'rgba(83,162,190,0.6)', 2)}
|
|
137
|
-
${bar('50%', 3, 'rgba(83,162,190,0.4)', 2)}
|
|
138
|
-
</div>
|
|
139
|
-
|
|
140
|
-
<!-- article + aside -->
|
|
141
|
-
<div style="display:flex;gap:6px;margin-top:2px">
|
|
142
|
-
<div style="flex:1;display:flex;flex-direction:column;gap:4px">
|
|
143
|
-
${bar('100%', 48, 'rgba(83,162,190,0.15)', 3)}
|
|
144
|
-
${bar('100%', 4, 'rgba(255,255,255,0.5)', 1)}
|
|
145
|
-
${bar('90%', 4, 'rgba(255,255,255,0.5)', 1)}
|
|
146
|
-
${bar('95%', 4, 'rgba(255,255,255,0.5)', 1)}
|
|
147
|
-
${bar('60%', 4, 'rgba(255,255,255,0.5)', 1)}
|
|
148
|
-
${bar('55%', 6, 'rgba(255,255,255,0.7)', 1)}
|
|
149
|
-
${bar('100%', 3, 'rgba(255,255,255,0.4)', 1)}
|
|
150
|
-
${bar('85%', 3, 'rgba(255,255,255,0.4)', 1)}
|
|
151
|
-
</div>
|
|
152
|
-
<div style="width:38px;display:flex;flex-direction:column;gap:3px">
|
|
153
|
-
${bar('100%', 4, 'rgba(255,255,255,0.5)', 1)}
|
|
154
|
-
${bar('100%', 3, 'rgba(83,162,190,0.6)', 1)}
|
|
155
|
-
${bar('100%', 3, 'rgba(83,162,190,0.6)', 1)}
|
|
156
|
-
${bar('100%', 3, 'rgba(83,162,190,0.6)', 1)}
|
|
157
|
-
<div style="margin-top:3px;background:#132e32;border-radius:3px;padding:3px;display:flex;flex-direction:column;gap:2px">
|
|
158
|
-
${bar('60%', 3, '#53a2be')}
|
|
159
|
-
${bar('100%', 5, 'rgba(255,255,255,0.6)', 1)}
|
|
160
|
-
${bar('100%', 5, 'rgba(255,255,255,0.6)', 1)}
|
|
161
|
-
${bar('100%', 10, 'rgba(83,162,190,0.5)', 2)}
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
</div>`
|
|
166
|
-
|
|
167
|
-
const TEMPLATES = [
|
|
168
|
-
{
|
|
169
|
-
name: 'Mobile App Landing Page',
|
|
170
|
-
route: '/templates/mobile-app',
|
|
171
|
-
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.',
|
|
172
|
-
tags: ['Landing Page', 'Marketing', 'SaaS'],
|
|
173
|
-
preview: mobileAppPreview,
|
|
174
|
-
stats: {
|
|
175
|
-
'Accessibility': '100',
|
|
176
|
-
'Best Practices': '100',
|
|
177
|
-
'SEO': '100',
|
|
178
|
-
'CLS': '0.00',
|
|
179
|
-
'JS': '~4.2 kB',
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
name: 'Blog Post',
|
|
184
|
-
route: '/templates/blog-post',
|
|
185
|
-
description: 'A full editorial article page — sticky nav, article hero, prose with image, two-column layout with table-of-contents sidebar, newsletter signup, and footer. Dark astronomy theme.',
|
|
186
|
-
tags: ['Blog', 'Editorial'],
|
|
187
|
-
preview: blogPostPreview,
|
|
188
|
-
stats: {
|
|
189
|
-
'Accessibility': '100',
|
|
190
|
-
'Best Practices': '100',
|
|
191
|
-
'SEO': '100',
|
|
192
|
-
'CLS': '0.00',
|
|
193
|
-
'JS': '~4.2 kB',
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: 'Sparkys — Local Trades',
|
|
198
|
-
route: '/templates/sparkys',
|
|
199
|
-
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.',
|
|
200
|
-
tags: ['Local Business', 'Trades', 'Light Theme'],
|
|
201
|
-
preview: `<div style="background:#f7f9fc;padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
202
|
-
<div style="height:8px;background:#062247;border-radius:3px;width:100%;margin-bottom:0.75rem"></div>
|
|
203
|
-
<div style="display:grid;grid-template-columns:1.2fr 1fr;gap:0.75rem;min-height:80px">
|
|
204
|
-
<div style="display:flex;flex-direction:column;gap:5px;justify-content:center">
|
|
205
|
-
<div style="height:5px;background:#0a3160;border-radius:3px;width:50%"></div>
|
|
206
|
-
<div style="height:10px;background:rgba(15,23,42,0.8);border-radius:3px;width:90%"></div>
|
|
207
|
-
<div style="height:10px;background:rgba(15,23,42,0.8);border-radius:3px;width:75%"></div>
|
|
208
|
-
<div style="height:6px;background:rgba(15,23,42,0.3);border-radius:3px;width:80%;margin-bottom:4px"></div>
|
|
209
|
-
<div style="display:flex;gap:5px">
|
|
210
|
-
<div style="height:18px;background:#0a3160;border-radius:4px;width:60px"></div>
|
|
211
|
-
<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>
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
<div style="background:linear-gradient(160deg,#0a3160,#062247);border-radius:6px;padding:0.6rem;display:grid;grid-template-columns:1fr 1fr;gap:5px">
|
|
215
|
-
${[1,2,3,4].map(() => `<div style="display:flex;flex-direction:column;gap:2px">
|
|
216
|
-
<div style="height:9px;background:rgba(255,255,255,0.7);border-radius:2px;width:70%"></div>
|
|
217
|
-
<div style="height:5px;background:rgba(244,167,33,0.5);border-radius:2px;width:90%"></div>
|
|
218
|
-
</div>`).join('')}
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:5px;margin-top:0.75rem">
|
|
222
|
-
${[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">
|
|
223
|
-
<div style="width:18px;height:18px;background:#e3ecf7;border-radius:3px"></div>
|
|
224
|
-
<div style="height:5px;background:rgba(15,23,42,0.6);border-radius:2px;width:80%"></div>
|
|
225
|
-
<div style="height:4px;background:rgba(15,23,42,0.25);border-radius:2px;width:100%"></div>
|
|
226
|
-
<div style="height:4px;background:rgba(15,23,42,0.2);border-radius:2px;width:70%"></div>
|
|
227
|
-
</div>`).join('')}
|
|
228
|
-
</div>
|
|
229
|
-
</div>`,
|
|
230
|
-
stats: {
|
|
231
|
-
'Accessibility': '100',
|
|
232
|
-
'Best Practices': '100',
|
|
233
|
-
'SEO': '100',
|
|
234
|
-
'CLS': '0.00',
|
|
235
|
-
'Theme': 'light',
|
|
236
|
-
},
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
name: 'Local Business',
|
|
240
|
-
route: '/templates/local-business',
|
|
241
|
-
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.',
|
|
242
|
-
tags: ['Local Business', 'Services', 'Light Theme'],
|
|
243
|
-
preview: `<div style="background:linear-gradient(135deg,#fdf6ee,#fdebd0);padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
244
|
-
<div style="height:10px;background:#e8956d;border-radius:6px;width:60%;margin-bottom:0.75rem"></div>
|
|
245
|
-
<div style="height:22px;background:rgba(61,43,30,0.85);border-radius:4px;width:85%;margin-bottom:0.5rem"></div>
|
|
246
|
-
<div style="height:10px;background:rgba(61,43,30,0.35);border-radius:4px;width:75%;margin-bottom:1rem"></div>
|
|
247
|
-
<div style="display:flex;gap:0.5rem;margin-bottom:1.25rem">
|
|
248
|
-
<div style="height:30px;background:#c4632a;border-radius:14px;width:100px;flex-shrink:0"></div>
|
|
249
|
-
<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>
|
|
250
|
-
</div>
|
|
251
|
-
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.5rem">
|
|
252
|
-
${[1,2,3].map(() => `<div style="background:#fff;border-radius:10px;padding:0.5rem">
|
|
253
|
-
<div style="height:8px;background:#e8956d;border-radius:4px;width:70%;margin-bottom:0.35rem"></div>
|
|
254
|
-
<div style="height:6px;background:rgba(0,0,0,0.12);border-radius:3px;width:90%"></div>
|
|
255
|
-
<div style="height:6px;background:rgba(0,0,0,0.08);border-radius:3px;width:70%;margin-top:0.25rem"></div>
|
|
256
|
-
</div>`).join('')}
|
|
257
|
-
</div>
|
|
258
|
-
</div>`,
|
|
259
|
-
stats: {
|
|
260
|
-
'Vibe': 'warm',
|
|
261
|
-
'Theme': 'light',
|
|
262
|
-
'CLS': '0.00',
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
name: 'Portfolio',
|
|
267
|
-
route: '/templates/portfolio',
|
|
268
|
-
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.',
|
|
269
|
-
tags: ['Portfolio', 'Personal', 'Minimal'],
|
|
270
|
-
preview: `<div style="background:#0d0d10;padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
271
|
-
<div style="height:12px;background:rgba(255,255,255,0.7);border-radius:3px;width:55%;margin-bottom:0.75rem;font-size:0;"></div>
|
|
272
|
-
<div style="height:20px;background:rgba(255,255,255,0.85);border-radius:3px;width:80%;margin-bottom:0.5rem"></div>
|
|
273
|
-
<div style="height:8px;background:rgba(255,255,255,0.25);border-radius:3px;width:65%;margin-bottom:1.25rem"></div>
|
|
274
|
-
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.5rem">
|
|
275
|
-
${[1,2,3].map(i => `<div style="background:rgba(255,255,255,0.05);border-radius:6px;overflow:hidden">
|
|
276
|
-
<div style="height:48px;background:rgba(255,255,255,0.08);"></div>
|
|
277
|
-
<div style="padding:0.4rem">
|
|
278
|
-
<div style="height:7px;background:rgba(255,255,255,0.4);border-radius:3px;width:80%;margin-bottom:0.3rem"></div>
|
|
279
|
-
<div style="height:6px;background:rgba(255,255,255,0.2);border-radius:3px;width:60%"></div>
|
|
280
|
-
</div>
|
|
281
|
-
</div>`).join('')}
|
|
282
|
-
</div>
|
|
283
|
-
</div>`,
|
|
284
|
-
stats: {
|
|
285
|
-
'Vibe': 'minimal',
|
|
286
|
-
'Theme': 'dark',
|
|
287
|
-
'CLS': '0.00',
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
name: 'Event / Conference',
|
|
292
|
-
route: '/templates/event',
|
|
293
|
-
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.',
|
|
294
|
-
tags: ['Event', 'Conference', 'Bold'],
|
|
295
|
-
preview: `<div style="background:linear-gradient(180deg,#05050a,#0d0829);padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
296
|
-
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1.25rem">
|
|
297
|
-
<div style="height:10px;background:rgba(255,255,255,0.8);border-radius:3px;width:60px"></div>
|
|
298
|
-
<div style="height:24px;background:#6366f1;border-radius:6px;width:80px"></div>
|
|
299
|
-
</div>
|
|
300
|
-
<div style="height:8px;background:rgba(99,102,241,0.6);border-radius:4px;width:50%;margin-bottom:0.5rem"></div>
|
|
301
|
-
<div style="height:20px;background:rgba(255,255,255,0.9);border-radius:4px;width:85%;margin-bottom:0.75rem"></div>
|
|
302
|
-
<div style="height:8px;background:rgba(255,255,255,0.3);border-radius:4px;width:70%;margin-bottom:1.25rem"></div>
|
|
303
|
-
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.4rem">
|
|
304
|
-
${[1,2,3,4].map(() => `<div style="background:rgba(255,255,255,0.06);border-radius:6px;padding:0.4rem;text-align:center">
|
|
305
|
-
<div style="height:14px;background:rgba(99,102,241,0.8);border-radius:3px;width:70%;margin:0 auto 0.25rem"></div>
|
|
306
|
-
<div style="height:6px;background:rgba(255,255,255,0.2);border-radius:3px;width:80%;margin:0 auto"></div>
|
|
307
|
-
</div>`).join('')}
|
|
308
|
-
</div>
|
|
309
|
-
</div>`,
|
|
310
|
-
stats: {
|
|
311
|
-
'Vibe': 'bold',
|
|
312
|
-
'Theme': 'dark',
|
|
313
|
-
'CLS': '0.00',
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
name: 'Editorial / Magazine',
|
|
318
|
-
route: '/templates/editorial',
|
|
319
|
-
description: 'A publication homepage — featured article hero, pullquote, article grid with category badges, newsletter signup, and footer. Serif typography throughout via the editorial vibe.',
|
|
320
|
-
tags: ['Editorial', 'Publication', 'Blog'],
|
|
321
|
-
preview: `<div style="background:#0a0a0c;padding:1.5rem;border-radius:8px;font-family:Georgia,serif">
|
|
322
|
-
<div style="height:8px;background:rgba(180,100,60,0.7);border-radius:2px;width:40%;margin-bottom:0.75rem"></div>
|
|
323
|
-
<div style="height:20px;background:rgba(255,255,255,0.9);border-radius:2px;width:90%;margin-bottom:0.35rem"></div>
|
|
324
|
-
<div style="height:20px;background:rgba(255,255,255,0.75);border-radius:2px;width:70%;margin-bottom:0.75rem"></div>
|
|
325
|
-
<div style="height:8px;background:rgba(255,255,255,0.3);border-radius:2px;width:80%;margin-bottom:1.25rem"></div>
|
|
326
|
-
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.5rem">
|
|
327
|
-
${[1,2,3].map(() => `<div style="background:rgba(255,255,255,0.04);border-radius:2px;overflow:hidden">
|
|
328
|
-
<div style="height:44px;background:rgba(255,255,255,0.08);"></div>
|
|
329
|
-
<div style="padding:0.4rem">
|
|
330
|
-
<div style="height:6px;background:rgba(180,100,60,0.6);border-radius:2px;width:40%;margin-bottom:0.3rem"></div>
|
|
331
|
-
<div style="height:8px;background:rgba(255,255,255,0.5);border-radius:2px;width:90%;margin-bottom:0.25rem"></div>
|
|
332
|
-
<div style="height:8px;background:rgba(255,255,255,0.3);border-radius:2px;width:70%"></div>
|
|
333
|
-
</div>
|
|
334
|
-
</div>`).join('')}
|
|
335
|
-
</div>
|
|
336
|
-
</div>`,
|
|
337
|
-
stats: {
|
|
338
|
-
'Vibe': 'editorial',
|
|
339
|
-
'Theme': 'dark',
|
|
340
|
-
'CLS': '0.00',
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
name: 'Swish — Fashion Ecommerce',
|
|
345
|
-
route: '/templates/swish',
|
|
346
|
-
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.',
|
|
347
|
-
tags: ['Ecommerce', 'Fashion', 'Editorial', 'Light Theme'],
|
|
348
|
-
preview: `<div style="background:#faf9f7;padding:1.5rem;border-radius:8px;font-family:system-ui,sans-serif">
|
|
349
|
-
<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">
|
|
350
|
-
<div>
|
|
351
|
-
<div style="height:6px;background:rgba(255,255,255,0.5);border-radius:2px;width:60px;margin-bottom:4px"></div>
|
|
352
|
-
<div style="height:12px;background:#fff;border-radius:2px;width:120px;margin-bottom:6px"></div>
|
|
353
|
-
<div style="height:20px;background:rgba(255,255,255,0.9);border-radius:14px;width:80px"></div>
|
|
354
|
-
</div>
|
|
355
|
-
</div>
|
|
356
|
-
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.4rem">
|
|
357
|
-
${[1,2,3].map(() => `<div style="background:#ede9e3;border-radius:4px;overflow:hidden">
|
|
358
|
-
<div style="height:44px;background:rgba(90,70,50,0.15);"></div>
|
|
359
|
-
<div style="padding:0.3rem">
|
|
360
|
-
<div style="height:6px;background:rgba(0,0,0,0.4);border-radius:2px;width:80%;margin-bottom:3px"></div>
|
|
361
|
-
<div style="height:5px;background:rgba(0,0,0,0.2);border-radius:2px;width:50%"></div>
|
|
362
|
-
</div>
|
|
363
|
-
</div>`).join('')}
|
|
364
|
-
</div>
|
|
365
|
-
</div>`,
|
|
366
|
-
stats: {
|
|
367
|
-
'Vibe': 'editorial',
|
|
368
|
-
'Theme': 'light',
|
|
369
|
-
'CLS': '0.00',
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
name: 'BookWorms — Independent Bookshop',
|
|
374
|
-
route: '/templates/bookworms',
|
|
375
|
-
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.',
|
|
376
|
-
tags: ['Local Business', 'Retail', 'Editorial', 'Light Theme'],
|
|
377
|
-
preview: `<div style="background:#fdf8f2;padding:1.5rem;border-radius:8px;font-family:Georgia,serif">
|
|
378
|
-
<div style="height:8px;background:#8b4513;border-radius:2px;width:30%;margin-bottom:0.75rem"></div>
|
|
379
|
-
<div style="height:18px;background:rgba(60,30,10,0.85);border-radius:2px;width:85%;margin-bottom:0.5rem"></div>
|
|
380
|
-
<div style="height:8px;background:rgba(60,30,10,0.3);border-radius:2px;width:70%;margin-bottom:1rem"></div>
|
|
381
|
-
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:0.4rem">
|
|
382
|
-
${[1,2,3,4].map(() => `<div style="background:#f0e8da;border-radius:3px;overflow:hidden">
|
|
383
|
-
<div style="height:50px;background:rgba(139,69,19,0.2);"></div>
|
|
384
|
-
<div style="padding:0.3rem">
|
|
385
|
-
<div style="height:5px;background:rgba(60,30,10,0.5);border-radius:2px;width:90%;margin-bottom:3px"></div>
|
|
386
|
-
<div style="height:4px;background:rgba(60,30,10,0.2);border-radius:2px;width:70%"></div>
|
|
387
|
-
</div>
|
|
388
|
-
</div>`).join('')}
|
|
389
|
-
</div>
|
|
390
|
-
</div>`,
|
|
391
|
-
stats: {
|
|
392
|
-
'Vibe': 'paper',
|
|
393
|
-
'Theme': 'light',
|
|
394
|
-
'CLS': '0.00',
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
name: 'Retro Wheels Co. — Classic Cars',
|
|
399
|
-
route: '/templates/retrowheels',
|
|
400
|
-
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.',
|
|
401
|
-
tags: ['Retro', 'Automotive', 'Form', 'Light Theme'],
|
|
402
|
-
preview: `<div style="background:#f5f0e8;padding:1.5rem;border-radius:8px;font-family:'Courier New',monospace">
|
|
403
|
-
<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">
|
|
404
|
-
<div style="height:14px;background:rgba(205,160,60,0.9);border-radius:2px;width:100px"></div>
|
|
405
|
-
</div>
|
|
406
|
-
<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">
|
|
407
|
-
${[1,2,3].map(() => `<div style="text-align:center">
|
|
408
|
-
<div style="height:10px;background:rgba(205,160,60,0.8);border-radius:2px;width:60%;margin:0 auto 3px"></div>
|
|
409
|
-
<div style="height:6px;background:rgba(255,255,255,0.3);border-radius:2px;width:80%;margin:0 auto"></div>
|
|
410
|
-
</div>`).join('')}
|
|
411
|
-
</div>
|
|
412
|
-
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.4rem">
|
|
413
|
-
${[1,2].map(() => `<div style="background:#fff;border:2px solid #1a1a2e;border-radius:3px;padding:0.4rem">
|
|
414
|
-
<div style="height:7px;background:rgba(26,26,46,0.7);border-radius:2px;width:90%;margin-bottom:4px"></div>
|
|
415
|
-
<div style="height:5px;background:rgba(26,26,46,0.3);border-radius:2px;width:70%"></div>
|
|
416
|
-
</div>`).join('')}
|
|
417
|
-
</div>
|
|
418
|
-
</div>`,
|
|
419
|
-
stats: {
|
|
420
|
-
'Vibe': 'retro',
|
|
421
|
-
'Theme': 'light',
|
|
422
|
-
'CLS': '0.00',
|
|
423
|
-
},
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
name: 'Susuki Bento — Artist Portfolio',
|
|
427
|
-
route: '/templates/suzukibento',
|
|
428
|
-
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.',
|
|
429
|
-
tags: ['Portfolio', 'Art', 'Brutalist', 'Dark Theme'],
|
|
430
|
-
preview: `<div style="background:#0e0e0e;padding:1.5rem;border-radius:0;font-family:system-ui,sans-serif;border:1px solid #222">
|
|
431
|
-
<div style="height:10px;background:#f2efe8;width:80%;margin-bottom:0.75rem"></div>
|
|
432
|
-
<div style="height:6px;background:#cc3300;width:40%;margin-bottom:1rem"></div>
|
|
433
|
-
<div style="display:grid;grid-template-columns:1.5fr 1fr 1fr;grid-template-rows:60px 40px;gap:3px">
|
|
434
|
-
<div style="background:rgba(242,239,232,0.1);grid-row:1/3"></div>
|
|
435
|
-
<div style="background:rgba(242,239,232,0.08)"></div>
|
|
436
|
-
<div style="background:rgba(204,51,0,0.15)"></div>
|
|
437
|
-
<div style="background:rgba(242,239,232,0.06);grid-column:2/4"></div>
|
|
438
|
-
</div>
|
|
439
|
-
<div style="margin-top:0.75rem;display:flex;gap:0.4rem">
|
|
440
|
-
${[1,2,3].map(() => `<div style="height:4px;background:rgba(242,239,232,0.2);border-radius:0;flex:1"></div>`).join('')}
|
|
441
|
-
</div>
|
|
442
|
-
</div>`,
|
|
443
|
-
stats: {
|
|
444
|
-
'Vibe': 'brutalist',
|
|
445
|
-
'Theme': 'dark',
|
|
446
|
-
'CLS': '0.00',
|
|
447
|
-
'Mode': 'B — raw HTML',
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
name: 'Andy Pixel — Dev Blog',
|
|
452
|
-
route: '/templates/andypixel',
|
|
453
|
-
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.',
|
|
454
|
-
tags: ['Blog', 'Editorial', 'Article', 'Light Theme'],
|
|
455
|
-
preview: `<div style="background:#fdfaf6;padding:1.5rem;border-radius:8px;font-family:Georgia,serif">
|
|
456
|
-
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.75rem">
|
|
457
|
-
<div style="width:24px;height:24px;background:#e65c00;border-radius:50%;flex-shrink:0"></div>
|
|
458
|
-
<div style="height:6px;background:rgba(0,0,0,0.3);border-radius:3px;width:80px"></div>
|
|
459
|
-
<div style="height:14px;background:#e65c00;border-radius:10px;width:40px;margin-left:auto"></div>
|
|
460
|
-
</div>
|
|
461
|
-
<div style="height:18px;background:rgba(0,0,0,0.8);border-radius:2px;width:90%;margin-bottom:0.5rem"></div>
|
|
462
|
-
<div style="height:18px;background:rgba(0,0,0,0.6);border-radius:2px;width:70%;margin-bottom:0.75rem"></div>
|
|
463
|
-
<div style="background:#1a1a1a;border-radius:6px;padding:0.5rem;margin-bottom:0.75rem">
|
|
464
|
-
<div style="height:5px;background:rgba(230,92,0,0.5);border-radius:2px;width:50%;margin-bottom:5px"></div>
|
|
465
|
-
${[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('')}
|
|
466
|
-
</div>
|
|
467
|
-
<div style="height:6px;background:rgba(0,0,0,0.2);border-radius:2px;width:85%"></div>
|
|
468
|
-
</div>`,
|
|
469
|
-
stats: {
|
|
470
|
-
'Vibe': 'editorial',
|
|
471
|
-
'Theme': 'light',
|
|
472
|
-
'CLS': '0.00',
|
|
473
|
-
},
|
|
474
|
-
},
|
|
475
|
-
{
|
|
476
|
-
name: 'Journey — App Landing Page',
|
|
477
|
-
route: '/templates/journey',
|
|
478
|
-
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.',
|
|
479
|
-
tags: ['Landing Page', 'App', 'Dark Theme', 'Split-screen'],
|
|
480
|
-
preview: `<div style="background:#0a0a0f;padding:0;border-radius:8px;overflow:hidden;display:grid;grid-template-columns:1fr 1fr;min-height:130px">
|
|
481
|
-
<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)">
|
|
482
|
-
<div style="height:5px;background:rgba(201,169,110,0.5);border-radius:2px;width:50%;margin-bottom:2px"></div>
|
|
483
|
-
<div style="height:14px;background:rgba(240,237,232,0.9);border-radius:2px;width:90%"></div>
|
|
484
|
-
<div style="height:14px;background:rgba(240,237,232,0.6);border-radius:2px;width:70%"></div>
|
|
485
|
-
<div style="height:8px;background:rgba(240,237,232,0.25);border-radius:2px;width:80%;margin-top:2px"></div>
|
|
486
|
-
<div style="height:20px;background:rgba(201,169,110,0.8);border-radius:4px;width:80px;margin-top:4px"></div>
|
|
487
|
-
</div>
|
|
488
|
-
<div style="padding:0.75rem;display:flex;flex-direction:column;gap:0.4rem">
|
|
489
|
-
${[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)">
|
|
490
|
-
<div style="height:5px;background:rgba(201,169,110,0.5);border-radius:2px;width:40%;margin-bottom:3px"></div>
|
|
491
|
-
<div style="height:4px;background:rgba(240,237,232,0.2);border-radius:2px;width:85%"></div>
|
|
492
|
-
</div>`).join('')}
|
|
493
|
-
</div>
|
|
494
|
-
</div>`,
|
|
495
|
-
stats: {
|
|
496
|
-
'Vibe': 'bold',
|
|
497
|
-
'Theme': 'dark',
|
|
498
|
-
'CLS': '0.00',
|
|
499
|
-
'Mode': 'B — raw HTML',
|
|
500
|
-
},
|
|
501
|
-
},
|
|
502
|
-
]
|
|
503
|
-
|
|
504
|
-
const content = `
|
|
505
|
-
${h1('Templates')}
|
|
506
|
-
${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.')}
|
|
507
|
-
|
|
508
|
-
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1.5rem;margin-top:2rem">
|
|
509
|
-
${TEMPLATES.map(templateCard).join('')}
|
|
510
|
-
</div>
|
|
511
|
-
|
|
512
|
-
${section('design-directions', 'Design directions')}
|
|
513
|
-
<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>
|
|
514
|
-
<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>
|
|
515
|
-
|
|
516
|
-
${section('adding', 'Suggesting a template')}
|
|
517
|
-
<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>
|
|
518
|
-
`
|
|
519
|
-
|
|
520
|
-
export default {
|
|
521
|
-
route: '/templates',
|
|
522
|
-
|
|
523
|
-
meta: {
|
|
524
|
-
title: 'Templates — Pulse',
|
|
525
|
-
description: 'Production-ready Pulse templates. Every template uses only Pulse UI components and ships with 100/100/100 Lighthouse scores.',
|
|
526
|
-
theme: 'light',
|
|
527
|
-
styles: ['/pulse-ui.css', '/theme.css', '/docs.css'],
|
|
528
|
-
},
|
|
529
|
-
|
|
530
|
-
view: () => {
|
|
531
|
-
const { prev, next } = prevNext('/templates')
|
|
532
|
-
return renderLayout({ currentHref: '/templates', content, prev, next })
|
|
533
|
-
},
|
|
534
|
-
}
|