@invisibleloop/pulse 0.20.4 → 0.20.6

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.
Files changed (80) hide show
  1. package/.claude/pulse-checklist.md +1 -2
  2. package/.github/workflows/ci.yml +3 -0
  3. package/CLAUDE.md +1 -2
  4. package/docs/.claude/pulse-checklist.md +1 -2
  5. package/docs/public/coutumes.css +21 -44
  6. package/docs/public/docs.css +67 -0
  7. package/docs/public/pulse-ui.css +8 -1037
  8. package/docs/public/retrowheels.css +23 -0
  9. package/docs/public/shop.css +35 -1
  10. package/docs/public/sparkys.css +56 -23
  11. package/docs/server.js +0 -11
  12. package/docs/src/lib/nav.js +0 -11
  13. package/docs/src/pages/components/charts.js +2 -1
  14. package/docs/src/pages/components/grid.js +9 -5
  15. package/docs/src/pages/components.js +0 -4
  16. package/docs/src/pages/home.js +16 -1
  17. package/docs/src/pages/how-it-works.js +20 -14
  18. package/docs/src/pages/templates/coutumes.js +83 -72
  19. package/docs/src/pages/templates/mobile-app.js +36 -9
  20. package/docs/src/pages/templates/retrowheels.js +17 -4
  21. package/docs/src/pages/templates/shop.js +21 -10
  22. package/docs/src/pages/templates/sparkys.js +35 -20
  23. package/examples/.claude/pulse-checklist.md +1 -2
  24. package/examples/examples.test.js +4 -4
  25. package/examples/pricing.js +21 -3
  26. package/examples/public/pulse-ui.css +8 -1037
  27. package/examples/retrowheels.css +23 -0
  28. package/examples/retrowheels.js +17 -4
  29. package/package.json +5 -1
  30. package/public/.pulse-ui-version +1 -1
  31. package/public/pulse-ui.css +8 -1037
  32. package/src/agent/checklist.md +1 -2
  33. package/src/agent/guide-components.md +43 -67
  34. package/src/agent/guide-design-gallery.md +14 -14
  35. package/src/agent/guide-design-references.md +21 -21
  36. package/src/agent/guide-styles.md +2 -2
  37. package/src/agent/guide-templates.md +6 -6
  38. package/src/agent/identity.md +1 -1
  39. package/src/agent/workflow.md +1 -2
  40. package/src/mcp/server.js +31 -24
  41. package/src/ui/index.js +0 -27
  42. package/src/ui/ui.test.js +0 -137
  43. package/types/charts.d.ts +61 -0
  44. package/types/ui.d.ts +1 -129
  45. package/docs/src/pages/components/banner.js +0 -44
  46. package/docs/src/pages/components/carousel.js +0 -71
  47. package/docs/src/pages/components/code-window.js +0 -56
  48. package/docs/src/pages/components/decorate.js +0 -74
  49. package/docs/src/pages/components/gallery.js +0 -102
  50. package/docs/src/pages/components/list.js +0 -111
  51. package/docs/src/pages/components/marquee.js +0 -76
  52. package/docs/src/pages/components/photo-card.js +0 -74
  53. package/docs/src/pages/components/pricing.js +0 -67
  54. package/docs/src/pages/components/testimonial.js +0 -48
  55. package/docs/src/pages/components/timeline.js +0 -226
  56. package/src/ui/banner.js +0 -41
  57. package/src/ui/brand-statement.js +0 -58
  58. package/src/ui/carousel.js +0 -59
  59. package/src/ui/code-window.js +0 -39
  60. package/src/ui/colophon.js +0 -29
  61. package/src/ui/contact-row.js +0 -41
  62. package/src/ui/decorate.js +0 -55
  63. package/src/ui/display-heading.js +0 -44
  64. package/src/ui/drop-cap.js +0 -29
  65. package/src/ui/editorial-hero.js +0 -63
  66. package/src/ui/entry-list.js +0 -36
  67. package/src/ui/gallery.js +0 -60
  68. package/src/ui/image-pair.js +0 -72
  69. package/src/ui/list.js +0 -36
  70. package/src/ui/marquee.js +0 -48
  71. package/src/ui/photo-card.js +0 -44
  72. package/src/ui/pricing.js +0 -56
  73. package/src/ui/product-card.js +0 -65
  74. package/src/ui/section-label.js +0 -38
  75. package/src/ui/service-card.js +0 -47
  76. package/src/ui/stat-grid.js +0 -40
  77. package/src/ui/testimonial.js +0 -59
  78. package/src/ui/timeline.js +0 -72
  79. package/src/ui/top-strip.js +0 -47
  80. package/src/ui/trust-list.js +0 -46
@@ -31,9 +31,8 @@ Before finishing any spec, verify every point below. Fix anything that fails.
31
31
  - Hero sections → `hero({ title, subtitle, actions, image, ... })`
32
32
  - Product/service cards → `card({ title, body, image, footer, ... })`
33
33
  - Image + text two-column layouts → `media({ image, content, reverse })`
34
- - Horizontal strips/banners → `banner({ content, variant })`
35
34
  - Feature tiles → `feature({ icon, title, description })`
36
- - Testimonials → `testimonial({ quote, name, role, src })`
35
+ - Testimonials → `card({ content, footer })` with quote + avatar/name in the content slot
37
36
  - CTAs → `cta({ title, subtitle, actions })`
38
37
  - Navigation → `nav({ logo, links, actions })`
39
38
  - Footers → `footer({ logo, links, columns, legal, ... })`
