@invisibleloop/pulse 0.15.1 → 0.17.0

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.
@@ -50,7 +50,7 @@ Check for:
50
50
  - Layout looks correct
51
51
  - No console errors via `mcp__chrome-devtools__list_console_messages`
52
52
 
53
- Then use `ask_user` to ask: "Happy with the design and layout, or would you like any changes before I run tests and Lighthouse?" with choices: `["Yes, looks good — run tests and Lighthouse", "I'd like some changes first"]`
53
+ Then ask the user: "Happy with the design and layout, or would you like any changes before I run tests and Lighthouse?" — if your host provides a question tool (e.g. AskUserQuestion), offer the choices `["Yes, looks good — run tests and Lighthouse", "I'd like some changes first"]`; otherwise ask in plain prose.
54
54
 
55
55
  **Do not proceed to step 7 until the user explicitly confirms.** If they want changes, make them and return to step 6.
56
56
 
@@ -15,7 +15,7 @@ If $ARGUMENTS is provided, use it as the file path or route. Otherwise, identify
15
15
  If this is a new page build (came through `pulse_intake` or `build-page`):
16
16
 
17
17
  1. Take a screenshot and describe what you see to the user.
18
- 2. Use `ask_user` to ask: "Happy with the design and layout, or would you like any changes before I run Lighthouse?" — provide choices: `["Yes, looks good — proceed", "I'd like some changes first"]`
18
+ 2. Ask the user: "Happy with the design and layout, or would you like any changes before I run Lighthouse?" — if your host provides a question tool (e.g. AskUserQuestion), offer the choices `["Yes, looks good — proceed", "I'd like some changes first"]`; otherwise ask in plain prose.
19
19
  3. **Do not proceed to step 3 until the user explicitly confirms.** If they want changes, stop here, make the changes, then restart from step 2. Lighthouse takes ~90 seconds — do not waste it on a design the user hasn't approved.
20
20
 
21
21
  ### 3. Validate the spec
