@invisibleloop/pulse 0.20.5 → 0.20.7
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/pulse-checklist.md +1 -2
- package/CLAUDE.md +1 -2
- package/docs/.claude/pulse-checklist.md +1 -2
- package/docs/public/coutumes.css +21 -44
- package/docs/public/docs.css +73 -6
- package/docs/public/home-brut.css +175 -191
- package/docs/public/pulse-ui.css +8 -1037
- package/docs/public/retrowheels.css +23 -0
- package/docs/public/shop.css +35 -1
- package/docs/public/sparkys.css +56 -23
- package/docs/public/theme.css +51 -7
- package/docs/server.js +0 -11
- package/docs/src/lib/component-page.js +2 -1
- package/docs/src/lib/nav.js +0 -11
- package/docs/src/pages/accessibility.js +2 -1
- package/docs/src/pages/actions.js +2 -1
- package/docs/src/pages/auth.js +2 -1
- package/docs/src/pages/caching.js +2 -1
- package/docs/src/pages/components/accordion.js +2 -1
- package/docs/src/pages/components/alert.js +2 -1
- package/docs/src/pages/components/app-badge.js +2 -1
- package/docs/src/pages/components/avatar.js +2 -1
- package/docs/src/pages/components/badge.js +2 -1
- package/docs/src/pages/components/breadcrumbs.js +2 -1
- package/docs/src/pages/components/button.js +2 -1
- package/docs/src/pages/components/card.js +2 -1
- package/docs/src/pages/components/charts.js +4 -2
- package/docs/src/pages/components/checkbox.js +2 -1
- package/docs/src/pages/components/cluster.js +2 -1
- package/docs/src/pages/components/container.js +2 -1
- package/docs/src/pages/components/cta.js +2 -1
- package/docs/src/pages/components/divider.js +2 -1
- package/docs/src/pages/components/empty.js +2 -1
- package/docs/src/pages/components/feature.js +2 -1
- package/docs/src/pages/components/fieldset.js +2 -1
- package/docs/src/pages/components/fileupload.js +2 -1
- package/docs/src/pages/components/footer.js +2 -1
- package/docs/src/pages/components/grid.js +11 -6
- package/docs/src/pages/components/heading.js +2 -1
- package/docs/src/pages/components/hero.js +2 -1
- package/docs/src/pages/components/icons.js +2 -1
- package/docs/src/pages/components/image.js +2 -1
- package/docs/src/pages/components/input.js +2 -1
- package/docs/src/pages/components/media.js +2 -1
- package/docs/src/pages/components/modal.js +2 -1
- package/docs/src/pages/components/nav.js +2 -1
- package/docs/src/pages/components/phone-frame.js +2 -1
- package/docs/src/pages/components/progress.js +2 -1
- package/docs/src/pages/components/prose.js +2 -1
- package/docs/src/pages/components/pullquote.js +2 -1
- package/docs/src/pages/components/radio.js +2 -1
- package/docs/src/pages/components/rating.js +2 -1
- package/docs/src/pages/components/search.js +2 -1
- package/docs/src/pages/components/section.js +2 -1
- package/docs/src/pages/components/segmented.js +2 -1
- package/docs/src/pages/components/select.js +2 -1
- package/docs/src/pages/components/slider.js +2 -1
- package/docs/src/pages/components/spinner.js +2 -1
- package/docs/src/pages/components/stack.js +2 -1
- package/docs/src/pages/components/stat.js +2 -1
- package/docs/src/pages/components/stepper.js +2 -1
- package/docs/src/pages/components/table.js +2 -1
- package/docs/src/pages/components/textarea.js +2 -1
- package/docs/src/pages/components/toggle.js +2 -1
- package/docs/src/pages/components/tooltip.js +2 -1
- package/docs/src/pages/components.js +2 -5
- package/docs/src/pages/config.js +2 -1
- package/docs/src/pages/constraints.js +2 -1
- package/docs/src/pages/deploy.js +2 -1
- package/docs/src/pages/error-pages.js +2 -1
- package/docs/src/pages/extending.js +2 -1
- package/docs/src/pages/faq.js +2 -1
- package/docs/src/pages/forms.js +2 -1
- package/docs/src/pages/getting-started.js +2 -1
- package/docs/src/pages/guard.js +2 -1
- package/docs/src/pages/home.js +24 -15
- package/docs/src/pages/how-it-works.js +22 -15
- package/docs/src/pages/hydration.js +2 -1
- package/docs/src/pages/images.js +2 -1
- package/docs/src/pages/markdown.js +2 -1
- package/docs/src/pages/meta.js +2 -1
- package/docs/src/pages/mutations.js +2 -1
- package/docs/src/pages/navigation.js +2 -1
- package/docs/src/pages/performance.js +2 -1
- package/docs/src/pages/persist.js +2 -1
- package/docs/src/pages/project-structure.js +2 -1
- package/docs/src/pages/prompt-examples.js +2 -1
- package/docs/src/pages/raw-responses.js +2 -1
- package/docs/src/pages/routing.js +2 -1
- package/docs/src/pages/server-api.js +2 -1
- package/docs/src/pages/server-data.js +2 -1
- package/docs/src/pages/slash-commands.js +2 -1
- package/docs/src/pages/spec.js +2 -1
- package/docs/src/pages/state.js +2 -1
- package/docs/src/pages/store.js +2 -1
- package/docs/src/pages/streaming.js +2 -1
- package/docs/src/pages/stripe.js +2 -1
- package/docs/src/pages/supabase.js +2 -1
- package/docs/src/pages/templates/coutumes.js +83 -72
- package/docs/src/pages/templates/mobile-app.js +36 -9
- package/docs/src/pages/templates/retrowheels.js +17 -4
- package/docs/src/pages/templates/shop.js +21 -10
- package/docs/src/pages/templates/sparkys.js +35 -20
- package/docs/src/pages/templates.js +2 -2
- package/docs/src/pages/testing.js +2 -1
- package/docs/src/pages/validation.js +2 -1
- package/examples/.claude/pulse-checklist.md +1 -2
- package/examples/examples.test.js +4 -4
- package/examples/pricing.js +21 -3
- package/examples/public/pulse-ui.css +8 -1037
- package/examples/retrowheels.css +23 -0
- package/examples/retrowheels.js +17 -4
- package/package.json +5 -1
- package/public/.pulse-ui-version +1 -1
- package/public/pulse-ui.css +8 -1037
- package/src/agent/checklist.md +1 -2
- package/src/agent/guide-components.md +43 -67
- package/src/agent/guide-design-gallery.md +14 -14
- package/src/agent/guide-design-references.md +21 -21
- package/src/agent/guide-styles.md +2 -2
- package/src/agent/guide-templates.md +6 -6
- package/src/agent/identity.md +1 -1
- package/src/agent/workflow.md +1 -2
- package/src/mcp/server.js +31 -24
- package/src/ui/index.js +0 -27
- package/src/ui/ui.test.js +0 -137
- package/types/charts.d.ts +61 -0
- package/types/ui.d.ts +1 -129
- package/docs/src/pages/components/banner.js +0 -44
- package/docs/src/pages/components/carousel.js +0 -71
- package/docs/src/pages/components/code-window.js +0 -56
- package/docs/src/pages/components/decorate.js +0 -74
- package/docs/src/pages/components/gallery.js +0 -102
- package/docs/src/pages/components/list.js +0 -111
- package/docs/src/pages/components/marquee.js +0 -76
- package/docs/src/pages/components/photo-card.js +0 -74
- package/docs/src/pages/components/pricing.js +0 -67
- package/docs/src/pages/components/testimonial.js +0 -48
- package/docs/src/pages/components/timeline.js +0 -226
- package/src/ui/banner.js +0 -41
- package/src/ui/brand-statement.js +0 -58
- package/src/ui/carousel.js +0 -59
- package/src/ui/code-window.js +0 -39
- package/src/ui/colophon.js +0 -29
- package/src/ui/contact-row.js +0 -41
- package/src/ui/decorate.js +0 -55
- package/src/ui/display-heading.js +0 -44
- package/src/ui/drop-cap.js +0 -29
- package/src/ui/editorial-hero.js +0 -63
- package/src/ui/entry-list.js +0 -36
- package/src/ui/gallery.js +0 -60
- package/src/ui/image-pair.js +0 -72
- package/src/ui/list.js +0 -36
- package/src/ui/marquee.js +0 -48
- package/src/ui/photo-card.js +0 -44
- package/src/ui/pricing.js +0 -56
- package/src/ui/product-card.js +0 -65
- package/src/ui/section-label.js +0 -38
- package/src/ui/service-card.js +0 -47
- package/src/ui/stat-grid.js +0 -40
- package/src/ui/testimonial.js +0 -59
- package/src/ui/timeline.js +0 -72
- package/src/ui/top-strip.js +0 -47
- package/src/ui/trust-list.js +0 -46
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table } from '../../lib/layout.js'
|
|
4
|
-
import { carousel } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/carousel')
|
|
7
|
-
|
|
8
|
-
function slide(bg, label) {
|
|
9
|
-
return `<div style="height:220px;display:flex;align-items:center;justify-content:center;background:${bg};border-radius:10px;font-size:1.1rem;font-weight:700;color:var(--ui-text)">${label}</div>`
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default {
|
|
13
|
-
route: '/components/carousel',
|
|
14
|
-
meta: {
|
|
15
|
-
title: 'Carousel — Pulse Docs',
|
|
16
|
-
description: 'Carousel / slider component for Pulse UI.',
|
|
17
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
18
|
-
},
|
|
19
|
-
view: () => renderComponentPage({
|
|
20
|
-
currentHref: '/components/carousel',
|
|
21
|
-
prev,
|
|
22
|
-
next,
|
|
23
|
-
name: 'carousel',
|
|
24
|
-
description: 'CSS scroll-snap carousel with optional prev/next arrows and dot navigation. Touch / swipe friendly out of the box. Requires <code>pulse-ui.js</code> for button and dot interactivity.',
|
|
25
|
-
content: `
|
|
26
|
-
${demo(
|
|
27
|
-
carousel({
|
|
28
|
-
slides: [
|
|
29
|
-
slide('var(--ui-surface-2)', 'Slide 1'),
|
|
30
|
-
slide('var(--ui-surface)', 'Slide 2'),
|
|
31
|
-
slide('var(--ui-surface-2)', 'Slide 3'),
|
|
32
|
-
],
|
|
33
|
-
}),
|
|
34
|
-
`carousel({
|
|
35
|
-
slides: [
|
|
36
|
-
\`<div class="slide">Slide 1</div>\`,
|
|
37
|
-
\`<div class="slide">Slide 2</div>\`,
|
|
38
|
-
\`<div class="slide">Slide 3</div>\`,
|
|
39
|
-
],
|
|
40
|
-
})`
|
|
41
|
-
)}
|
|
42
|
-
|
|
43
|
-
<h2 class="doc-h2" id="arrows-dots"><a href="#arrows-dots" class="heading-anchor">Arrows and dots</a></h2>
|
|
44
|
-
${demo(
|
|
45
|
-
carousel({
|
|
46
|
-
arrows: true,
|
|
47
|
-
dots: false,
|
|
48
|
-
slides: [
|
|
49
|
-
slide('var(--ui-surface-2)', 'Arrows only — Slide 1'),
|
|
50
|
-
slide('var(--ui-surface)', 'Arrows only — Slide 2'),
|
|
51
|
-
],
|
|
52
|
-
}),
|
|
53
|
-
`carousel({
|
|
54
|
-
slides: [ /* ... */ ],
|
|
55
|
-
arrows: true,
|
|
56
|
-
dots: false, // hide dot navigation
|
|
57
|
-
})`
|
|
58
|
-
)}
|
|
59
|
-
|
|
60
|
-
${table(
|
|
61
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
62
|
-
[
|
|
63
|
-
['<code>slides</code>', 'string[] (HTML)', '[]', 'Array of raw HTML strings — one per slide'],
|
|
64
|
-
['<code>arrows</code>', 'boolean', '<code>true</code>', 'Show prev/next arrow buttons'],
|
|
65
|
-
['<code>dots</code>', 'boolean', '<code>true</code>', 'Show dot navigation (hidden when only one slide)'],
|
|
66
|
-
['<code>class</code>', 'string', '—', ''],
|
|
67
|
-
]
|
|
68
|
-
)}
|
|
69
|
-
`,
|
|
70
|
-
}),
|
|
71
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table } from '../../lib/layout.js'
|
|
4
|
-
import { codeWindow } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/code-window')
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
route: '/components/code-window',
|
|
10
|
-
meta: {
|
|
11
|
-
title: 'Code Window — Pulse Docs',
|
|
12
|
-
description: 'macOS-style window chrome around a code block. Accepts pre-highlighted HTML as the content slot.',
|
|
13
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
-
},
|
|
15
|
-
view: () => renderComponentPage({
|
|
16
|
-
currentHref: '/components/code-window',
|
|
17
|
-
prev,
|
|
18
|
-
next,
|
|
19
|
-
name: 'codeWindow',
|
|
20
|
-
description: 'macOS-style window chrome around a code block. Accepts pre-highlighted HTML as the content slot — the component handles all the chrome, layout, scrolling, and monospace typography.',
|
|
21
|
-
content: `
|
|
22
|
-
${demo(
|
|
23
|
-
codeWindow({
|
|
24
|
-
filename: 'home.js',
|
|
25
|
-
lang: 'JavaScript',
|
|
26
|
-
content: `<span style="color:var(--ui-accent)">export default</span> {
|
|
27
|
-
<span style="color:var(--ui-text)">state</span>: { count: <span style="color:var(--ui-green)">0</span> },
|
|
28
|
-
|
|
29
|
-
<span style="color:var(--ui-text)">view</span>: (state) => \`
|
|
30
|
-
<h1>\${state.count}</h1>
|
|
31
|
-
<button data-event=<span style="color:var(--ui-yellow)">"inc"</span>>+</button>
|
|
32
|
-
\`,
|
|
33
|
-
|
|
34
|
-
<span style="color:var(--ui-text)">mutations</span>: {
|
|
35
|
-
inc: (state) => ({ count: state.count + <span style="color:var(--ui-green)">1</span> }),
|
|
36
|
-
},
|
|
37
|
-
}`,
|
|
38
|
-
}),
|
|
39
|
-
`codeWindow({
|
|
40
|
-
filename: 'home.js',
|
|
41
|
-
lang: 'JavaScript',
|
|
42
|
-
content: highlightedHtml, // pre-rendered HTML with syntax token spans
|
|
43
|
-
})`
|
|
44
|
-
)}
|
|
45
|
-
|
|
46
|
-
${table(
|
|
47
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
48
|
-
[
|
|
49
|
-
['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot — pre-highlighted code HTML or plain text'],
|
|
50
|
-
['<code>filename</code>', 'string', '—', "Filename shown in the chrome bar (e.g. 'home.js')"],
|
|
51
|
-
['<code>lang</code>', 'string', '—', "Language label shown on the right of the chrome (e.g. 'JavaScript')"],
|
|
52
|
-
]
|
|
53
|
-
)}
|
|
54
|
-
`,
|
|
55
|
-
}),
|
|
56
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table, callout } from '../../lib/layout.js'
|
|
4
|
-
import { decorate } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/decorate')
|
|
7
|
-
|
|
8
|
-
const swatch = (pattern, label) => `
|
|
9
|
-
<div style="position:relative;width:200px;height:120px;border-radius:var(--ui-radius);border:1px solid var(--ui-border);overflow:hidden;display:flex;align-items:center;justify-content:center">
|
|
10
|
-
${decorate({ pattern })}
|
|
11
|
-
<span style="position:relative;z-index:1;font-weight:600;font-size:.875rem">${label}</span>
|
|
12
|
-
</div>`
|
|
13
|
-
|
|
14
|
-
export default {
|
|
15
|
-
route: '/components/decorate',
|
|
16
|
-
meta: {
|
|
17
|
-
title: 'Decorate — Pulse Docs',
|
|
18
|
-
description: 'Inline SVG background pattern overlay for adding subtle texture to sections in Pulse UI.',
|
|
19
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
20
|
-
},
|
|
21
|
-
view: () => renderComponentPage({
|
|
22
|
-
currentHref: '/components/decorate',
|
|
23
|
-
prev,
|
|
24
|
-
next,
|
|
25
|
-
name: 'decorate',
|
|
26
|
-
description: 'Inline SVG background pattern. Drop it inside any container with <code>position: relative</code> to add subtle texture without external assets. Five patterns: dots, grid, lines, zigzag, cross. Zero JavaScript — pure inline SVG.',
|
|
27
|
-
content: `
|
|
28
|
-
<h2 class="doc-h2" id="patterns">Patterns</h2>
|
|
29
|
-
<p>Five built-in patterns. The SVG fills the parent completely with <code>position: absolute; inset: 0</code>.</p>
|
|
30
|
-
${demo(
|
|
31
|
-
`<div style="display:flex;flex-wrap:wrap;gap:1rem">` +
|
|
32
|
-
swatch('dots', 'dots') +
|
|
33
|
-
swatch('grid', 'grid') +
|
|
34
|
-
swatch('lines', 'lines') +
|
|
35
|
-
swatch('zigzag', 'zigzag') +
|
|
36
|
-
swatch('cross', 'cross') +
|
|
37
|
-
`</div>`,
|
|
38
|
-
`// Parent must have position: relative
|
|
39
|
-
<div style="position:relative">
|
|
40
|
-
${`${decorate({ pattern: 'dots' })}`}
|
|
41
|
-
<p>Your content sits on top</p>
|
|
42
|
-
</div>`
|
|
43
|
-
)}
|
|
44
|
-
|
|
45
|
-
<h2 class="doc-h2" id="color">Color and opacity</h2>
|
|
46
|
-
<p>The default color is <code>var(--ui-border)</code> — it fits both light and dark themes. Override with any CSS value. Control density with <code>opacity</code>.</p>
|
|
47
|
-
${demo(
|
|
48
|
-
`<div style="display:flex;flex-wrap:wrap;gap:1rem">` +
|
|
49
|
-
`<div style="position:relative;width:200px;height:120px;border-radius:var(--ui-radius);overflow:hidden;background:var(--ui-accent);display:flex;align-items:center;justify-content:center">` +
|
|
50
|
-
decorate({ pattern: 'dots', color: '#fff', opacity: 0.3 }) +
|
|
51
|
-
`<span style="position:relative;z-index:1;color:#fff;font-weight:600">opacity: 0.3</span>` +
|
|
52
|
-
`</div>` +
|
|
53
|
-
`<div style="position:relative;width:200px;height:120px;border-radius:var(--ui-radius);overflow:hidden;background:var(--ui-surface-raised);display:flex;align-items:center;justify-content:center">` +
|
|
54
|
-
decorate({ pattern: 'grid', opacity: 0.6 }) +
|
|
55
|
-
`<span style="position:relative;z-index:1;font-weight:600">opacity: 0.6</span>` +
|
|
56
|
-
`</div>` +
|
|
57
|
-
`</div>`,
|
|
58
|
-
`decorate({ pattern: 'dots', color: '#fff', opacity: 0.3 })`
|
|
59
|
-
)}
|
|
60
|
-
|
|
61
|
-
${callout('note', 'The <code>decorate</code> element is <code>aria-hidden="true"</code> — it is purely decorative. Make sure surrounding content has sufficient contrast without relying on the pattern.')}
|
|
62
|
-
|
|
63
|
-
${table(
|
|
64
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
65
|
-
[
|
|
66
|
-
['<code>pattern</code>', 'string', "'dots'", "'dots' · 'grid' · 'lines' · 'zigzag' · 'cross'"],
|
|
67
|
-
['<code>color</code>', 'string', "'var(--ui-border)'", 'Any CSS color value — <code>var()</code> tokens and hex are both accepted'],
|
|
68
|
-
['<code>opacity</code>', 'number', '0.4', 'SVG opacity — 0 (invisible) to 1 (solid)'],
|
|
69
|
-
['<code>size</code>', 'number', '20', 'Pattern tile size in pixels — smaller values = denser pattern'],
|
|
70
|
-
]
|
|
71
|
-
)}
|
|
72
|
-
`,
|
|
73
|
-
}),
|
|
74
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table } from '../../lib/layout.js'
|
|
4
|
-
import { gallery } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/gallery')
|
|
7
|
-
|
|
8
|
-
const PHOTOS = [
|
|
9
|
-
{ src: '/img/placeholder-landscape.svg', alt: 'Mountain landscape' },
|
|
10
|
-
{ src: '/img/placeholder-square.svg', alt: 'Portrait photo' },
|
|
11
|
-
{ src: '/img/placeholder-tall.svg', alt: 'Urban street' },
|
|
12
|
-
{ src: '/img/placeholder-landscape.svg', alt: 'Coastal path' },
|
|
13
|
-
{ src: '/img/placeholder-square.svg', alt: 'Dog in the park' },
|
|
14
|
-
{ src: '/img/placeholder-tall.svg', alt: 'City skyline' },
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
const CAPTIONED = [
|
|
18
|
-
{ src: '/img/placeholder-landscape.svg', alt: 'Mountain landscape', caption: 'Glen Coe, Scotland' },
|
|
19
|
-
{ src: '/img/placeholder-square.svg', alt: 'Portrait', caption: 'Lisbon, Portugal' },
|
|
20
|
-
{ src: '/img/placeholder-tall.svg', alt: 'Urban', caption: 'Tokyo, Japan' },
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
export default {
|
|
24
|
-
route: '/components/gallery',
|
|
25
|
-
meta: {
|
|
26
|
-
title: 'Gallery — Pulse Docs',
|
|
27
|
-
description: 'Responsive image gallery with grid, strip, and masonry layouts for Pulse UI.',
|
|
28
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
29
|
-
},
|
|
30
|
-
view: () => renderComponentPage({
|
|
31
|
-
currentHref: '/components/gallery',
|
|
32
|
-
prev,
|
|
33
|
-
next,
|
|
34
|
-
name: 'gallery',
|
|
35
|
-
description: 'Responsive image gallery. Three layout modes: <code>grid</code> (equal aspect-ratio cells), <code>strip</code> (single horizontally scrollable row), and <code>masonry</code> (Pinterest-style varying heights). All images are lazy-loaded.',
|
|
36
|
-
content: `
|
|
37
|
-
<h2 class="doc-h2" id="grid">Grid layout</h2>
|
|
38
|
-
<p>Equal-sized cells with a consistent aspect ratio. Good for portfolios and photo libraries.</p>
|
|
39
|
-
${demo(
|
|
40
|
-
gallery({ images: PHOTOS, layout: 'grid', cols: 3, ratio: '4/3' }),
|
|
41
|
-
`gallery({
|
|
42
|
-
images: photos.map(p => ({ src: p.url, alt: p.alt })),
|
|
43
|
-
layout: 'grid',
|
|
44
|
-
cols: 3,
|
|
45
|
-
ratio: '4/3',
|
|
46
|
-
})`
|
|
47
|
-
)}
|
|
48
|
-
|
|
49
|
-
<h2 class="doc-h2" id="strip">Strip layout</h2>
|
|
50
|
-
<p>A single horizontally scrollable row — ideal for a quick photo preview or a scrolling carousel of product shots.</p>
|
|
51
|
-
${demo(
|
|
52
|
-
gallery({ images: PHOTOS, layout: 'strip', ratio: '4/3' }),
|
|
53
|
-
`gallery({
|
|
54
|
-
images: photos.map(p => ({ src: p.url, alt: p.alt })),
|
|
55
|
-
layout: 'strip',
|
|
56
|
-
ratio: '4/3',
|
|
57
|
-
})`
|
|
58
|
-
)}
|
|
59
|
-
|
|
60
|
-
<h2 class="doc-h2" id="masonry">Masonry layout</h2>
|
|
61
|
-
<p>Varying heights — images render at their natural ratio. Best for editorial photography where portrait and landscape images are mixed.</p>
|
|
62
|
-
${demo(
|
|
63
|
-
gallery({ images: PHOTOS, layout: 'masonry', cols: 3 }),
|
|
64
|
-
`gallery({
|
|
65
|
-
images: photos.map(p => ({ src: p.url, alt: p.alt })),
|
|
66
|
-
layout: 'masonry',
|
|
67
|
-
cols: 3,
|
|
68
|
-
})`
|
|
69
|
-
)}
|
|
70
|
-
|
|
71
|
-
<h2 class="doc-h2" id="captions">Captions</h2>
|
|
72
|
-
<p>Add a <code>caption</code> to any image in the array — rendered as a <code><figcaption></code>.</p>
|
|
73
|
-
${demo(
|
|
74
|
-
gallery({ images: CAPTIONED, layout: 'grid', cols: 3, ratio: '4/3' }),
|
|
75
|
-
`gallery({
|
|
76
|
-
images: [
|
|
77
|
-
{ src: '/img/landscape.jpg', alt: 'Mountain', caption: 'Glen Coe, Scotland' },
|
|
78
|
-
{ src: '/img/portrait.jpg', alt: 'Street', caption: 'Lisbon, Portugal' },
|
|
79
|
-
],
|
|
80
|
-
layout: 'grid',
|
|
81
|
-
cols: 3,
|
|
82
|
-
ratio: '4/3',
|
|
83
|
-
})`
|
|
84
|
-
)}
|
|
85
|
-
|
|
86
|
-
<h2 class="doc-h2" id="linked">Linked images</h2>
|
|
87
|
-
<p>Add an <code>href</code> to any image to wrap it in a link.</p>
|
|
88
|
-
|
|
89
|
-
${table(
|
|
90
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
91
|
-
[
|
|
92
|
-
['<code>images</code>', 'object[]', '[]', 'Array of image objects: <code>{ src, alt, caption?, href? }</code>'],
|
|
93
|
-
['<code>layout</code>', 'string', "'grid'", "'grid' · 'strip' · 'masonry'"],
|
|
94
|
-
['<code>cols</code>', 'number', '3', 'Columns in grid/masonry (1–4)'],
|
|
95
|
-
['<code>ratio</code>', 'string', "'4/3'", "CSS aspect-ratio for grid/strip cells — e.g. <code>'16/9'</code>, <code>'1/1'</code>. Ignored in masonry."],
|
|
96
|
-
['<code>gap</code>', 'string', "'md'", "'sm' · 'md' · 'lg'"],
|
|
97
|
-
['<code>rounded</code>', 'boolean', 'false', 'Adds border-radius to each image'],
|
|
98
|
-
]
|
|
99
|
-
)}
|
|
100
|
-
`,
|
|
101
|
-
}),
|
|
102
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table } from '../../lib/layout.js'
|
|
4
|
-
import { list } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/list')
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
route: '/components/list',
|
|
10
|
-
meta: {
|
|
11
|
-
title: 'List — Pulse Docs',
|
|
12
|
-
description: 'Styled ordered and unordered list component for Pulse UI.',
|
|
13
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
-
},
|
|
15
|
-
view: () => renderComponentPage({
|
|
16
|
-
currentHref: '/components/list',
|
|
17
|
-
prev,
|
|
18
|
-
next,
|
|
19
|
-
name: 'list',
|
|
20
|
-
description: 'Styled unordered or ordered list with consistent spacing and colour tokens. Use this instead of raw <code><ul></code> / <code><ol></code>. Items are HTML strings — other components can be passed as items.',
|
|
21
|
-
content: `
|
|
22
|
-
|
|
23
|
-
<h2 class="doc-h2" id="unordered">Unordered</h2>
|
|
24
|
-
${demo(
|
|
25
|
-
list({ items: ['Streaming SSR on every page', 'Security headers by default', 'Zero client-side dependencies', 'Lighthouse 100 out of the box'] }),
|
|
26
|
-
`list({
|
|
27
|
-
items: [
|
|
28
|
-
'Streaming SSR on every page',
|
|
29
|
-
'Security headers by default',
|
|
30
|
-
'Zero client-side dependencies',
|
|
31
|
-
'Lighthouse 100 out of the box',
|
|
32
|
-
],
|
|
33
|
-
})`,
|
|
34
|
-
{ col: true }
|
|
35
|
-
)}
|
|
36
|
-
|
|
37
|
-
<h2 class="doc-h2" id="ordered">Ordered</h2>
|
|
38
|
-
${demo(
|
|
39
|
-
list({
|
|
40
|
-
ordered: true,
|
|
41
|
-
items: ['Create a new Pulse project', 'Add your pages to <code>src/pages/</code>', 'Run <code>pulse dev</code> to start the server', 'Deploy when ready'],
|
|
42
|
-
}),
|
|
43
|
-
`list({
|
|
44
|
-
ordered: true,
|
|
45
|
-
items: [
|
|
46
|
-
'Create a new Pulse project',
|
|
47
|
-
'Add your pages to src/pages/',
|
|
48
|
-
'Run pulse dev to start the server',
|
|
49
|
-
'Deploy when ready',
|
|
50
|
-
],
|
|
51
|
-
})`,
|
|
52
|
-
{ col: true }
|
|
53
|
-
)}
|
|
54
|
-
|
|
55
|
-
<h2 class="doc-h2" id="gap">Gap</h2>
|
|
56
|
-
<p>Control vertical spacing between items with the <code>gap</code> prop.</p>
|
|
57
|
-
${demo(
|
|
58
|
-
`<div class="u-flex u-gap-8">
|
|
59
|
-
<div>
|
|
60
|
-
<p class="u-text-muted u-text-sm u-mb-2">xs</p>
|
|
61
|
-
${list({ gap: 'xs', items: ['Design', 'Build', 'Ship'] })}
|
|
62
|
-
</div>
|
|
63
|
-
<div>
|
|
64
|
-
<p class="u-text-muted u-text-sm u-mb-2">sm (default)</p>
|
|
65
|
-
${list({ gap: 'sm', items: ['Design', 'Build', 'Ship'] })}
|
|
66
|
-
</div>
|
|
67
|
-
<div>
|
|
68
|
-
<p class="u-text-muted u-text-sm u-mb-2">md</p>
|
|
69
|
-
${list({ gap: 'md', items: ['Design', 'Build', 'Ship'] })}
|
|
70
|
-
</div>
|
|
71
|
-
</div>`,
|
|
72
|
-
`list({ gap: 'xs', items: [...] })
|
|
73
|
-
list({ gap: 'sm', items: [...] }) // default
|
|
74
|
-
list({ gap: 'md', items: [...] })`,
|
|
75
|
-
{ col: true }
|
|
76
|
-
)}
|
|
77
|
-
|
|
78
|
-
<h2 class="doc-h2" id="rich-items">Items with markup</h2>
|
|
79
|
-
<p>Items are HTML strings — pass any markup including other components. Always escape user data before including it in item strings.</p>
|
|
80
|
-
${demo(
|
|
81
|
-
list({
|
|
82
|
-
items: [
|
|
83
|
-
`<strong>spec.state</strong> — initial client state, deep-cloned on mount`,
|
|
84
|
-
`<strong>spec.mutations</strong> — synchronous state updates`,
|
|
85
|
-
`<strong>spec.actions</strong> — async operations with lifecycle hooks`,
|
|
86
|
-
`<strong>spec.server</strong> — server-side data fetchers`,
|
|
87
|
-
],
|
|
88
|
-
}),
|
|
89
|
-
`list({
|
|
90
|
-
items: [
|
|
91
|
-
'<strong>spec.state</strong> — initial client state',
|
|
92
|
-
'<strong>spec.mutations</strong> — synchronous state updates',
|
|
93
|
-
'<strong>spec.actions</strong> — async operations with lifecycle hooks',
|
|
94
|
-
'<strong>spec.server</strong> — server-side data fetchers',
|
|
95
|
-
],
|
|
96
|
-
})`,
|
|
97
|
-
{ col: true }
|
|
98
|
-
)}
|
|
99
|
-
|
|
100
|
-
${table(
|
|
101
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
102
|
-
[
|
|
103
|
-
['<code>items</code>', 'string[]', '—', 'Array of HTML strings for each list item — escape user data before passing'],
|
|
104
|
-
['<code>ordered</code>', 'boolean', '<code>false</code>', '<code>false</code> renders <code><ul></code>, <code>true</code> renders <code><ol></code>'],
|
|
105
|
-
['<code>gap</code>', '<code>xs | sm | md</code>', '<code>sm</code>', 'Vertical spacing between items'],
|
|
106
|
-
['<code>class</code>', 'string', '—', 'Extra classes on the list element'],
|
|
107
|
-
]
|
|
108
|
-
)}
|
|
109
|
-
`,
|
|
110
|
-
}),
|
|
111
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table } from '../../lib/layout.js'
|
|
4
|
-
import { marquee } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/marquee')
|
|
7
|
-
|
|
8
|
-
const LOGOS = [
|
|
9
|
-
`<span style="font-weight:600;font-size:.875rem;opacity:.6;white-space:nowrap;letter-spacing:.5px">ACME CORP</span>`,
|
|
10
|
-
`<span style="font-weight:600;font-size:.875rem;opacity:.6;white-space:nowrap;letter-spacing:.5px">NOVA INC</span>`,
|
|
11
|
-
`<span style="font-weight:600;font-size:.875rem;opacity:.6;white-space:nowrap;letter-spacing:.5px">STELLAR</span>`,
|
|
12
|
-
`<span style="font-weight:600;font-size:.875rem;opacity:.6;white-space:nowrap;letter-spacing:.5px">ORBIT CO</span>`,
|
|
13
|
-
`<span style="font-weight:600;font-size:.875rem;opacity:.6;white-space:nowrap;letter-spacing:.5px">HELIX</span>`,
|
|
14
|
-
`<span style="font-weight:600;font-size:.875rem;opacity:.6;white-space:nowrap;letter-spacing:.5px">PRISM AI</span>`,
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
export default {
|
|
18
|
-
route: '/components/marquee',
|
|
19
|
-
meta: {
|
|
20
|
-
title: 'Marquee — Pulse Docs',
|
|
21
|
-
description: 'CSS-only infinite scrolling strip for logo clouds, trust badges, and tag lists in Pulse UI.',
|
|
22
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
23
|
-
},
|
|
24
|
-
view: () => renderComponentPage({
|
|
25
|
-
currentHref: '/components/marquee',
|
|
26
|
-
prev,
|
|
27
|
-
next,
|
|
28
|
-
name: 'marquee',
|
|
29
|
-
description: 'CSS-only infinite scrolling strip. No JavaScript — pure CSS animation. Use for logo clouds, press mentions, trust badges, or feature tags. <strong>The strip is hidden from screen readers</strong> (<code>aria-hidden="true"</code>) — it is decorative, not content.',
|
|
30
|
-
content: `
|
|
31
|
-
<h2 class="doc-h2" id="logos">Logo cloud</h2>
|
|
32
|
-
<p>A common use-case: a strip of client or partner logos scrolling slowly from right to left.</p>
|
|
33
|
-
${demo(
|
|
34
|
-
marquee({ items: LOGOS }),
|
|
35
|
-
`marquee({
|
|
36
|
-
items: ['<img src="/logos/acme.svg" alt="">',
|
|
37
|
-
'<img src="/logos/nova.svg" alt="">',
|
|
38
|
-
'<img src="/logos/helix.svg" alt="">'],
|
|
39
|
-
})`
|
|
40
|
-
)}
|
|
41
|
-
|
|
42
|
-
<h2 class="doc-h2" id="slow">Custom speed</h2>
|
|
43
|
-
<p>The default <code>speed</code> is 30 seconds per loop. Slow it down for fewer items, or speed it up for a more energetic feel.</p>
|
|
44
|
-
${demo(
|
|
45
|
-
marquee({ items: LOGOS, speed: 60 }),
|
|
46
|
-
`marquee({ items, speed: 60 }) // slow`
|
|
47
|
-
)}
|
|
48
|
-
|
|
49
|
-
<h2 class="doc-h2" id="direction">Direction</h2>
|
|
50
|
-
<p>Set <code>direction: 'right'</code> to scroll right-to-left instead.</p>
|
|
51
|
-
${demo(
|
|
52
|
-
marquee({ items: LOGOS, direction: 'right' }),
|
|
53
|
-
`marquee({ items, direction: 'right' })`
|
|
54
|
-
)}
|
|
55
|
-
|
|
56
|
-
<h2 class="doc-h2" id="no-fade">Without fade</h2>
|
|
57
|
-
<p>Edge fading is on by default. Disable it with <code>fade: false</code>.</p>
|
|
58
|
-
${demo(
|
|
59
|
-
marquee({ items: LOGOS, fade: false }),
|
|
60
|
-
`marquee({ items, fade: false })`
|
|
61
|
-
)}
|
|
62
|
-
|
|
63
|
-
${table(
|
|
64
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
65
|
-
[
|
|
66
|
-
['<code>items</code>', 'string[]', '[]', 'Array of raw HTML strings — images, badges, or text'],
|
|
67
|
-
['<code>speed</code>', 'number', '30', 'Duration (seconds) for one full loop. Lower = faster.'],
|
|
68
|
-
['<code>gap</code>', 'string', "'lg'", "'sm' · 'md' · 'lg'"],
|
|
69
|
-
['<code>direction</code>', 'string', "'left'", "'left' (right-to-left scroll) or 'right'"],
|
|
70
|
-
['<code>pause</code>', 'boolean', 'true', 'Pause animation on hover'],
|
|
71
|
-
['<code>fade</code>', 'boolean', 'true', 'Apply gradient mask to fade the left and right edges'],
|
|
72
|
-
]
|
|
73
|
-
)}
|
|
74
|
-
`,
|
|
75
|
-
}),
|
|
76
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table } from '../../lib/layout.js'
|
|
4
|
-
import { photoCard } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/photo-card')
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
route: '/components/photo-card',
|
|
10
|
-
meta: {
|
|
11
|
-
title: 'Photo Card — Pulse Docs',
|
|
12
|
-
description: 'Polaroid-style photo card with optional tilt and caption for Pulse UI.',
|
|
13
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
-
},
|
|
15
|
-
view: () => renderComponentPage({
|
|
16
|
-
currentHref: '/components/photo-card',
|
|
17
|
-
prev,
|
|
18
|
-
next,
|
|
19
|
-
name: 'photoCard',
|
|
20
|
-
description: 'Polaroid-style photo card with a border, white padding, optional caption, and CSS tilt. Ideal for "meet the team" sections, testimonial portraits, and lifestyle photography.',
|
|
21
|
-
content: `
|
|
22
|
-
<h2 class="doc-h2" id="basic">Basic photo card</h2>
|
|
23
|
-
<p>A framed image with a white polaroid border.</p>
|
|
24
|
-
${demo(
|
|
25
|
-
`<div style="display:flex;gap:2rem;align-items:center;justify-content:center;flex-wrap:wrap;padding:2rem">` +
|
|
26
|
-
photoCard({ src: '/img/placeholder-square.svg', alt: 'Team member portrait', caption: 'Sarah, Designer' }) +
|
|
27
|
-
`</div>`,
|
|
28
|
-
`photoCard({
|
|
29
|
-
src: '/img/team/sarah.jpg',
|
|
30
|
-
alt: 'Sarah, Designer',
|
|
31
|
-
caption: 'Sarah, Designer',
|
|
32
|
-
})`
|
|
33
|
-
)}
|
|
34
|
-
|
|
35
|
-
<h2 class="doc-h2" id="tilt">With tilt</h2>
|
|
36
|
-
<p>Pass a <code>tilt</code> value (degrees) to rotate the card. Negative values lean left, positive values lean right. Useful for giving sections a hand-placed, organic feeling.</p>
|
|
37
|
-
${demo(
|
|
38
|
-
`<div style="display:flex;gap:2rem;align-items:center;justify-content:center;flex-wrap:wrap;padding:2rem">` +
|
|
39
|
-
photoCard({ src: '/img/placeholder-square.svg', alt: 'Team photo', caption: 'Andy, founder', tilt: -2 }) +
|
|
40
|
-
photoCard({ src: '/img/placeholder-tall.svg', alt: 'Office dog', caption: 'Biscuit, mascot', tilt: 1.5 }) +
|
|
41
|
-
photoCard({ src: '/img/placeholder-square.svg', alt: 'Team photo', caption: 'Jen, engineering', tilt: -1 }) +
|
|
42
|
-
`</div>`,
|
|
43
|
-
`photoCard({ src, alt: 'Andy, founder', caption: 'Andy, founder', tilt: -2 })
|
|
44
|
-
photoCard({ src, alt: 'Office dog', caption: 'Biscuit, mascot', tilt: 1.5 })
|
|
45
|
-
photoCard({ src, alt: 'Jen, engineering', caption: 'Jen, engineering', tilt: -1 })`
|
|
46
|
-
)}
|
|
47
|
-
|
|
48
|
-
<h2 class="doc-h2" id="ratios">Aspect ratios</h2>
|
|
49
|
-
<p>Set <code>ratio</code> to control the crop. <code>'1/1'</code> for square portraits, <code>'4/3'</code> for landscape shots.</p>
|
|
50
|
-
${demo(
|
|
51
|
-
`<div style="display:flex;gap:2rem;align-items:flex-start;justify-content:center;flex-wrap:wrap;padding:2rem">` +
|
|
52
|
-
photoCard({ src: '/img/placeholder-square.svg', alt: 'Square crop', caption: "ratio: '1/1'", ratio: '1/1', tilt: 1 }) +
|
|
53
|
-
photoCard({ src: '/img/placeholder-landscape.svg', alt: 'Landscape crop', caption: "ratio: '4/3'", ratio: '4/3', tilt: -1.5 }) +
|
|
54
|
-
photoCard({ src: '/img/placeholder-tall.svg', alt: 'Tall crop', caption: "ratio: '3/4'", ratio: '3/4', tilt: 2 }) +
|
|
55
|
-
`</div>`,
|
|
56
|
-
`photoCard({ src, alt: 'Square portrait', ratio: '1/1', tilt: 1 })
|
|
57
|
-
photoCard({ src, alt: 'Landscape photo', ratio: '4/3', tilt: -1.5 })
|
|
58
|
-
photoCard({ src, alt: 'Tall portrait', ratio: '3/4', tilt: 2 })`
|
|
59
|
-
)}
|
|
60
|
-
|
|
61
|
-
${table(
|
|
62
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
63
|
-
[
|
|
64
|
-
['<code>src</code>', 'string', '—', 'Image source URL'],
|
|
65
|
-
['<code>alt</code>', 'string', '—', 'Alt text — required for accessibility'],
|
|
66
|
-
['<code>caption</code>', 'string', '—', 'Caption text below the photo'],
|
|
67
|
-
['<code>tilt</code>', 'number', '0', 'CSS rotate degrees — negative leans left, positive leans right. 0 = no tilt'],
|
|
68
|
-
['<code>ratio</code>', 'string', "'4/3'", 'CSS aspect-ratio for the image crop'],
|
|
69
|
-
['<code>rounded</code>', 'boolean', 'false', 'Rounds the image corners inside the card frame'],
|
|
70
|
-
]
|
|
71
|
-
)}
|
|
72
|
-
`,
|
|
73
|
-
}),
|
|
74
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
-
import { prevNext } from '../../lib/nav.js'
|
|
3
|
-
import { table, section } from '../../lib/layout.js'
|
|
4
|
-
import { button, pricing } from '../../../../src/ui/index.js'
|
|
5
|
-
|
|
6
|
-
const { prev, next } = prevNext('/components/pricing')
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
route: '/components/pricing',
|
|
10
|
-
meta: {
|
|
11
|
-
title: 'Pricing — Pulse Docs',
|
|
12
|
-
description: 'Pricing component for Pulse UI.',
|
|
13
|
-
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
-
},
|
|
15
|
-
view: () => renderComponentPage({
|
|
16
|
-
currentHref: '/components/pricing',
|
|
17
|
-
prev,
|
|
18
|
-
next,
|
|
19
|
-
name: 'pricing',
|
|
20
|
-
description: 'Single plan card with a feature list and CTA. Set <code>highlighted: true</code> on the recommended plan — it gets an accent border and a floating <code>badge</code> label.',
|
|
21
|
-
content: `
|
|
22
|
-
${demo(
|
|
23
|
-
pricing({ name: 'Pro', price: '$9', period: '/month', description: 'For growing teams', features: ['Unlimited pages', 'Priority support', 'Custom domain'], highlighted: true, badge: 'Most popular', action: button({ label: 'Get started', fullWidth: true }) }),
|
|
24
|
-
`pricing({
|
|
25
|
-
name: 'Pro',
|
|
26
|
-
price: '$9',
|
|
27
|
-
period: '/month',
|
|
28
|
-
description: 'For growing teams',
|
|
29
|
-
features: ['Unlimited pages', 'Priority support', 'Custom domain'],
|
|
30
|
-
highlighted: true,
|
|
31
|
-
badge: 'Most popular',
|
|
32
|
-
action: button({ label: 'Get started', fullWidth: true }),
|
|
33
|
-
})`,
|
|
34
|
-
{ col: true }
|
|
35
|
-
)}
|
|
36
|
-
|
|
37
|
-
${section('multi-plan', 'Multi-plan layout')}
|
|
38
|
-
${demo(
|
|
39
|
-
`<div class="ui-pricing-grid ui-pricing-grid--cols-3">` +
|
|
40
|
-
pricing({ name: 'Free', price: '$0', period: '/month', description: 'For personal projects', features: ['3 pages', 'Community support'], action: button({ label: 'Get started', variant: 'secondary', fullWidth: true }) }) +
|
|
41
|
-
pricing({ name: 'Pro', price: '$9', period: '/month', description: 'For growing teams', features: ['Unlimited pages', 'Priority support', 'Custom domain'], highlighted: true, badge: 'Most popular', action: button({ label: 'Get started', fullWidth: true }) }) +
|
|
42
|
-
pricing({ name: 'Team', price: '$29', period: '/month', description: 'For large organisations', features: ['Everything in Pro', 'SSO', 'SLA'], action: button({ label: 'Get started', variant: 'secondary', fullWidth: true }) }) +
|
|
43
|
-
`</div>`,
|
|
44
|
-
`<div class="ui-pricing-grid ui-pricing-grid--cols-3">
|
|
45
|
-
\${pricing({ name: 'Free', price: '$0', ... })}
|
|
46
|
-
\${pricing({ name: 'Pro', price: '$9', highlighted: true, badge: 'Most popular', ... })}
|
|
47
|
-
\${pricing({ name: 'Team', price: '$29', ... })}
|
|
48
|
-
</div>`,
|
|
49
|
-
)}
|
|
50
|
-
|
|
51
|
-
${table(
|
|
52
|
-
['Prop', 'Type', 'Default', 'Description'],
|
|
53
|
-
[
|
|
54
|
-
['<code>name</code>', 'string', '—', ''],
|
|
55
|
-
['<code>level</code>', 'number', '3', 'Heading tag for the plan name (1–6). Visual style is unchanged.'],
|
|
56
|
-
['<code>price</code>', 'string', '—', 'Formatted string — e.g. "$9", "Free"'],
|
|
57
|
-
['<code>period</code>', 'string', '—', 'e.g. "/month", "/year"'],
|
|
58
|
-
['<code>description</code>', 'string', '—', ''],
|
|
59
|
-
['<code>features</code>', 'string[]', '[]', 'List of feature strings'],
|
|
60
|
-
['<code>action</code>', 'string (HTML)', '—', 'Raw HTML slot — typically a button()'],
|
|
61
|
-
['<code>highlighted</code>', 'boolean', 'false', 'Accent border + elevated appearance'],
|
|
62
|
-
['<code>badge</code>', 'string', '—', 'Floating label above the card'],
|
|
63
|
-
]
|
|
64
|
-
)}
|
|
65
|
-
`,
|
|
66
|
-
}),
|
|
67
|
-
}
|