@@ -10,7 +10,7 @@ import { nav, hero, feature, button, card, stat, grid, section, container, iconZ
10
10
  meta: { styles: ['/pulse-ui.css', '/app.css'] }
11
11
  ```
12
12
 
13
- Interactive components (carousel, tooltip) also need `/pulse-ui.js` in `meta.scripts`. Modal is handled natively by the Pulse runtime — no extra script needed.
13
+ Interactive components (tooltip) also need `/pulse-ui.js` in `meta.scripts`. Modal is handled natively by the Pulse runtime — no extra script needed.
14
14
 
15
15
  ### Component root CSS classes
16
16
 
@@ -37,8 +37,6 @@ Use these when writing tests (`.get('.ui-card')`) or custom CSS overrides. The r
37
37
  | `stack` | `.ui-stack` | |
38
38
  | `cluster` | `.ui-cluster` | |
39
39
  | `feature` | `.ui-feature` | |
40
- | `testimonial` | `.ui-testimonial` | |
41
- | `pricing` | `.ui-pricing` | |
42
40
  | `accordion` | `.ui-accordion` | |
43
41
  | `cta` | `.ui-cta` | |
44
42
  | `footer` | `.ui-footer` | |
@@ -47,24 +45,11 @@ Use these when writing tests (`.get('.ui-card')`) or custom CSS overrides. The r
47
45
  | `progress` | `.ui-progress` | |
48
46
  | `breadcrumbs` | `.ui-breadcrumbs` | |
49
47
  | `stepper` | `.ui-stepper` | |
50
- | `timeline` | `.ui-timeline` | |
51
- | `carousel` | `.ui-carousel` | |
52
48
  | `tooltip` | `.ui-tooltip` | |
53
49
  | `appBadge` | `.ui-app-badge` | |
54
- | `banner` | `.ui-banner` | |
55
50
  | `uiImage` | `.ui-image` | |
56
- | `gallery` | `.ui-gallery` | |
57
- | `photoCard` | `.ui-photo-card` | |
58
- | `marquee` | `.ui-marquee` | |
59
- | `decorate` | `.ui-decorate` | Inline SVG — parent needs `position: relative` |
60
- | `codeWindow` | `.ui-code-window` | |
61
51
  | `segmented` | `.ui-segmented` | |
62
52
  | `rating` | `.ui-rating` | |
63
- | `entryList` | `.ui-entry-list` | Editorial — tabular list with metadata |
64
- | `displayHeading` | `.ui-display-heading` | Editorial — large typographic heading |
65
- | `sectionLabel` | `.ui-section-label` | Editorial — eyebrow + h2 + rule |
66
- | `dropCap` | `.ui-drop-cap` | Editorial — first-letter treatment |
67
- | `colophon` | `.ui-colophon` | Editorial — minimal footer |
68
53
 
69
54
 
70
55
 
@@ -73,7 +58,7 @@ Use these when writing tests (`.get('.ui-card')`) or custom CSS overrides. The r
73
58
  Server-rendered SVG charts — no JS, no dependencies. Drop into any card, section, or grid.
74
59
 
75
60
  ```js
76
- import { barChart, lineChart, donutChart, sparkline } from '@invisibleloop/pulse/ui'
61
+ import { barChart, lineChart, donutChart, sparkline } from '@invisibleloop/pulse/charts'
77
62
 
78
63
  barChart({
79
64
  data: [{ label: 'Jan', value: 42 }, { label: 'Feb', value: 78 }],
@@ -178,85 +163,76 @@ To add a missing icon: copy a Lucide path (MIT) into `src/ui/icons.js` using `s(
178
163
  | `breadcrumbs` | `items` ([{label,href}] — last item has no href), `separator` |
179
164
  | `stepper` | `steps` (array of label strings), `current` (0-based) |
180
165
  | `uiImage` | `src`, `alt`, `caption`, `ratio`, `rounded`, `pill`, `width`, `height`, `maxWidth` |
181
- | `gallery` | `images` ([{src,alt,caption?,href?}]), `layout` (grid/strip/masonry), `cols` (1–4), `gap` (sm/md/lg), `rounded`, `ratio` — responsive image grid |
182
- | `photoCard` | `src`, `alt`, `caption`, `tilt` (CSS rotate degrees, e.g. 2 or -1.5), `rounded`, `ratio` — polaroid-style card with optional tilt |
183
- | `marquee` | `items` (array of HTML strings), `speed` (seconds, default 30), `gap`, `direction` (left/right), `pause` (hover pause), `fade` — CSS-only infinite scroll strip. Ideal for logos, trust badges |
184
- | `decorate` | `pattern` (dots/grid/lines/zigzag/cross), `color`, `opacity`, `size` — inline SVG overlay. Parent needs `position: relative` (add class `u-relative`) |
185
166
  | `pullquote` | `quote`, `cite`, `size` (md/lg) |
186
- | `timeline` | `direction` (vertical/horizontal), `items` ([{dot,dotColor,label,content}]) |
187
- | `timelineItem` | `content`, `label`, `dot`, `dotColor` (accent/success/warning/error/muted) |
167
+
168
+ Image galleries, photo grids, logo/trust marquees, and timelines have no dedicated component — build them with `grid()` / `stack()` + `uiImage()` and raw HTML (creative override), or a CSS-only scrolling strip for marquee-style content.
188
169
 
189
170
  ### Typography components
190
171
 
191
172
  | Component | Key props |
192
173
  |-----------|-----------|
193
174
  | `heading` | `level` (1–6), `text`, `size` (xs–4xl, overrides level default), `color` (default/muted/accent), `balance` (true — prevents orphaned words) |
194
- | `list` | `items` (array of HTML strings), `ordered`, `gap` (xs/sm/md) |
195
175
  | `prose` | `content` (raw HTML — not escaped), `size` (sm/base/lg) — for CMS/markdown content you don't control |
196
176
 
197
- **Never write raw `<h1>`–`<h6>` or `<ul>`/`<ol>` without styling.** Use `heading()` and `list()` instead. *(Mode A only — in a creative override, raw headings styled directly with utility classes or custom CSS are correct. The `heading()` component adds wrapper markup that can fight custom layouts.)*
177
+ **Never write raw `<h1>`–`<h6>` without styling.** Use `heading()` instead. *(Mode A only — in a creative override, raw headings styled directly with utility classes or custom CSS are correct. The `heading()` component adds wrapper markup that can fight custom layouts.)* There is no dedicated list component — write `<ul>`/`<ol>` directly, styled with utility classes or `app.css`.
198
178
 
199
- **Heading order across components.** `card`, `feature`, `section`, `pricing`, `cta`, and `modal` all accept a `level` prop. Use it to keep the document outline correct without changing visual style. For example, if a page has an h2 section title and each card inside it has a title, pass `level: 3` on the cards (the default) — but if the cards are the first heading on the page, pass `level: 2`. The CSS class stays the same (`ui-card-title` etc.) so appearance is unaffected.
179
+ **Heading order across components.** `card`, `feature`, `section`, `cta`, and `modal` all accept a `level` prop. Use it to keep the document outline correct without changing visual style. For example, if a page has an h2 section title and each card inside it has a title, pass `level: 3` on the cards (the default) — but if the cards are the first heading on the page, pass `level: 2`. The CSS class stays the same (`ui-card-title` etc.) so appearance is unaffected.
200
180
 
201
181
  **`balance: true`** on `heading()` prevents orphaned words. Apply to any heading the verification orphan check flags.
202
182
 
203
- Use `prose()` for HTML from external sources (CMS, markdown). Use `heading()`/`list()` when writing content yourself.
204
-
205
- ### Editorial components
206
-
207
- Editorial/typographic primitives for non-SaaS layouts: paperback books, vintage magazines, editorial sites, portfolio pages. These complement the landing-page-focused components (hero, feature, cta) when the design needs structure that's tabular, asymmetric, or typography-led.
208
-
209
- | Component | Key props | Use for |
210
- |-----------|-----------|---------|
211
- | `entryList` | `items` ([{meta, title, description}]), `metaLabel` (accessible name for meta column — e.g. "Year") | Backlists, release notes, changelogs, project archives, talk logs |
212
- | `displayHeading` | `text`, `level` (1–6), `maxWidth` (ch units, default 20), `balance` (true), `tracking` ('tight'\|'normal'\|'wide') | Hero titles, chapter openings, large typographic moments |
213
- | `sectionLabel` | `eyebrow`, `heading`, `level` (1–6, default 2), `rule` (true — show horizontal rule), `align` ('left'\|'center') | Section dividers, chapter labels, content blocks |
214
- | `dropCap` | `letter` (the drop cap letter or word), `content` (rest of paragraph), `lines` (height in lines, default 3) | First-paragraph treatment, editorial openings |
215
- | `colophon` | `content` (HTML slot), `align` ('center'\|'left') | Paperback-style footer — minimal, tight type, no columns |
183
+ Use `prose()` for HTML from external sources (CMS, markdown). Use `heading()` when writing headings yourself.
216
184
 
217
- #### When to use editorial components
185
+ ### Editorial / typographic layouts
218
186
 
219
- Use these when the design direction is **editorial**, **retro**, **brutalist**, or **paper** vibe, or when the content structure is naturally tabular (list of books, releases, projects). They're shaped for asymmetry and typography-first layouts, not centred hero + three-column features.
187
+ There is no dedicated editorial component family — for non-SaaS layouts (paperback books, vintage magazines, editorial sites, portfolio pages) needing structure that's tabular, asymmetric, or typography-led, compose from `heading()`, `prose()`, `pullquote()`, `divider()`, and raw HTML styled with utility classes or `app.css` (creative override).
220
188
 
221
- **Example: Book backlist**
189
+ **Example: Book backlist** — a tabular list of items, built with raw HTML inside `stack()`:
222
190
  ```js
223
- entryList({
224
- metaLabel: 'Year',
225
- items: [
226
- { meta: '2023', title: 'The Argent Ghost', description: 'A tale of time-travel and lost identity.' },
227
- { meta: '2021', title: 'Signal Drift', description: 'First contact from a dead civilization.' },
228
- { meta: '2019', title: 'Cold Equations', description: 'Survival at the edge of known space.' },
229
- ],
191
+ stack({
192
+ gap: 'md',
193
+ content: books.map(b => `
194
+ <div class="book-entry">
195
+ <span class="book-entry-year">${b.year}</span>
196
+ <div>
197
+ <h3>${b.title}</h3>
198
+ <p>${b.description}</p>
199
+ </div>
200
+ </div>
201
+ `).join(''),
230
202
  })
231
203
  ```
232
204
 
233
- **Example: Editorial hero**
205
+ **Example: Editorial hero** — a large display title with a custom-styled heading, and a first-paragraph drop-cap treatment via CSS (`::first-letter`) rather than a component:
234
206
  ```js
235
- ${displayHeading({ text: 'In Search of Lost Code', level: 1, tracking: 'tight' })}
236
- ${dropCap({ letter: 'T', content: 'he first computers were humans, pencil-wielding calculators in windowless rooms, computing artillery trajectories by hand...' })}
207
+ ${heading({ level: 1, text: 'In Search of Lost Code', class: 'display-heading' })}
208
+ <p class="drop-cap">The first computers were humans, pencil-wielding calculators in windowless rooms, computing artillery trajectories by hand...</p>
237
209
  ```
238
210
 
239
- **Example: Section divider**
211
+ **Example: Section divider** — an eyebrow + heading + rule, built directly:
240
212
  ```js
241
- sectionLabel({ eyebrow: 'Chapter 3', heading: 'The Rise of Symbolic Logic', rule: true })
213
+ `<div class="section-label">
214
+ <p class="section-label-eyebrow">Chapter 3</p>
215
+ ${heading({ level: 2, text: 'The Rise of Symbolic Logic' })}
216
+ ${divider()}
217
+ </div>`
242
218
  ```
243
219
 
244
- **Example: Minimal footer for an editorial page**
220
+ **Example: Minimal footer for an editorial page** — plain HTML rather than `footer()`, when the page wants a tight, columnless colophon:
245
221
  ```js
246
- colophon({
247
- content: `<p>© 2025 Alden Press • Set in Lyon Text and Atlas Grotesk</p>
248
- <p><a href="/about">About</a> • <a href="/contact">Contact</a></p>`,
249
- })
222
+ `<footer class="colophon">
223
+ <p>© 2025 Alden Press • Set in Lyon Text and Atlas Grotesk</p>
224
+ <p><a href="/about">About</a> • <a href="/contact">Contact</a></p>
225
+ </footer>`
250
226
  ```
251
227
 
252
- **Composition:** Editorial components work inside `section()` + `container()` like any other Pulse component. Use `prose()` for body text, `sectionLabel()` for section breaks, `entryList()` for tabular content, `displayHeading()` for large type moments, and `colophon()` instead of `footer()` for editorial pages.
228
+ **Composition:** These layouts work inside `section()` + `container()` like any other Pulse component. Use `prose()` for body text, `heading()` for large type moments, and custom CSS classes (defined in `app.css` with tokens from `theme.css`) for editorial-specific presentation.
253
229
 
254
230
  #### When components feel restrictive
255
231
 
256
232
  **Symptom:** You're fighting a component's wrapper styles (padding, max-width, centering) in your CSS overrides.
257
233
 
258
234
  **Solutions:**
259
- 1. **Use the right component for your design.** If the page is editorial/typographic, use the editorial components (`displayHeading`, `sectionLabel`, `entryList`, `colophon`) instead of trying to force `hero()`, `feature()`, `cta()` into an editorial shape.
235
+ 1. **Use the right approach for your design.** If the page is editorial/typographic, compose from `heading()`, `prose()`, and raw HTML (creative override) instead of trying to force `hero()`, `feature()`, `cta()` into an editorial shape.
260
236
  2. **Write custom markup for structural differences.** Components provide sensible defaults for common patterns. When your design structure diverges significantly (asymmetric grid, full-bleed sections, custom positioning), write the HTML directly — that's permitted for layouts. Use components for the UI primitives (`button`, `card`, `input`, `badge`), but write the outer structure yourself.
261
237
  3. **Override with higher specificity.** Component wrappers use single class names (`.ui-hero`, `.ui-cta`). Override with a more specific selector in your `app.css`:
262
238
  ```css
@@ -276,9 +252,9 @@ For example:
276
252
  - `hero()` with `vibe: 'editorial'` still renders eyebrow + title + subtitle + actions (SaaS structure)
277
253
  - The vibe adjusts the font sizes, tracking, and spacing, but the component HTML is unchanged
278
254
 
279
- **If you need different HTML structure for an editorial design** — e.g., a large display title + asymmetric layout + drop cap instead of eyebrow/subtitle/actions — use the editorial components (`displayHeading`, `dropCap`, `sectionLabel`) or write custom markup. Do not expect `hero()` to become structurally editorial when `vibe: 'editorial'` is set.
255
+ **If you need different HTML structure for an editorial design** — e.g., a large display title + asymmetric layout + drop cap instead of eyebrow/subtitle/actions — write custom markup styled with `heading()`/`prose()` plus utility classes or `app.css`. Do not expect `hero()` to become structurally editorial when `vibe: 'editorial'` is set.
280
256
 
281
- This is by design: vibes are a paint job, not a structural transformation. Components stay predictable and testable. When the design structure itself differs from the SaaS patterns (centred hero, three-column features, pricing cards), reach for the editorial components or custom layout instead of trying to force `hero()` / `feature()` / `cta()` into shapes they weren't designed for.
257
+ This is by design: vibes are a paint job, not a structural transformation. Components stay predictable and testable. When the design structure itself differs from the SaaS patterns (centred hero, three-column features, pricing cards), reach for custom layout instead of trying to force `hero()` / `feature()` / `cta()` into shapes they weren't designed for.
282
258
 
283
259
  ### Landing page components
284
260
 
@@ -288,12 +264,12 @@ This is by design: vibes are a paint job, not a structural transformation. Compo
288
264
  | `hero` | `eyebrow`, `title`, `subtitle`, `actions` (HTML slot), `image` (HTML slot — activates image layout), `imageAlign` (right/left), `align` (center/left), `size` (md/sm), `layout` (split/asymmetric/overlap — when image provided: 'split'=50/50, 'asymmetric'=60/40 text-heavy, 'overlap'=image fills section with text overlay), `gradient` (true/false or preset: 'purple'\|'blue'\|'green'\|'rose'\|'orange'), `background` (any CSS value), `eyebrowColor` |
289
265
  | `phoneFrame` | `content` (HTML slot — rendered inside the phone screen), `animate` (boolean, default `false` — enables a gentle 3-D tilt on hover). Pure CSS phone mockup with dynamic island, side buttons, home indicator. Use as the `image` slot in `hero()` for app landing pages. Add `animate: true` whenever the phone is a prominent hero element. |
290
266
  | `feature` | `image` (HTML slot — rendered above icon), `icon` (HTML slot), `title`, `level` (1–6, default 3), `description` (or `body` as alias), `center` |
291
- | `testimonial` | `quote`, `name`, `role`, `src`, `rating` (1–5) |
292
- | `pricing` | `name`, `level` (1–6, default 3), `price`, `period`, `features` ([strings]), `action` (HTML slot), `highlighted` |
293
267
  | `accordion` | `items` ([{question,answer}]) — native `<details>`, no JS |
294
268
  | `appBadge` | `store` (apple/google), `href` — **always use this for App Store / Google Play download buttons. Never write a raw `<a>` with an SVG badge image.** Designed to sit in a `hero()` `actions` slot or anywhere a download CTA is needed. |
295
269
  | `cta` | `eyebrow`, `title`, `level` (1–6, default 2), `subtitle` (or `body` as alias), `actions` (HTML slot), `align` (center/left) |
296
270
 
271
+ There is no dedicated `testimonial` or `pricing` component. Build a testimonial with `card({ content, footer })` — the quote in `content`, an `avatar()` + name/role in `footer`. Build a pricing table with `grid()` of `card({ title, content, footer })` tiles — one card per plan, `variant: 'elevated'` on the highlighted plan.
272
+
297
273
  ### Layout components
298
274
 
299
275
  | Component | Key props |
@@ -304,11 +280,11 @@ This is by design: vibes are a paint job, not a structural transformation. Compo
304
280
  | `stack` | `content`, `gap` (xs–xl), `align` — flex column |
305
281
  | `cluster` | `content`, `gap`, `justify`, `align` — flex row with wrap |
306
282
  | `divider` | `label` |
307
- | `banner` | `content`, `variant` |
308
283
  | `media` | `image` (HTML slot), `content` (HTML slot), `reverse` — two-column image + text |
309
- | `codeWindow` | `content` (highlighted code HTML), `filename`, `lang` |
310
284
  | `footer` | `logo`, `logoHref`, `links` (flat: `[{label,href}]`), `legal`, `background`, `color` — **Rich layout:** add `columns: [{title, links:[{label,href}]}]` to switch to multi-column mode; also accepts `subscribe` (HTML slot), `wordmark` (giant display text) |
311
285
 
286
+ There is no dedicated `banner` (thin announcement strip) or `codeWindow` (syntax-highlighted code frame) component. Build a banner as a small raw `<div>` strip styled with utility classes. Build a code window with a raw `<pre><code>` block (creative override), styled via `app.css`.
287
+
312
288
  ### Modal / dialog
313
289
 
314
290
  Use `modal()` + `modalTrigger()` (or `data-dialog-open`). **Never use `state.modalOpen` or conditional rendering** — this destroys the dialog element on every render, breaking focus, animation, and native ESC.
@@ -10,9 +10,9 @@ A curated catalogue of ready-to-use design patterns. Each entry names the live t
10
10
  **Route:** `/templates/local-business`
11
11
  **Vibe:** `warm` **Theme:** `light` **CSS:** `lumio.css`
12
12
 
13
- Warm cream background, hand-crafted feel. Photo-forward hero with a split layout (text left, dog/product image right). Earthy greens and amber for CTAs. Services grid of 6 cards with price footers. Two-column about section with credentials checklist. Three-column testimonials. Contact section with location/hours sidebar and inline booking form. Stat strip showing social proof numbers.
13
+ Warm cream background, hand-crafted feel. Photo-forward hero with a split layout (text left, dog/product image right). Earthy greens and amber for CTAs. Services grid of 6 cards with price footers. Two-column about section with credentials checklist. Three-column testimonials (built with `card`). Contact section with location/hours sidebar and inline booking form. Stat strip showing social proof numbers.
14
14
 
15
- **Key components:** `hero({ layout:'split' })`, `section`, `container`, `grid`, `card` (with `footer`), `stat`, `testimonial`, `cta`, `badge`
15
+ **Key components:** `hero({ layout:'split' })`, `section`, `container`, `grid`, `card` (with `footer`), `stat`, `cta`, `badge`
16
16
 
17
17
  **Distinguishing moves:**
18
18
  - Gradient hero `linear-gradient(135deg, #fdf6ee, #fdebd0)` with `color:#3d2b1e` — define these hex values as tokens in `public/theme.css` and reference them via `var()`; the lint hook blocks raw hex in any other stylesheet
@@ -47,14 +47,14 @@ Dark, editorial, high-contrast. Works grid with cover images (fluid fill via `fl
47
47
  **Route:** `/templates/event`
48
48
  **Vibe:** `bold` **Theme:** `dark` **CSS:** *(none extra — dark theme built-in)*
49
49
 
50
- Dark navy with violet/purple gradient CTAs. Centred hero with countdown-style stats strip. Sponsor logo marquee. Speaker portrait grid (4 columns, `flush:true` square images). Schedule as an accordion (3 day items, each with time/stage timetable). Venue section with two-column layout (map + details). Ticket pricing highlight.
50
+ Dark navy with violet/purple gradient CTAs. Centred hero with countdown-style stats strip. Sponsor logo strip (raw HTML, CSS-only scroll animation). Speaker portrait grid (4 columns, `flush:true` square images). Schedule as an accordion (3 day items, each with time/stage timetable). Venue section with two-column layout (map + details). Ticket pricing highlight.
51
51
 
52
- **Key components:** `hero`, `section`, `container`, `grid({ cols:4 })`, `card({ flush:true })`, `accordion`, `marquee`, `badge`, `stat`, `cta`
52
+ **Key components:** `hero`, `section`, `container`, `grid({ cols:4 })`, `card({ flush:true })`, `accordion`, `badge`, `stat`, `cta`
53
53
 
54
54
  **Distinguishing moves:**
55
55
  - Speaker cards: `aspect-ratio:1/1` square portrait images + name/role/quote below
56
56
  - Accordion items: `{ question: 'Day 1 — ...', answer: '<div>...timetable rows...</div>' }`
57
- - Marquee for sponsor logos: `marquee({ items: [...], gap:'xl', speed:'slow' })`
57
+ - Sponsor logo strip: raw HTML `cluster()` of logos, or a CSS `@keyframes` scroll animation for an infinite marquee effect (creative override — no dedicated component)
58
58
  - Stats strip: `section({ variant:'alt', padding:'sm' })` + `grid({ cols:4 })`
59
59
 
60
60
  **Best for:** Conference, hackathon, festival, product launch, awards ceremony, summit
@@ -83,9 +83,9 @@ Dark with serif headline typography. Featured article hero with byline, read-tim
83
83
  **Route:** `/templates/mobile-app`
84
84
  **Vibe:** `bold` or `minimal` **Theme:** `dark` **CSS:** *(dark theme built-in)*
85
85
 
86
- Hero with phone-frame mockup, feature grid, pricing section, testimonials, and App Store badges. The centrepiece is a `phoneFrame` component wrapping a screenshot or animated mockup.
86
+ Hero with phone-frame mockup, feature grid, pricing section, testimonials, and App Store badges. The centrepiece is a `phoneFrame` component wrapping a screenshot or animated mockup. Pricing tiers and testimonials are built from `grid()` of `card()` tiles (no dedicated `pricing`/`testimonial` component).
87
87
 
88
- **Key components:** `hero`, `phoneFrame`, `feature`, `pricing`, `testimonial`, `appBadge`, `cta`
88
+ **Key components:** `hero`, `phoneFrame`, `feature`, `grid`, `card`, `appBadge`, `cta`
89
89
 
90
90
  **Best for:** iOS/Android app, SaaS with mobile client, consumer product launch
91
91
 
@@ -174,14 +174,15 @@ section({
174
174
  })
175
175
  ```
176
176
 
177
- ### Sponsor Marquee
177
+ ### Sponsor Logo Strip
178
+ No dedicated marquee component — use `cluster()` for a static wrapped row, or raw HTML with a CSS `@keyframes` scroll animation for an infinite marquee effect (creative override):
178
179
  ```js
179
- marquee({
180
- items: ['Acme Corp', 'Globex', 'Initech', 'Umbrella', 'Hooli'].map(
180
+ cluster({
181
+ gap: 'xl',
182
+ justify: 'center',
183
+ content: ['Acme Corp', 'Globex', 'Initech', 'Umbrella', 'Hooli'].map(
181
184
  name => `<span class="u-font-semibold u-text-lg u-opacity-60">${name}</span>`
182
- ),
183
- speed: 'slow',
184
- gap: 'xl',
185
+ ).join(''),
185
186
  })
186
187
  ```
187
188
 
@@ -251,7 +252,6 @@ The most common mistakes when writing templates. These will silently produce emp
251
252
  | `card` | `body:` | `content:` |
252
253
  | `card` | `image:` *(unsupported)* | Put `<img>` inside `content:` |
253
254
  | `card` | `href:` *(unsupported)* | Wrap title in `<a>` inside `content:` |
254
- | `testimonial` | `author:` | `name:` |
255
255
  | `pullquote` | `author:` | `cite:` |
256
256
  | `accordion` items | `title:` | `question:` |
257
257
  | `accordion` items | `content:` | `answer:` |
@@ -14,8 +14,8 @@ When you choose a design direction, tell the user which one you're using and why
14
14
  **Geometry:** tight radius (2–6px), mono grids, generous whitespace
15
15
  **Typography:** geometric sans, tight tracking on headings, strong size contrast
16
16
  **Palette pattern:** near-black bg + single accent (indigo / blue / violet)
17
- **Signature moves:** bento grid, floating nav on scroll, stat bar, code window
18
- **Components to lead with:** `hero(layout:'centered')`, `grid`, `stat`, `codeWindow`, `feature`
17
+ **Signature moves:** bento grid, floating nav on scroll, stat bar, code window (raw `<pre><code>`, creative override)
18
+ **Components to lead with:** `hero(layout:'centered')`, `grid`, `stat`, `feature`
19
19
 
20
20
  ---
21
21
 
@@ -25,8 +25,8 @@ When you choose a design direction, tell the user which one you're using and why
25
25
  **Geometry:** large radius (14–20px), soft card shadows, rounded buttons
26
26
  **Typography:** humanist sans (inter, nunito), comfortable line-height
27
27
  **Palette pattern:** earthy warm — terracotta / sage / cream / warm off-white
28
- **Signature moves:** real photo hero, services grid with icons, "meet the team", hours + map, handwritten accent (SVG underline via decorate)
29
- **Components to lead with:** `hero(layout:'split')`, `photoCard`, `gallery`, `section(variant:'alt')`, `footer` with address
28
+ **Signature moves:** real photo hero, services grid with icons, "meet the team", hours + map, handwritten accent (SVG underline, raw HTML)
29
+ **Components to lead with:** `hero(layout:'split')`, `uiImage`, `grid`, `section(variant:'alt')`, `footer` with address
30
30
 
31
31
  ---
32
32
 
@@ -53,8 +53,8 @@ When you choose a design direction, tell the user which one you're using and why
53
53
  **Geometry:** medium radius (6–10px), oversized type, dramatic section transitions
54
54
  **Typography:** heavy condensed sans (Black weight), massive heading, strong contrast
55
55
  **Palette pattern:** black bg + vivid single colour (yellow / red / lime) for accents
56
- **Signature moves:** work case-study grid, client logo marquee, big number stats, diagonal section, full-bleed hero
57
- **Components to lead with:** `hero(layout:'poster')`, `marquee`, `gallery(layout:'masonry')`, `section(variant:'diagonal')`, `stat`
56
+ **Signature moves:** work case-study grid, client logo strip (raw HTML, CSS scroll animation), big number stats, diagonal section, full-bleed hero
57
+ **Components to lead with:** `hero(layout:'poster')`, `grid`, `uiImage`, `section(variant:'diagonal')`, `stat`
58
58
 
59
59
  ---
60
60
 
@@ -67,12 +67,12 @@ When you choose a design direction, tell the user which one you're using and why
67
67
  **Signature moves (use these, not 3-col feature grids):**
68
68
  - **Full-colour section blocks** — each section has its own bg colour (yellow stats strip, cyan testimonials, hot-pink CTA). Palette is structure, not accent.
69
69
  - **Oversized hero element** — the product's key visual (a game board, an illustration, an icon) fills most of the hero viewport. `phoneFrame` is one option, not the default.
70
- - **Marquee strip** — giant repeated icon-library glyphs, brand marks, or product icons scrolling across the page between sections (`marquee` component). Use icons from `src/ui/icons.js`, not emoji — emoji are banned in UI output (not accessible, not theme-aware).
71
- - **Tilted `photoCard`s** — testimonial or feature cards at ±2–4° with `photoCard` or CSS `transform: rotate()`
72
- - **Memphis-style geometric shapes** between sections — use `decorate(pattern:'circles')` or CSS background shapes
73
- - **Social proof as ticker** — a `marquee` of short quotes or star ratings, not a 3-col grid
70
+ - **Scrolling strip** — giant repeated icon-library glyphs, brand marks, or product icons scrolling across the page between sections (raw HTML + CSS `@keyframes` animation, creative override — no dedicated component). Use icons from `src/ui/icons.js`, not emoji — emoji are banned in UI output (not accessible, not theme-aware).
71
+ - **Tilted cards** — testimonial or feature cards at ±2–4° with `card` + CSS `transform: rotate()`
72
+ - **Memphis-style geometric shapes** between sections — raw inline SVG or CSS background shapes (creative override)
73
+ - **Social proof as ticker** — a CSS scrolling strip of short quotes or star ratings, not a 3-col grid
74
74
 
75
- **Components to lead with:** `hero`, `appBadge`, `marquee`, `photoCard`, `section` with flat-colour backgrounds, `stat`
75
+ **Components to lead with:** `hero`, `appBadge`, `card`, `section` with flat-colour backgrounds, `stat`
76
76
  **What NOT to do:** 3-column `feature()` grids, centred hero + three columns, phone mockup when the product is a game/board/visual thing
77
77
 
78
78
  ---
@@ -84,7 +84,7 @@ When you choose a design direction, tell the user which one you're using and why
84
84
  **Typography:** light weight body, all-caps labels, large work titles
85
85
  **Palette pattern:** white or near-black + single neutral accent
86
86
  **Signature moves:** work grid (no card chrome), simple text navigation, contact via plain `<a href="mailto:...">`, no footer clutter
87
- **Components to lead with:** `gallery(layout:'grid')`, `grid`, `container(size:'sm')`, `heading`, minimal `footer`
87
+ **Components to lead with:** `grid`, `uiImage`, `container(size:'sm')`, `heading`, minimal `footer`
88
88
 
89
89
  ---
90
90
 
@@ -105,8 +105,8 @@ When you choose a design direction, tell the user which one you're using and why
105
105
  **Geometry:** medium radius (8px), textured backgrounds, thick borders
106
106
  **Typography:** slab serif (Rockwell / Courier), mixed cases, stamp-style labels
107
107
  **Palette pattern:** amber / cream / dark brown / forest green
108
- **Signature moves:** `decorate(pattern:'dots')` background, thick divider lines, badge-style eyebrows, stacked text hierarchy
109
- **Components to lead with:** `hero(layout:'stacked')`, `section(variant:'alt')`, `decorate`, `badge`, `card`
108
+ **Signature moves:** dotted-pattern background (inline SVG or CSS `radial-gradient`, creative override), thick divider lines, badge-style eyebrows, stacked text hierarchy
109
+ **Components to lead with:** `hero(layout:'stacked')`, `section(variant:'alt')`, `badge`, `card`
110
110
 
111
111
  ---
112
112
 
@@ -117,7 +117,7 @@ When you choose a design direction, tell the user which one you're using and why
117
117
  **Typography:** system-ui or clean sans, no display serif, professional
118
118
  **Palette pattern:** navy / corporate blue + white + grey
119
119
  **Signature moves:** trust logos, security/compliance badges, case study cards, feature table, partner logos
120
- **Components to lead with:** `hero(layout:'centered')`, `grid`, `feature`, `stat`, `testimonial` with company name
120
+ **Components to lead with:** `hero(layout:'centered')`, `grid`, `feature`, `stat`, `card` (testimonial with company name in the footer slot)
121
121
 
122
122
  ---
123
123
 
@@ -128,7 +128,7 @@ When you choose a design direction, tell the user which one you're using and why
128
128
  **Typography:** impact / black weight / giant, sometimes rotated via CSS transform
129
129
  **Palette pattern:** black + white + ONE violent accent (red / lime / hot pink)
130
130
  **Signature moves:** asymmetric grid, oversized section numbers, text-only nav, full bleed images bleeding into sections, no visual fluff
131
- **Components to lead with:** raw `section`, `container`, `heading`, `gallery` (no rounded), `decorate(pattern:'noise')`
131
+ **Components to lead with:** raw `section`, `container`, `heading`, `uiImage` (no rounded), CSS noise texture (creative override)
132
132
 
133
133
  ---
134
134
 
@@ -138,8 +138,8 @@ When you choose a design direction, tell the user which one you're using and why
138
138
  **Geometry:** sharp (2–4px), glowing borders, glassmorphism
139
139
  **Typography:** monospace or geometric sans, colour gradient on headings
140
140
  **Palette pattern:** very dark bg (#0a0a0f) + neon accent (cyan / green / magenta)
141
- **Signature moves:** blurred glow backgrounds, animated grid lines (CSS), terminal code window, token/protocol stats
142
- **Components to lead with:** `hero` with gradient, `codeWindow`, `stat`, `section(variant:'spotlight')`, `grid`
141
+ **Signature moves:** blurred glow backgrounds, animated grid lines (CSS), terminal code window (raw `<pre><code>`, creative override), token/protocol stats
142
+ **Components to lead with:** `hero` with gradient, `stat`, `section(variant:'spotlight')`, `grid`
143
143
 
144
144
  ---
145
145
 
@@ -166,7 +166,7 @@ When you choose a design direction, tell the user which one you're using and why
166
166
  ## Cross-cutting rules
167
167
 
168
168
  - Never default to centered hero + feature grid + pricing + FAQ without checking if a different layout suits the product better.
169
- - The `marquee` component is great for trust logos, client names, and tech stack badges — use it on agency/corporate/SaaS directions.
170
- - The `photoCard` and `gallery` components are underused — deploy them whenever the product has visual appeal (food, fitness, places, products).
171
- - `decorate` backgrounds (dots, grid, noise, lines) add texture without images — especially useful on retro, brutalist, and paper directions.
169
+ - A CSS-only scrolling strip (raw HTML + `@keyframes`, creative override) is great for trust logos, client names, and tech stack badges — use it on agency/corporate/SaaS directions.
170
+ - Photo-forward layouts (`uiImage` inside `grid`) are underused — deploy them whenever the product has visual appeal (food, fitness, places, products).
171
+ - Subtle background textures (dot-grid, noise, lines via inline SVG or CSS gradients, creative override) add texture without images — especially useful on retro, brutalist, and paper directions.
172
172
  - `section(variant:'diagonal')` → bold / agency; `section(variant:'paper')` → warm / retro; `section(variant:'spotlight')` → dark tech / neon.
@@ -3,7 +3,7 @@
3
3
  - meta.styles — array of CSS paths loaded as <link rel="stylesheet">. Always include '/app.css'.
4
4
  - meta.scripts — array of JS paths loaded as <script defer>. Required for interactive UI components.
5
5
 
6
- Interactive Pulse UI components (carousel, modal, accordion, tooltip) require BOTH:
6
+ Interactive Pulse UI components (tooltip) require BOTH:
7
7
  ```js
8
8
  meta: {
9
9
  styles: ['/app.css', '/pulse-ui.css'],
@@ -125,7 +125,7 @@ meta: {
125
125
  |---|---|---|
126
126
  | `warm` | radius 14px, softer letter-spacing | rounded photo cards, `section: paper`, warm palette |
127
127
  | `editorial` | radius 0, serif display font, tight letter-spacing | `hero layout: overlap`, `section: diagonal`, `pullquote` |
128
- | `playful` | radius 22px, neutral letter-spacing | `gallery` with rounded images, `marquee`, bright accent |
128
+ | `playful` | radius 22px, neutral letter-spacing | rounded image grid, scrolling strip (creative override), bright accent |
129
129
  | `minimal` | radius 2px, open letter-spacing, no shadows | left-aligned hero, `section: spotlight`, monochrome |
130
130
  | `bold` | radius 6px, very tight letter-spacing | `section: dark`, large `stat` components, gradient hero |
131
131
 
@@ -36,9 +36,9 @@ When the vibe is **playful, bold, brutalist, retro, or neon**, run `pulse_sketch
36
36
  **What this means in practice for expressive vibes:**
37
37
 
38
38
  - The template tells you *what to include* (a hero, features, social proof, CTA). It does **not** tell you how to arrange them or what shape they take.
39
- - Full-colour section blocks, oversized typography heroes, marquee strips, dense grids, and palette-as-structure are all fair game.
39
+ - Full-colour section blocks, oversized typography heroes, CSS-only scrolling strips, dense grids, and palette-as-structure are all fair game.
40
40
  - The `phoneFrame` in Mobile App Landing is **not mandatory** — a full-bleed game board, an oversized illustration, or a typography-only hero may serve the brief better.
41
- - `feature()` in a 3-column grid is the most predictable layout on the web. If the brief is "fun and bold", use a marquee strip of features, a zigzag alternating layout, or full-bleed feature sections instead.
41
+ - `feature()` in a 3-column grid is the most predictable layout on the web. If the brief is "fun and bold", use a scrolling strip of features (raw HTML + CSS `@keyframes`, creative override), a zigzag alternating layout, or full-bleed feature sections instead.
42
42
 
43
43
  ---
44
44
 
@@ -115,8 +115,8 @@ Record the intake answers. If the user provides palette hex values, note which a
115
115
  | Hero | `hero` (split layout) with `phoneFrame` mockup |
116
116
  | Stats bar | `stat` × 4 in a `grid` |
117
117
  | Features | `feature` × 6 in a `grid` |
118
- | Testimonials | `testimonial` × 3 in a `grid` |
119
- | Pricing | `pricing` × 3 (Free / Pro / Family) in a `grid` |
118
+ | Testimonials | `card({ content, footer })` × 3 in a `grid` |
119
+ | Pricing | `card({ title, content, footer })` × 3 (Free / Pro / Family) in a `grid` |
120
120
  | FAQ | `accordion` |
121
121
  | Download CTA | `cta` with `appBadge` |
122
122
  | Footer | `footer` with logo, links, legal |
@@ -326,7 +326,7 @@ Do NOT set `meta.theme: 'light'` on dark templates — the default pulse-ui them
326
326
  | Stats strip | `stat` × 4 in `grid` — clients, rating, years, availability |
327
327
  | Services | `card` × 6 in `grid` with name, description, and price |
328
328
  | About | Two-column: photo + team bio with trust badges |
329
- | Reviews | `testimonial` × 3 in `grid` |
329
+ | Reviews | `card({ content, footer })` × 3 in `grid` |
330
330
  | Contact / Hours | Two-column: address/phone/hours + booking form |
331
331
  | CTA banner | `cta` with offer (e.g. 10% off first visit) |
332
332
  | Footer | `footer` with address in `legal` |
@@ -394,7 +394,7 @@ Portfolio templates rarely need a separate theme CSS — the default dark palett
394
394
  | Nav | `nav` with event logo, links, Get Tickets CTA |
395
395
  | Hero | `hero(size:'xl', align:'center')` with gradient background |
396
396
  | Stats strip | `stat` × 4 — days, speakers, attendees, tracks |
397
- | Sponsor logos | `marquee` with sponsor names/logos |
397
+ | Sponsor logos | `cluster()` row, or raw HTML + CSS `@keyframes` scrolling strip (creative override) with sponsor names/logos |
398
398
  | Speakers | `card` × 8 in `grid` with photo, name, role, topic |
399
399
  | Schedule | `accordion` — one item per day with time/session/location |
400
400
  | Venue | Two-column: venue info + photo |
@@ -45,7 +45,7 @@ The component rules marked *(Mode A)* below are suspended under a declared creat
45
45
  - Use inline `style=""` attributes. Use utility classes or `var(--ui-*)` tokens in a stylesheet. *Exception:* structural image styles with no utility-class equivalent (`aspect-ratio`, `object-fit`) are permitted inline — these are layout facts, not theming, and the design gallery uses them this way.
46
46
  - Write raw `<button>`, `<input>`, `<select>`, or `<textarea>` HTML when the component library already provides accessible, styled versions. *(Mode A — under a creative override, functional atoms should still come from components unless there is a specific design reason.)*
47
47
  - Write raw `<h1>`–`<h6>` without styling. Use `heading({ level, text })` instead. *(Mode A — under a creative override, raw headings styled with utility classes or custom CSS are correct; `heading()` adds wrapper markup that fights custom layouts.)*
48
- - Write raw `<ul>` or `<ol>` without styling. Use `list({ items })` instead. *(Mode A — under a creative override, raw lists are permitted when the component markup would interfere with the layout.)*
48
+ - Write raw `<ul>` or `<ol>` without visible styling. There is no dedicated list component — style lists with utility classes or `app.css` tokens rather than leaving default browser markers unstyled.
49
49
  - Output CMS or database HTML without a `prose()` wrapper. Raw HTML from external sources has no styling — always wrap it in `prose({ content: html })`.
50
50
  - Use `data-event` on text inputs. Re-rendering on every keystroke destroys focus. Use uncontrolled inputs and read values from `FormData` in `action.onStart` or `action.run`.
51
51
  - Skip `onError` in an action. It is required. Always handle failure.
@@ -154,9 +154,8 @@ Or use the guide-components table. Never write these patterns from scratch in Mo
154
154
  - Hero section → `hero()`
155
155
  - Product/service cards → `card()`
156
156
  - Image + text layouts → `media()`
157
- - Horizontal strips → `banner()`
158
157
  - Feature tiles → `feature()`
159
- - Testimonials → `testimonial()`
158
+ - Testimonials → `card({ content, footer })` with quote + avatar/name in the footer slot
160
159
  - CTAs → `cta()`
161
160
 
162
161
  If you're about to type `class="hero"` or `class="product-card"` in Mode A, stop — use the component instead.