@@ -48,45 +48,63 @@ The browser should still be on `http://localhost:3001/` from step 6. Run `mcp__c
48
48
 
49
49
  After both Lighthouse runs pass, call `pulse_restart_server` to return to the dev server.
50
50
 
51
- ### 8. Performance
51
+ ### 8. Mobile layout check
52
+
53
+ Emulate a mobile viewport and take a screenshot to catch wrapping, overflow, and layout issues before they become Lighthouse failures.
54
+
55
+ ```
56
+ mcp__chrome-devtools__emulate viewport: "390x844x2,mobile,touch"
57
+ mcp__chrome-devtools__navigate_page url: "http://localhost:3000/"
58
+ mcp__chrome-devtools__take_screenshot
59
+ ```
60
+
61
+ Describe what you see. Look specifically for:
62
+ - Text or buttons overflowing the viewport
63
+ - Content too small to read or tap
64
+ - Navigation that overlaps content
65
+ - Images that break the layout
66
+
67
+ Fix any issues, then reset to desktop before continuing:
68
+
69
+ ```
70
+ mcp__chrome-devtools__emulate viewport: "1440x900x1"
71
+ ```
72
+
73
+ ### 9. Performance
52
74
 
53
75
  Navigate to the page route, then run `mcp__chrome-devtools__performance_start_trace` with `reload: true` and `autoStop: true`. Report LCP and CLS. Flag any LCP insight that suggests a fixable problem (render-blocking resources, large image load delay, etc.).
54
76
 
55
- ### 9. Console errors
77
+ ### 10. Console errors
56
78
 
57
79
  Use `mcp__chrome-devtools__list_console_messages` — report any errors or unexpected warnings.
58
80
 
59
- ### 10. Code review
81
+ ### 11. Code review
60
82
 
61
83
  Call `pulse_review` with the spec file path. This runs a full code review AND atomically writes the `.pulse-verified` stamp server-side, eliminating any race condition with the stop hook.
62
84
 
63
85
  Work through every item the review returns. Fix anything that fails before proceeding.
64
86
 
65
- ### 11. Close extra browser tabs
87
+ ### 12. Close extra browser tabs
66
88
 
67
89
  Use `mcp__chrome-devtools__list_pages` to get all open pages. Close every page **except the last one** — `close_page` refuses to close the final tab. `pageId` must be a number, not a string.
68
90
 
69
91
  If there is only one page open, skip this step — there is nothing to close.
70
92
 
71
- ### 12. Write verification stamp
72
-
73
- `pulse_review` writes the stamp automatically. As a belt-and-suspenders backup, also run:
93
+ ### 13. Write verification stamp
74
94
 
75
- ```bash
76
- date +%s > .pulse-verified
77
- ```
95
+ Call `pulse_stamp`. This writes `.pulse-verified` via the MCP server, which is more reliable than the `date +%s` shell command — the MCP write always lands after all spec edits, avoiding the mtime race condition that can cause the stop hook to block immediately after verification.
78
96
 
79
- This ensures the stamp is present even if `pulse_review` was not called with a file path.
97
+ **This must be the last operation before stopping.** The stop hook compares each edited spec's mtime against this stamp — if any spec is newer than the stamp, the hook blocks. Do not edit any spec file after calling `pulse_stamp`.
80
98
 
81
- ### 13. Report
99
+ ### 14. Report
82
100
 
83
101
  Summarise:
84
102
  - Validation: pass or fail (with errors if any)
85
- - Visual: what was visible in the screenshot
86
- - Lighthouse desktop: scores for all four categories
87
- - Lighthouse mobile: scores for all four categories
103
+ - Visual: desktop screenshot and mobile layout check — any issues found and fixed
104
+ - Lighthouse desktop: scores for Accessibility, Best Practices, SEO
105
+ - Lighthouse mobile: scores for Accessibility, Best Practices, SEO
88
106
  - Performance: LCP and CLS values, any flagged insights
89
107
  - Console: any errors
90
108
  - Review: pass or issues found and fixed
91
109
 
92
- Only confirm the page is good when validation passes, both Lighthouse runs are 100/100/100/100, CLS is 0.00, and there are no console errors. Otherwise, fix and run `/verify` again.
110
+ Only confirm the page is good when validation passes, both Lighthouse runs are 100/100/100, CLS is 0.00, and there are no console errors. Otherwise, fix and run `/verify` again.
@@ -22,10 +22,10 @@ Before finishing any spec, verify every point below. Fix anything that fails.
22
22
 
23
23
  - **Always restart the server and reload the browser after every file edit — before checking the result.** The dev server restarts on file changes but the browser tab stays stale. The required sequence is: `pulse_restart_server` → `navigate_page`. Never attempt to debug a visual problem without doing both steps first.
24
24
 
25
- ### Components first
25
+ ### Components first — or creative override
26
26
 
27
27
  - **Before writing any HTML by hand, check `src/ui/index.js`.** There are 50+ components. Use `button`, `card`, `alert`, `input`, `spinner`, `badge`, `modal`, `nav`, `pagination`, `table`, etc. before writing equivalent HTML from scratch.
28
- - **Do not reinvent component patterns.** These patterns have dedicated components — never write custom HTML for them:
28
+ - **Do not reinvent component patterns.** These patterns have dedicated components — never write custom HTML for them unless a creative override applies (see below):
29
29
  - Hero sections → `hero({ title, subtitle, actions, image, ... })`
30
30
  - Product/service cards → `card({ title, body, image, footer, ... })`
31
31
  - Image + text two-column layouts → `media({ image, content, reverse })`
@@ -35,10 +35,16 @@ Before finishing any spec, verify every point below. Fix anything that fails.
35
35
  - CTAs → `cta({ title, subtitle, actions })`
36
36
  - Navigation → `nav({ logo, links, actions })`
37
37
  - Footers → `footer({ logo, links, columns, legal, ... })`
38
-
39
- If you find yourself writing `class="hero"` or `class="product-card"` or `class="testimonial"`, stop — import the component instead.
40
38
 
41
- - **Exception — brutalist, editorial, and playful vibes:** these vibes intentionally break from polished templates. `pulse://guide/explore` explicitly endorses zone-based raw HTML for distinctive layouts. For `brutalist`, `editorial`, `retro`, or `neon` vibes, raw structural HTML is correct — components can still be used for functional atoms (`button`, `input`, `modal`) but structural sections do not need to come from components. When reviewing a brutalist or editorial spec, do not flag raw section HTML as a violation.
39
+ - **Creative override — raw HTML throughout is permitted** when the design intent genuinely calls for it: a highly custom visual identity, an asymmetric or typographically-driven layout, a brutalist/editorial/retro/neon/paper vibe where components would constrain the expression, or a layout that requires features a component simply doesn't support (e.g. full-viewport height, custom gradient glows, clamp-scaled display type). This is a deliberate design decision, not a shortcut. When taking this path:
40
+ - **Declare it in a comment at the top of the spec** so the reviewer recognises it: `// component-free — creative override: <reason>`. This is the signal the review tool reads.
41
+ - Functional atoms (`button`, `input`, `badge`, `modal`) should still come from components unless there is a specific design reason not to.
42
+ - `/pulse-ui.css` is always required — it provides the token system your custom CSS will use.
43
+ - The quality gate replaces the component checklist: **Lighthouse 100 on Accessibility, Best Practices, and SEO — desktop and mobile — plus CLS 0.00 is the pass bar.** (Performance is reported but not gated; it varies with machine load.) A component-free page that passes every audit is correct. A component-heavy page that fails accessibility is not.
44
+
45
+ - **When reviewing**: before flagging a class name like `.hero` as a violation, first ask whether the `hero()` component could actually reproduce the layout. If the answer is no, it is a creative override — check Lighthouse instead of refactoring. Do not flatten a good custom layout into a generic component just to clear a pattern-match.
46
+
47
+ - **The review tool auto-detects creative override** from the spec comment. When detected, component pattern checks are shown as advisory (`⚡`) not as failures, and the Lighthouse gate is the stated pass bar.
42
48
 
43
49
  - **Never write App Store or Google Play download buttons by hand.** Always use `appBadge({ store: 'apple', href })` and `appBadge({ store: 'google', href })`. Raw `<a>` tags with badge images are incorrect — `appBadge` renders the correct accessible, styled badge for each store.
44
50
 
@@ -132,12 +138,6 @@ Before finishing any spec, verify every point below. Fix anything that fails.
132
138
  - **Call the same `md()` fetcher in both `meta` and `server`** — it caches on `ctx._mdCache` so the file is only read once per request. Do not create two separate fetchers for the same file.
133
139
  - **Always add `onViewError` on dynamic markdown routes** (routes with `:param` segments loading `.md` files). A missing file throws `{ status: 404 }` which must be caught gracefully.
134
140
 
135
- ### Markdown
136
-
137
- - **Use `md()` from `@invisibleloop/pulse/md` for `.md` file content.** Never read and parse markdown manually. Pass the result's `html` to `prose()`.
138
- - **Call the same `md()` fetcher in both `meta` and `server`** — it caches on `ctx._mdCache` so the file is only read once per request. Do not create two separate fetchers for the same file.
139
- - **Always add `onViewError` on dynamic markdown routes** (routes with `:param` segments loading `.md` files). A missing file throws `{ status: 404 }` which must be caught gracefully.
140
-
141
141
  ### View error handling
142
142
 
143
143
  - **Define `onViewError` on any page where the view could throw due to bad or missing data.** Without it, a runtime view error returns a 500 on the server and shows a generic inline message on the client. With it, the server returns 200 with your fallback HTML instead of a 500, and the client renders your fallback:
package/CLAUDE.md CHANGED
@@ -5,7 +5,7 @@ Pulse is a spec-first frontend framework. The spec is the source of truth. No co
5
5
  ## Commands
6
6
 
7
7
  ```bash
8
- npm run dev # start dev server at http://localhost:3001
8
+ npm run dev # start dev server at http://localhost:3000 (pulse_build serves production on 3001)
9
9
  npm test # run all 92 unit tests
10
10
  npm run build # bundle for production → public/dist/
11
11
  ```
@@ -57,6 +57,12 @@ export const mySpec = {
57
57
  // Timeout for all server fetchers on this page (ms). Overrides createServer fetcherTimeout.
58
58
  serverTimeout: 5000,
59
59
 
60
+ // Query-string keys that change this page's output, for the in-process page cache.
61
+ // The cache IGNORES the query string by default — arbitrary ?n=… params cannot mint
62
+ // unbounded cache entries (memory-DoS guard). If the rendered output depends on a
63
+ // query param, list it here so distinct values get distinct cache entries.
64
+ cacheVary: ['q', 'page'],
65
+
60
66
  // Global store keys this page subscribes to — appears in view's server arg
61
67
  // Store mutations update all subscribed pages without a server round-trip
62
68
  store: ['user', 'cart'],
@@ -458,7 +464,7 @@ For any **new page, landing page, or branded site**, run this sequence first. Do
458
464
  3. pulse_intent(description) → archetype + scaffold + guide list
459
465
  ```
460
466
 
461
- **Before calling `pulse_intake`**, gather the required information by asking the user — one question at a time, in plain prose, no bullet lists. You need at minimum: the product name, what it does (pitch), and its key features. Ask for inspiration references, palette, vibe, and anti-style if the user hasn't mentioned them.
467
+ **Before calling `pulse_intake`**, gather the required information by asking the user — one question at a time, in plain prose, no bullet lists. You need at minimum: the product name, what it does (pitch), and its key features. **Always ask first whether they have design inspiration** (a site they love, a screenshot, a mood board — dropped into `public/intake/` or pasted as a URL), and check `public/intake/` for images already there. Then ask for palette, vibe, and anti-style if not mentioned.
462
468
 
463
469
  **`pulse_sketch` is mandatory** for `playful`, `bold`, `brutalist`, `retro`, or `neon` vibes — those vibes fight the default centred-hero layout. Skip it only for `corporate` or `minimal` when no structural preference has been stated.
464
470
 
@@ -470,13 +476,16 @@ Every build task follows this sequence. Each phase has a pass gate — do not ad
470
476
 
471
477
  **Before calling any slow tool (`pulse_build`, `lighthouse_audit`), output a status message to the user first** — e.g. "Building for production — ~30 s…" or "Running Lighthouse desktop audit…". Never call a slow tool silently.
472
478
 
479
+ > **Canonical workflow:** `pulse://workflow` (`src/agent/workflow.md`) is the authoritative version, including complexity tiers (not every task needs all phases). The table below is the summary — if they ever disagree, the MCP resource wins.
480
+
473
481
  | Phase | Action | Gate |
474
482
  |---|---|---|
475
483
  | 1. Understand | Read guides, call `pulse_list_structure` | — |
476
484
  | 2. Plan | Present plan to user, wait for confirmation | User confirms |
477
485
  | 3. Build | Write spec + related files | — |
478
486
  | 4. Validate | `pulse_validate` — fix all errors + warnings | Clean output |
479
- | 5. Browser | Screenshot → `pulse_design_review` (if intake ran) → `pulse_layout_review` → `/verify` (Lighthouse desktop + mobile + `pulse_review`) | ⛔ Do not skip: all gates must pass + `.pulse-verified` stamp written |
487
+ | 5a. Design approval *(new builds only)* | Screenshot → describe → ask the user | ⛔ User approves layout before any slow checks run (skip for edits/fixes) |
488
+ | 5b. Browser | `pulse_design_review` (if intake ran) → `pulse_layout_review` → `/verify` (Lighthouse desktop + mobile + `pulse_review`) | ⛔ Do not skip: all gates must pass + `.pulse-verified` stamp written |
480
489
  | 6. Tests | Write tests, run them, fix failures | All pass |
481
490
  | 7. Code Review | `pulse_review` — only after phases 4–6 pass | — |
482
491
  | 8. Fix | Fix every review issue, re-run affected gates | All gates still pass |
@@ -487,7 +496,7 @@ Every build task follows this sequence. Each phase has a pass gate — do not ad
487
496
 
488
497
  **The Code Review is always last.** Never invoke `pulse_review` before validate, Lighthouse, design review, and tests all pass.
489
498
 
490
- The `/verify` command runs the browser check loop (phases 4–5) automatically. Use it.
499
+ The `/verify` command runs the browser check loop (phases 4–5b) automatically. Use it.
491
500
 
492
501
  ## Pulse vs React — Do Not Do These
493
502
 
@@ -40,7 +40,7 @@ Before finishing any spec, verify every point below. Fix anything that fails.
40
40
  - **Declare it in a comment at the top of the spec** so the reviewer recognises it: `// component-free — creative override: <reason>`. This is the signal the review tool reads.
41
41
  - Functional atoms (`button`, `input`, `badge`, `modal`) should still come from components unless there is a specific design reason not to.
42
42
  - `/pulse-ui.css` is always required — it provides the token system your custom CSS will use.
43
- - The quality gate replaces the component checklist: **100/100/100/100 Lighthouse on both desktop and mobile is the pass bar.** A component-free page that passes every audit is correct. A component-heavy page that fails accessibility is not.
43
+ - The quality gate replaces the component checklist: **Lighthouse 100 on Accessibility, Best Practices, and SEO — desktop and mobile — plus CLS 0.00 is the pass bar.** (Performance is reported but not gated; it varies with machine load.) A component-free page that passes every audit is correct. A component-heavy page that fails accessibility is not.
44
44
 
45
45
  - **When reviewing**: before flagging a class name like `.hero` as a violation, first ask whether the `hero()` component could actually reproduce the layout. If the answer is no, it is a creative override — check Lighthouse instead of refactoring. Do not flatten a good custom layout into a generic component just to clear a pattern-match.
46
46
 
@@ -36,13 +36,15 @@ Before finishing any spec, verify every point below. Fix anything that fails.
36
36
  - Navigation → `nav({ logo, links, actions })`
37
37
  - Footers → `footer({ logo, links, columns, legal, ... })`
38
38
 
39
- - **Creative override — raw HTML throughout is permitted** when the design intent genuinely calls for it: a highly custom visual identity, an asymmetric or typographically-driven layout, a brutalist/editorial/retro/neon/paper vibe where components would constrain the expression. This is a deliberate design decision, not a shortcut. When taking this path:
40
- - State it explicitly in the build brief: *"Building component-free — raw HTML throughout for creative control."*
39
+ - **Creative override — raw HTML throughout is permitted** when the design intent genuinely calls for it: a highly custom visual identity, an asymmetric or typographically-driven layout, a brutalist/editorial/retro/neon/paper vibe where components would constrain the expression, or a layout that requires features a component simply doesn't support (e.g. full-viewport height, custom gradient glows, clamp-scaled display type). This is a deliberate design decision, not a shortcut. When taking this path:
40
+ - **Declare it in a comment at the top of the spec** so the reviewer recognises it: `// component-free — creative override: <reason>`. This is the signal the review tool reads.
41
41
  - Functional atoms (`button`, `input`, `badge`, `modal`) should still come from components unless there is a specific design reason not to.
42
42
  - `/pulse-ui.css` is always required — it provides the token system your custom CSS will use.
43
- - The quality gate replaces the component checklist: **100/100/100/100 Lighthouse on both desktop and mobile is the pass bar.** A component-free page that passes every audit is correct. A component-heavy page that fails accessibility is not.
43
+ - The quality gate replaces the component checklist: **Lighthouse 100 on Accessibility, Best Practices, and SEO — desktop and mobile — plus CLS 0.00 is the pass bar.** (Performance is reported but not gated; it varies with machine load.) A component-free page that passes every audit is correct. A component-heavy page that fails accessibility is not.
44
44
 
45
- - When reviewing a spec built under creative override, do not flag raw section HTML as a violation — check the Lighthouse results instead.
45
+ - **When reviewing**: before flagging a class name like `.hero` as a violation, first ask whether the `hero()` component could actually reproduce the layout. If the answer is no, it is a creative override — check Lighthouse instead of refactoring. Do not flatten a good custom layout into a generic component just to clear a pattern-match.
46
+
47
+ - **The review tool auto-detects creative override** from the spec comment. When detected, component pattern checks are shown as advisory (`⚡`) not as failures, and the Lighthouse gate is the stated pass bar.
46
48
 
47
49
  - **Never write App Store or Google Play download buttons by hand.** Always use `appBadge({ store: 'apple', href })` and `appBadge({ store: 'google', href })`. Raw `<a>` tags with badge images are incorrect — `appBadge` renders the correct accessible, styled badge for each store.
48
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invisibleloop/pulse",
3
- "version": "0.15.1",
3
+ "version": "0.17.0",
4
4
  "type": "module",
5
5
  "description": "AI-first frontend framework. The spec is the source of truth.",
6
6
  "license": "MIT",
@@ -57,7 +57,7 @@
57
57
  "scripts": {
58
58
  "dev": "node --watch src/cli/dev.js",
59
59
  "build": "node scripts/build.js",
60
- "test": "node src/spec/schema.test.js && node src/runtime/runtime.test.js && node src/runtime/navigate.test.js && node src/runtime/morph.test.js && node src/runtime/ssr.test.js && node src/runtime/image.test.js && node src/server/server.test.js && node src/cli/cli.test.js && node src/ui/ui.test.js && node src/testing/testing.test.js && node src/store/store.test.js && node src/md/md.test.js && node scripts/strip-server.test.js",
60
+ "test": "node src/spec/schema.test.js && node src/runtime/runtime.test.js && node src/runtime/navigate.test.js && node src/runtime/morph.test.js && node src/runtime/ssr.test.js && node src/runtime/image.test.js && node src/server/server.test.js && node src/cli/cli.test.js && node src/ui/ui.test.js && node src/testing/testing.test.js && node src/store/store.test.js && node src/md/md.test.js && node scripts/strip-server.test.js && node src/mcp/mcp.test.js",
61
61
  "typecheck": "tsc --noEmit",
62
62
  "docs": "node --watch src/cli/dev.js --root docs --port 4000",
63
63
  "docs:start": "node docs/server.js"
@@ -1 +1 @@
1
- 0.15.1
1
+ 0.17.0
@@ -40,7 +40,7 @@ Before finishing any spec, verify every point below. Fix anything that fails.
40
40
  - **Declare it in a comment at the top of the spec** so the reviewer recognises it: `// component-free — creative override: <reason>`. This is the signal the review tool reads.
41
41
  - Functional atoms (`button`, `input`, `badge`, `modal`) should still come from components unless there is a specific design reason not to.
42
42
  - `/pulse-ui.css` is always required — it provides the token system your custom CSS will use.
43
- - The quality gate replaces the component checklist: **100/100/100/100 Lighthouse on both desktop and mobile is the pass bar.** A component-free page that passes every audit is correct. A component-heavy page that fails accessibility is not.
43
+ - The quality gate replaces the component checklist: **Lighthouse 100 on Accessibility, Best Practices, and SEO — desktop and mobile — plus CLS 0.00 is the pass bar.** (Performance is reported but not gated; it varies with machine load.) A component-free page that passes every audit is correct. A component-heavy page that fails accessibility is not.
44
44
 
45
45
  - **When reviewing**: before flagging a class name like `.hero` as a violation, first ask whether the `hero()` component could actually reproduce the layout. If the answer is no, it is a creative override — check Lighthouse instead of refactoring. Do not flatten a good custom layout into a generic component just to clear a pattern-match.
46
46
 
@@ -8,73 +8,103 @@ Run the full verification loop on the current page or the page specified in $ARG
8
8
 
9
9
  If $ARGUMENTS is provided, use it as the file path or route. Otherwise, identify the most recently edited spec file from context.
10
10
 
11
- ### 2. Validate the spec
11
+ ### 2. Design approval (new builds only)
12
+
13
+ **Skip this step for edits, bug fixes, or "add X to existing Y" tasks.**
14
+
15
+ If this is a new page build (came through `pulse_intake` or `build-page`):
16
+
17
+ 1. Take a screenshot and describe what you see to the user.
18
+ 2. Ask the user: "Happy with the design and layout, or would you like any changes before I run Lighthouse?" — if your host provides a question tool (e.g. AskUserQuestion), offer the choices `["Yes, looks good — proceed", "I'd like some changes first"]`; otherwise ask in plain prose.
19
+ 3. **Do not proceed to step 3 until the user explicitly confirms.** If they want changes, stop here, make the changes, then restart from step 2. Lighthouse takes ~90 seconds — do not waste it on a design the user hasn't approved.
20
+
21
+ ### 3. Validate the spec
12
22
 
13
23
  Use `pulse_validate` with the spec file content. If validation fails, report every error clearly and stop — do not proceed to screenshot until the spec is valid.
14
24
 
15
- ### 3. Check the dev server
25
+ ### 4. Check the dev server
16
26
 
17
27
  Use `pulse_fetch_page` to confirm the server is responding for the route. If it errors, use `pulse_restart_server` and retry once.
18
28
 
19
- **If `pulse_fetch_page` returns unexpected HTML (e.g. a different project's page title, a Next.js 404, or any non-Pulse response):** check the `X-Pulse` response header — Pulse servers always send this. If it is absent, the wrong server is running on that port. Stop and inform the user before continuing.
20
-
21
- ### 4. Screenshot
29
+ ### 5. Screenshot
22
30
 
23
31
  Use `mcp__chrome-devtools__navigate_page` to load the page route, then `mcp__chrome-devtools__take_screenshot` to capture the result. Describe what you see — layout, content, any obvious rendering issues.
24
32
 
25
- ### 5. Lighthouse — desktop
33
+ ### 6. Lighthouse — desktop
34
+
35
+ **Pre-flight (required before every Lighthouse run):**
36
+ 1. Call `pulse_build` to produce a production build and start the production server on port 3001.
37
+ 2. Call `navigate_page` with `url: "http://localhost:3001/"` so the browser is on the production server.
38
+
39
+ Then run `mcp__chrome-devtools__lighthouse_audit` with `{ "device": "desktop" }`.
40
+
41
+ **Pass bar: Accessibility, Best Practices, and SEO must all be 100.** Performance is measured and reported but is not a hard requirement (it varies with machine load). Report the actual scores. If Accessibility, Best Practices, or SEO is below 100, identify the failing audit(s), fix the issue, and restart from step 3.
26
42
 
27
- Run `mcp__chrome-devtools__lighthouse_audit` on the route with `{ "strategy": "desktop" }`.
43
+ ### 7. Lighthouse — mobile
28
44
 
29
- **Pass bar: Accessibility, Best Practices, and SEO must all be 100.** Performance is measured and reported but is not a hard requirement (it varies with machine load). Report the actual scores. If Accessibility, Best Practices, or SEO is below 100, identify the failing audit(s), fix the issue, and restart from step 2.
45
+ The browser should still be on `http://localhost:3001/` from step 6. Run `mcp__chrome-devtools__lighthouse_audit` with `{ "device": "mobile" }`.
30
46
 
31
- ### 6. Lighthouse — mobile
47
+ **Same pass bar: Accessibility, Best Practices, and SEO must all be 100.** Fix any failures and restart from step 3.
32
48
 
33
- Run `mcp__chrome-devtools__lighthouse_audit` on the same route with `{ "strategy": "mobile" }`.
49
+ After both Lighthouse runs pass, call `pulse_restart_server` to return to the dev server.
34
50
 
35
- **Same pass bar: Accessibility, Best Practices, and SEO must all be 100.** Fix any failures and restart from step 2.
51
+ ### 8. Mobile layout check
36
52
 
37
- **If you need to visually inspect the layout at a mobile viewport width** (e.g. to debug a wrapping or overflow issue), use `mcp__chrome-devtools__emulate` with `viewport: "390x844x2,mobile,touch"` — not `resize_page`. The `emulate` tool reliably sets the viewport; `resize_page` does not reliably trigger responsive layout changes. Reset afterward with `emulate` using the desktop viewport: `"1440x900x1"`.
53
+ Emulate a mobile viewport and take a screenshot to catch wrapping, overflow, and layout issues before they become Lighthouse failures.
38
54
 
39
- ### 7. Performance
55
+ ```
56
+ mcp__chrome-devtools__emulate viewport: "390x844x2,mobile,touch"
57
+ mcp__chrome-devtools__navigate_page url: "http://localhost:3000/"
58
+ mcp__chrome-devtools__take_screenshot
59
+ ```
60
+
61
+ Describe what you see. Look specifically for:
62
+ - Text or buttons overflowing the viewport
63
+ - Content too small to read or tap
64
+ - Navigation that overlaps content
65
+ - Images that break the layout
66
+
67
+ Fix any issues, then reset to desktop before continuing:
68
+
69
+ ```
70
+ mcp__chrome-devtools__emulate viewport: "1440x900x1"
71
+ ```
72
+
73
+ ### 9. Performance
40
74
 
41
75
  Navigate to the page route, then run `mcp__chrome-devtools__performance_start_trace` with `reload: true` and `autoStop: true`. Report LCP and CLS. Flag any LCP insight that suggests a fixable problem (render-blocking resources, large image load delay, etc.).
42
76
 
43
- ### 8. Console errors
77
+ ### 10. Console errors
44
78
 
45
79
  Use `mcp__chrome-devtools__list_console_messages` — report any errors or unexpected warnings.
46
80
 
47
- ### 9. Code review
81
+ ### 11. Code review
48
82
 
49
- Call `pulse_review` with the absolute path to the spec file. Read the source, rendered HTML, validator output, and checklist it returns. Fix every issue before proceeding. This is the structured review — do not skip it or replace it with a manual checklist read.
83
+ Call `pulse_review` with the spec file path. This runs a full code review AND atomically writes the `.pulse-verified` stamp server-side, eliminating any race condition with the stop hook.
50
84
 
51
- ### 10. Close the browser
85
+ Work through every item the review returns. Fix anything that fails before proceeding.
52
86
 
53
- Use `mcp__chrome-devtools__list_pages` to get all open pages, then call `mcp__chrome-devtools__close_page` for every page ID returned. This closes the entire browser, not just the tab. `pageId` must be a number, not a string.
87
+ ### 12. Close extra browser tabs
54
88
 
55
- ### 11. Write verification stamp
89
+ Use `mcp__chrome-devtools__list_pages` to get all open pages. Close every page **except the last one** — `close_page` refuses to close the final tab. `pageId` must be a number, not a string.
90
+
91
+ If there is only one page open, skip this step — there is nothing to close.
92
+
93
+ ### 13. Write verification stamp
56
94
 
57
95
  Call `pulse_stamp`. This writes `.pulse-verified` via the MCP server, which is more reliable than the `date +%s` shell command — the MCP write always lands after all spec edits, avoiding the mtime race condition that can cause the stop hook to block immediately after verification.
58
96
 
59
97
  **This must be the last operation before stopping.** The stop hook compares each edited spec's mtime against this stamp — if any spec is newer than the stamp, the hook blocks. Do not edit any spec file after calling `pulse_stamp`.
60
98
 
61
- ### 12. Report
99
+ ### 14. Report
62
100
 
63
101
  Summarise:
64
102
  - Validation: pass or fail (with errors if any)
65
- - Visual: what was visible in the screenshot
66
- - Lighthouse desktop: scores for all four categories
67
- - Lighthouse mobile: scores for all four categories
103
+ - Visual: desktop screenshot and mobile layout check — any issues found and fixed
104
+ - Lighthouse desktop: scores for Accessibility, Best Practices, SEO
105
+ - Lighthouse mobile: scores for Accessibility, Best Practices, SEO
68
106
  - Performance: LCP and CLS values, any flagged insights
69
107
  - Console: any errors
70
108
  - Review: pass or issues found and fixed
71
109
 
72
- **Pass bar — all of these must be met before the page is confirmed good:**
73
- - Validation: clean (no errors, no warnings)
74
- - Lighthouse desktop: Accessibility, Best Practices, SEO all 100
75
- - Lighthouse mobile: same
76
- - CLS: 0.00 — any layout shift is a blocker
77
- - LCP: report the value; flag and fix any insight that identifies a fixable cause
78
- - Console: no errors
79
-
80
- If any gate fails, fix the issue and run `/verify` again.
110
+ Only confirm the page is good when validation passes, both Lighthouse runs are 100/100/100, CLS is 0.00, and there are no console errors. Otherwise, fix and run `/verify` again.
@@ -15,7 +15,7 @@ Warm cream background, hand-crafted feel. Photo-forward hero with a split layout
15
15
  **Key components:** `hero({ layout:'split' })`, `section`, `container`, `grid`, `card` (with `footer`), `stat`, `testimonial`, `cta`, `badge`
16
16
 
17
17
  **Distinguishing moves:**
18
- - Gradient hero `linear-gradient(135deg, #fdf6ee, #fdebd0)` with `color:#3d2b1e`
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
19
19
  - Nav with `background: rgba(255,250,245,0.92)` and matching text colour
20
20
  - Cards with price footer: `<div class="u-flex u-items-center u-justify-between"><span>From</span><strong>£35</strong></div>`
21
21
  - Credentials list: icon + span with `iconCheckCircle`
@@ -105,6 +105,8 @@ Long-form article layout with `prose` component for markdown rendering, breadcru
105
105
 
106
106
  ## Component Combinations by Use Case
107
107
 
108
+ > Inline `style=""` in these snippets is limited to structural image properties (`aspect-ratio`, `object-fit`, fixed heights) that have no utility-class equivalent — this is the permitted exception to the no-inline-style rule. Colours and theming still belong in tokens.
109
+
108
110
  ### Image Card (Card with Cover Photo)
109
111
  Card doesn't have a native image slot — use `flush:true` and put the image inside `content`:
110
112
 
@@ -218,16 +220,16 @@ section({
218
220
  ```
219
221
 
220
222
  ### Booking / Contact Form Inside a Card
223
+ Use the `input()` component for fields — it renders the label, id wiring, and required marker accessibly. Do not write raw `<input>`/`<label>` HTML:
224
+
221
225
  ```js
222
226
  card({
223
227
  title: 'Book an Appointment',
224
228
  content: `
225
229
  <p class="u-text-muted u-text-sm u-mb-4">Fill in the form and we'll confirm within a few hours.</p>
226
230
  <form class="u-flex u-flex-col u-gap-3" data-action="submit">
227
- <div class="u-flex u-flex-col u-gap-1">
228
- <label class="u-text-sm u-font-medium" for="name">Your name</label>
229
- <input id="name" name="name" type="text" class="u-input" required>
230
- </div>
231
+ ${input({ label: 'Your name', name: 'name', required: true })}
232
+ ${input({ label: 'Email', name: 'email', type: 'email', required: true })}
231
233
  ${button({ label: 'Send Enquiry', type: 'submit', variant: 'primary', fullWidth: true })}
232
234
  </form>
233
235
  `,
@@ -62,12 +62,12 @@ When you choose a design direction, tell the user which one you're using and why
62
62
  **Feels like:** Duolingo, Headspace, Loom, Figma landing page, pop-art toy brand
63
63
  **vibe:** `playful`
64
64
  **Geometry:** very rounded (18–24px), friendly icons, colourful
65
- **Typography:** rounded or friendly sans, loose spacing, emoji-friendly
65
+ **Typography:** rounded or friendly sans, loose spacing, friendly icon accents (icon library, never emoji)
66
66
  **Palette pattern:** bright bg (coral / mint / lavender) + dark text
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 glyphs, emoji, or product icons scrolling across the page between sections (`marquee` component)
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
71
  - **Tilted `photoCard`s** — testimonial or feature cards at ±2–4° with `photoCard` or CSS `transform: rotate()`
72
72
  - **Memphis-style geometric shapes** between sections — use `decorate(pattern:'circles')` or CSS background shapes
73
73
  - **Social proof as ticker** — a `marquee` of short quotes or star ratings, not a 3-col grid
@@ -1,3 +1,94 @@
1
+ ## Multi-page sites — the right patterns
2
+
3
+ ### Shared layout component
4
+
5
+ When a project has more than one page, **never repeat the nav and footer in every spec**. Extract them into a shared component:
6
+
7
+ ```js
8
+ // src/components/layout.js
9
+ import { nav, footer, button } from '@invisibleloop/pulse/ui'
10
+
11
+ export const NAV_LINKS = [
12
+ { label: 'Home', href: '/' },
13
+ { label: 'About', href: '/about' },
14
+ { label: 'Blog', href: '/blog' },
15
+ ]
16
+
17
+ export const layout = ({ content, activePath = '' }) => `
18
+ ${nav({
19
+ logo: 'My Site',
20
+ logoHref: '/',
21
+ links: NAV_LINKS.map(l => ({
22
+ ...l,
23
+ attrs: activePath === l.href ? 'aria-current="page"' : '',
24
+ })),
25
+ action: button({ label: 'Sign up', href: '/signup', variant: 'primary', size: 'sm' }),
26
+ })}
27
+ <main id="main-content">
28
+ ${content}
29
+ </main>
30
+ ${footer({
31
+ logo: 'My Site',
32
+ links: NAV_LINKS,
33
+ legal: '© 2026 My Site',
34
+ })}
35
+ `
36
+ ```
37
+
38
+ Each page spec uses it:
39
+ ```js
40
+ import { layout } from '../components/layout.js'
41
+
42
+ export default {
43
+ route: '/about',
44
+ view: () => layout({
45
+ activePath: '/about',
46
+ content: `<h1>About us</h1>`,
47
+ }),
48
+ }
49
+ ```
50
+
51
+ **Rules:**
52
+ - Add new nav links in `src/components/layout.js` only — never hardcode nav links in individual page specs.
53
+ - Pass `activePath` so the active nav item gets `aria-current="page"` — this is required for Lighthouse accessibility.
54
+ - The `layout()` component must include `<main id="main-content">` — do not add it again inside `content`.
55
+
56
+ ### Skip link
57
+
58
+ Add a skip link before the nav for keyboard users. It must be the first focusable element on every page:
59
+
60
+ ```js
61
+ // src/components/layout.js
62
+ export const SKIP_LINK = `<a href="#main-content" class="skip-link">Skip to main content</a>`
63
+
64
+ // public/app.css
65
+ .skip-link {
66
+ position: absolute;
67
+ left: -9999px;
68
+ top: var(--ui-space-2);
69
+ z-index: 9999;
70
+ padding: var(--ui-space-2) var(--ui-space-4);
71
+ background: var(--ui-accent);
72
+ color: var(--ui-bg);
73
+ font-weight: 600;
74
+ }
75
+ .skip-link:focus { left: var(--ui-space-2); }
76
+ ```
77
+
78
+ Include it at the top of every layout: `${SKIP_LINK}${nav({...})}`.
79
+
80
+ ### Active nav state
81
+
82
+ The active page link must have `aria-current="page"`. Without it Lighthouse accessibility fails.
83
+
84
+ The pattern above uses `attrs` — alternatively, use CSS with `:not` and a body class, or check `ctx` in the server fetcher and pass a flag to the view. Any approach is fine; the output must be `aria-current="page"` on the matching link.
85
+
86
+ ### Shared state across pages
87
+
88
+ Use the global store (`pulse.store.js`) for state that needs to be consistent across pages — user session, cart count, theme preference. Do not duplicate server fetchers across specs for the same data.
89
+
90
+ See `pulse://guide/server` for the full store API.
91
+
1
92
  ## Site navigation
2
93
 
3
94
  Projects define navigation in src/components/layout.js via a NAV_LINKS array.