@zohofinance/commerce 1.0.0 → 1.1.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.
Files changed (127) hide show
  1. package/README.md +59 -0
  2. package/package.json +2 -2
  3. package/ts-dist/mocks/init-theme/.claude/CLAUDE.md +6 -0
  4. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/SKILL.md +242 -0
  5. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/inputs/figma.md +81 -0
  6. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/inputs/screenshot.md +54 -0
  7. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/inputs/url.md +88 -0
  8. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/spec-table.md +102 -0
  9. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/verification/accessibility.md +110 -0
  10. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/verification/performance.md +127 -0
  11. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/verification/tokens.md +87 -0
  12. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-section-from-design.20260528/verification/visual-diff.md +123 -0
  13. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-theme-builder.20260528/SKILL.md +605 -0
  14. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-theme-builder.20260528/design-tokens.md +105 -0
  15. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-theme-builder.20260528/page-json-contract.md +58 -0
  16. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-theme-builder.20260528/qa-accessibility.md +35 -0
  17. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-theme-builder.20260528/reference-lookup.md +94 -0
  18. package/ts-dist/mocks/init-theme/.claude/skills/.archive/zcommerce-theme-builder.20260528/section-anatomy.md +109 -0
  19. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/SKILL.md +313 -0
  20. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/agent-hierarchy.md +142 -0
  21. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/ask-developer.md +113 -0
  22. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/bootstrap.md +260 -0
  23. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/build-metrics.md +89 -0
  24. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/code-quality.md +186 -0
  25. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/common-pages-agent.md +97 -0
  26. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/dev-server-and-urls.md +136 -0
  27. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/exemplars.md +79 -0
  28. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/figma-discovery.md +138 -0
  29. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/figma-extraction.md +132 -0
  30. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/image-sources.md +84 -0
  31. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/inputs/guidelines-frame.md +178 -0
  32. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/page-builder.md +322 -0
  33. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/page-orchestrator.md +199 -0
  34. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/parallel-fanout.md +152 -0
  35. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/scripts/a11y.mjs +129 -0
  36. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/scripts/lint-section.mjs +502 -0
  37. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/scripts/perf.mjs +134 -0
  38. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/scripts/pixel-diff.mjs +206 -0
  39. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/scripts/record-build-metrics.mjs +118 -0
  40. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/section-builder.md +131 -0
  41. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/subagent-escalation.md +75 -0
  42. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/transcription-rules.md +98 -0
  43. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/verification.md +227 -0
  44. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/visual-verify-loop.md +117 -0
  45. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/_index.md +4 -0
  46. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/assets.md +105 -0
  47. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/best-practices.md +212 -0
  48. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/custom-scripts.md +294 -0
  49. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/i18n.md +155 -0
  50. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/introduction.md +91 -0
  51. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/layouts.md +158 -0
  52. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/pages.md +156 -0
  53. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/reference/_index.md +4 -0
  54. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/reference/filters.md +419 -0
  55. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/reference/global-objects.md +415 -0
  56. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/reference/schema-types.md +926 -0
  57. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/reference/tags.md +259 -0
  58. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/sections.md +203 -0
  59. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/setup-guide.md +424 -0
  60. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/snippets.md +70 -0
  61. package/ts-dist/mocks/init-theme/.claude/skills/theme-from-figma/zcommerce-docs/theme-structure.md +252 -0
  62. package/ts-dist/mocks/init-theme/.cursor/PLAYBOOK.md +144 -0
  63. package/ts-dist/mocks/init-theme/.cursor/README.md +6 -0
  64. package/ts-dist/mocks/init-theme/.cursor/agents/figma-desktop-mobile-parallel.md +48 -0
  65. package/ts-dist/mocks/init-theme/.cursor/agents/visual-compare.md +221 -0
  66. package/ts-dist/mocks/init-theme/.cursor/commands/add-field.md +83 -0
  67. package/ts-dist/mocks/init-theme/.cursor/commands/add-theme-setting.md +200 -0
  68. package/ts-dist/mocks/init-theme/.cursor/commands/assemble-page.md +72 -0
  69. package/ts-dist/mocks/init-theme/.cursor/commands/audit-page.md +57 -0
  70. package/ts-dist/mocks/init-theme/.cursor/commands/build-page-sections.md +110 -0
  71. package/ts-dist/mocks/init-theme/.cursor/commands/build-section.md +418 -0
  72. package/ts-dist/mocks/init-theme/.cursor/commands/build-theme.md +307 -0
  73. package/ts-dist/mocks/init-theme/.cursor/commands/compare-section.md +358 -0
  74. package/ts-dist/mocks/init-theme/.cursor/commands/create-theme.md +77 -0
  75. package/ts-dist/mocks/init-theme/.cursor/commands/extract-tokens.md +43 -0
  76. package/ts-dist/mocks/init-theme/.cursor/commands/fix-section.md +92 -0
  77. package/ts-dist/mocks/init-theme/.cursor/commands/impact.md +50 -0
  78. package/ts-dist/mocks/init-theme/.cursor/commands/qa-a11y.md +66 -0
  79. package/ts-dist/mocks/init-theme/.cursor/commands/qa-i18n.md +110 -0
  80. package/ts-dist/mocks/init-theme/.cursor/commands/qa-layout.md +127 -0
  81. package/ts-dist/mocks/init-theme/.cursor/commands/qa-performance.md +72 -0
  82. package/ts-dist/mocks/init-theme/.cursor/commands/qa-section.md +84 -0
  83. package/ts-dist/mocks/init-theme/.cursor/commands/qa-sections.md +48 -0
  84. package/ts-dist/mocks/init-theme/.cursor/commands/qa-theme.md +85 -0
  85. package/ts-dist/mocks/init-theme/.cursor/commands/redesign-section.md +177 -0
  86. package/ts-dist/mocks/init-theme/.cursor/commands/refactor-section.md +185 -0
  87. package/ts-dist/mocks/init-theme/.cursor/commands/sync-theme-settings.md +132 -0
  88. package/ts-dist/mocks/init-theme/.cursor/commands/validate-page.md +60 -0
  89. package/ts-dist/mocks/init-theme/.cursor/commands/validate-section-group.md +88 -0
  90. package/ts-dist/mocks/init-theme/.cursor/commands/validate-theme-settings.md +119 -0
  91. package/ts-dist/mocks/init-theme/.cursor/examples/design-instructions-template.md +151 -0
  92. package/ts-dist/mocks/init-theme/.cursor/mcp.json +18 -0
  93. package/ts-dist/mocks/init-theme/.cursor/rules/README.md +160 -0
  94. package/ts-dist/mocks/init-theme/.cursor/rules/accessibility.mdc +24 -0
  95. package/ts-dist/mocks/init-theme/.cursor/rules/add-field.mdc +222 -0
  96. package/ts-dist/mocks/init-theme/.cursor/rules/add-theme-setting.mdc +39 -0
  97. package/ts-dist/mocks/init-theme/.cursor/rules/assemble-page.mdc +130 -0
  98. package/ts-dist/mocks/init-theme/.cursor/rules/audit-page.mdc +92 -0
  99. package/ts-dist/mocks/init-theme/.cursor/rules/build-section.mdc +178 -0
  100. package/ts-dist/mocks/init-theme/.cursor/rules/build-theme.mdc +47 -0
  101. package/ts-dist/mocks/init-theme/.cursor/rules/compare-section.mdc +94 -0
  102. package/ts-dist/mocks/init-theme/.cursor/rules/create-theme.mdc +78 -0
  103. package/ts-dist/mocks/init-theme/.cursor/rules/css.mdc +137 -0
  104. package/ts-dist/mocks/init-theme/.cursor/rules/design-spec-extraction.mdc +255 -0
  105. package/ts-dist/mocks/init-theme/.cursor/rules/extract-tokens.mdc +43 -0
  106. package/ts-dist/mocks/init-theme/.cursor/rules/figma-mcp-resolution.mdc +40 -0
  107. package/ts-dist/mocks/init-theme/.cursor/rules/fix-section.mdc +154 -0
  108. package/ts-dist/mocks/init-theme/.cursor/rules/general.mdc +30 -0
  109. package/ts-dist/mocks/init-theme/.cursor/rules/i18n.mdc +85 -0
  110. package/ts-dist/mocks/init-theme/.cursor/rules/javascript.mdc +85 -0
  111. package/ts-dist/mocks/init-theme/.cursor/rules/liquid-no-hardcoded-strings.mdc +42 -0
  112. package/ts-dist/mocks/init-theme/.cursor/rules/liquid.mdc +171 -0
  113. package/ts-dist/mocks/init-theme/.cursor/rules/mobile-desktop-setting-meta.mdc +34 -0
  114. package/ts-dist/mocks/init-theme/.cursor/rules/naming-convention.mdc +64 -0
  115. package/ts-dist/mocks/init-theme/.cursor/rules/pages.mdc +120 -0
  116. package/ts-dist/mocks/init-theme/.cursor/rules/performance.mdc +51 -0
  117. package/ts-dist/mocks/init-theme/.cursor/rules/qa-section.mdc +134 -0
  118. package/ts-dist/mocks/init-theme/.cursor/rules/redesign-section.mdc +147 -0
  119. package/ts-dist/mocks/init-theme/.cursor/rules/schema-sync.mdc +110 -0
  120. package/ts-dist/mocks/init-theme/.cursor/rules/schema.mdc +360 -0
  121. package/ts-dist/mocks/init-theme/.cursor/rules/share-urls.mdc +68 -0
  122. package/ts-dist/mocks/init-theme/.cursor/skills/browser-verify.md +128 -0
  123. package/ts-dist/mocks/init-theme/.cursor/skills/design-tokens.md +105 -0
  124. package/ts-dist/mocks/init-theme/.cursor/skills/page-json-contract.md +87 -0
  125. package/ts-dist/mocks/init-theme/.cursor/skills/qa-accessibility.md +35 -0
  126. package/ts-dist/mocks/init-theme/.cursor/skills/reference-lookup.md +88 -0
  127. package/ts-dist/mocks/init-theme/.cursor/skills/section-anatomy.md +173 -0
@@ -0,0 +1,102 @@
1
+ # The Design Spec Table
2
+
3
+ The spec table is the build contract between intake and code. **Every value the section must produce is enumerated here in pixels / hex / numeric units before any Liquid is written.** Self-compare against this table is what makes the build pass on the first try.
4
+
5
+ ## Columns
6
+
7
+ | # | Element | Property | Desktop | Mobile | Token / Tailwind | Source | Notes |
8
+ |---|---------|----------|--------:|-------:|------------------|--------|-------|
9
+ | 1 | section wrapper | padding-y | 64 | 40 | `py-theme-16 md:py-theme-16` + style | figma-context.json#node-12:3 | uses section padding var |
10
+
11
+ - `#` — row number, used in self-compare reports.
12
+ - `Element` — selector-style identifier. Use descriptive names — `hero h1`, `cta button`, `card image`. Be consistent so verification can match by name.
13
+ - `Property` — one CSS property per row. **Never** combine (no "padding: 64 40 32 40"). One row per value.
14
+ - `Desktop` / `Mobile` — numeric value. Use units `px`, `%`, `rem` consistently; default to `px`. Leave blank if same as desktop and the next-up breakpoint covers it.
15
+ - `Token / Tailwind` — the Tailwind class or CSS variable that produces this value. Mandatory before stage 4 begins (this is what gets typed into the `.liquid` file).
16
+ - `Source` — pointer back to the intake artefact. For Figma: `figma-context.json#<node-id>`. For screenshot: `screenshot-desktop.png#estimated`. For URL: `computed-styles-desktop.json#<index>`.
17
+ - `Notes` — anything the build needs to know (e.g. "uses dynamic value from section.settings.padding_top — schema default = 64").
18
+
19
+ ## Required row coverage
20
+
21
+ For every visible element in the section, the table **must** have rows for:
22
+
23
+ | Element type | Required rows |
24
+ |---|---|
25
+ | **Section wrapper** | tag, padding-top, padding-bottom, padding-x, background, max-width |
26
+ | **Any container** | display, flex-direction OR grid-template-*, gap, justify-content, align-items, padding |
27
+ | **Text** | font-family, font-size, font-weight, line-height, letter-spacing, color, text-align |
28
+ | **Image** | width, height OR aspect-ratio, object-fit, border-radius |
29
+ | **Button / link** | padding-x, padding-y, font-size, font-weight, color, background, border, border-radius, hover-state |
30
+ | **Icon** | size (w/h), color, stroke-width if SVG |
31
+ | **Card / surface** | background, border, border-radius, box-shadow, padding |
32
+ | **Divider** | thickness, color, margin |
33
+
34
+ If a property is omitted because it's the browser default, that's fine — but mark it explicitly in a "defaults" row at the top so future readers know it was intentional.
35
+
36
+ ## Worked example — Hero banner section (Figma input)
37
+
38
+ ```
39
+ # Element Property Desktop Mobile Token / Tailwind Source Notes
40
+ 1 section wrapper padding-y 96 64 py-theme-24 md:py-theme-32 figma-context.json#node-12:3 dynamic via schema
41
+ 2 section wrapper padding-x 80 16 px-theme-20 md:px-theme-4 figma-context.json#node-12:3 also --section-padding-x
42
+ 3 section wrapper background — — bg-primary figma-vars#color/primary #1F2937
43
+ 4 section wrapper min-height 640 auto min-h-[640px] md:min-h-0 figma-context.json#node-12:3
44
+ 5 content grid display grid flex grid md:grid-cols-2 gap-theme-8 figma-context.json#node-12:5 mobile = column
45
+ 6 content grid gap 32 24 gap-theme-8 md:gap-theme-6 figma-context.json#node-12:5
46
+ 7 hero h1 font-family — — font-display figma-vars#font/display Inter
47
+ 8 hero h1 font-size 60 40 text-[60px] md:text-[40px] figma-context.json#node-12:8 also fluid clamp option
48
+ 9 hero h1 font-weight 700 700 font-bold figma-context.json#node-12:8
49
+ 10 hero h1 line-height 1.05 1.1 leading-[1.05] md:leading-[1.1] figma-context.json#node-12:8
50
+ 11 hero h1 color — — text-on-primary figma-vars#color/on-primary #FFFFFF
51
+ 12 cta button padding 16x32 14x28 px-theme-8 py-theme-4 figma-context.json#node-12:14
52
+ 13 cta button background — — bg-accent figma-vars#color/accent #FB923C
53
+ 14 cta button color — — text-on-accent figma-vars#color/on-accent
54
+ 15 cta button border-radius 8 8 rounded-theme-button figma-vars#radius/sm
55
+ 16 cta button font-size 18 16 text-lg md:text-base figma-context.json#node-12:14
56
+ 17 hero image aspect-ratio 4/3 4/5 aspect-[4/3] md:aspect-[4/5] figma-context.json#node-12:20
57
+ 18 hero image object-fit — — object-cover figma-context.json#node-12:20
58
+ ```
59
+
60
+ This is the contract the section file is built against. After build, the verification pipeline scans the generated `.liquid` and asserts every row.
61
+
62
+ ## Worked example — Screenshot input (less precise)
63
+
64
+ Same columns, but values are best-estimate and many rows will carry `?`:
65
+
66
+ ```
67
+ # Element Property Desktop Mobile Token / Tailwind Source Notes
68
+ 1 section padding-y ~64 ~40 py-theme-16 md:py-theme-10 screenshot-desktop.png ESTIMATED — confirm
69
+ 2 section background #FAFAFA — bg-neutral-50 screenshot-desktop.png sampled, ΔE 1.2 from token
70
+ 3 card border-radius 12 12 rounded-xl screenshot-desktop.png
71
+ 4 card gap ? ? gap-theme-? screenshot-desktop.png ASK USER — could be 16 or 24
72
+ ```
73
+
74
+ The `ASK USER` rows must be resolved before stage 4 begins. Build is blocked otherwise.
75
+
76
+ ## Worked example — URL input
77
+
78
+ ```
79
+ # Element Property Desktop Mobile Token / Tailwind Source Notes
80
+ 1 section padding 80x120 32x16 py-theme-20 px-theme-30 computed-styles-desktop.json#3 source uses raw px
81
+ 2 section background #FFFFFF — bg-background mapped: #FFFFFF → --color-background exact match
82
+ 3 h1 font-size 48 32 text-5xl md:text-3xl computed-styles-desktop.json#7
83
+ 4 h1 font-family Inter Inter font-sans font-requests.txt: Inter-Bold.woff2 source loads Inter
84
+ ```
85
+
86
+ URL inputs are mid-fidelity: spacing and sizes are exact (computed styles don't lie), but you still need to map raw colors to the target theme's variables.
87
+
88
+ ## Hard rules
89
+
90
+ 1. **One property per row.** Compound CSS (padding shorthand, font shorthand) is forbidden in the table.
91
+ 2. **No empty Token column** when entering stage 4. If a row's Tailwind/CSS variable is unknown, resolve it first.
92
+ 3. **No build before approval** for screenshot/URL inputs — the user reviews the spec to catch misreads.
93
+ 4. **Persist the table** to `.claude/scratchpad/<section_name>/spec-table.md` (markdown) and reload it for stage 5 verification.
94
+
95
+ ## Verifying Tailwind class names per target theme
96
+
97
+ The example tables above use classes like `py-theme-16`, `gap-theme-8`, `rounded-theme-button`, `max-w-container-theme`. These are defined in **each theme's own** `tailwind.config.js` under `theme.extend.spacing` / `borderRadius` / `maxWidth`.
98
+
99
+ Before finalizing the spec, **`cat tailwind.config.js`** and confirm every Tailwind theme-* class the spec references has a matching extend entry. If a class is missing:
100
+
101
+ - The class is likely missing because the underlying CSS variable hasn't been added to `settings/theme.json` either. Add both, with user approval.
102
+ - Or, fall back to **arbitrary-value Tailwind** (`py-[64px]`, `gap-[24px]`, `rounded-[8px]`) — these always work but bypass the theme's design token system. Use sparingly and only for one-off values that don't recur elsewhere in the theme.
@@ -0,0 +1,110 @@
1
+ # Gate 3 — Accessibility (WCAG 2.1 AA)
2
+
3
+ Run axe-core against the rendered section and assert zero critical/serious violations.
4
+
5
+ ## Two ways to run
6
+
7
+ ### Option A — CLI build path (most accurate, slowest)
8
+
9
+ `npx @zohofinance/commerce theme build` already runs axe-core through puppeteer against the dev server. Run it and parse the output:
10
+
11
+ ```bash
12
+ cd <theme_name>
13
+ npx @zohofinance/commerce theme build
14
+ ```
15
+
16
+ This spins up puppeteer, runs axe with tags `wcag2a, wcag2aa, wcag21a, wcag21aa`, and prints critical/serious violations. Failing causes the build to halt. Read the console output and surface the relevant violations.
17
+
18
+ **Use this for the final gate check before declaring done.** Don't use it in tight iteration loops — it's slow.
19
+
20
+ ### Option B — Per-section axe injection (fast, for iteration)
21
+
22
+ Faster for the inner loop. With Claude in Chrome:
23
+
24
+ 1. `navigate` to the section's host page on the dev server.
25
+ 2. `javascript_tool` — inject axe-core from CDN and run scoped to the section:
26
+
27
+ ```js
28
+ const s = document.createElement('script');
29
+ s.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
30
+ s.onload = async () => {
31
+ const results = await axe.run(
32
+ document.querySelector('#section-<section_id>'), // scope to just this section
33
+ {
34
+ runOnly: { type: 'tag', values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'] },
35
+ resultTypes: ['violations'],
36
+ },
37
+ );
38
+ window.__axeResult = JSON.stringify(results, null, 2);
39
+ };
40
+ document.head.appendChild(s);
41
+ ```
42
+
43
+ 3. Wait ~1s, then read back: `JSON.parse(window.__axeResult)`.
44
+ 4. Filter to `impact: critical` and `impact: serious`. These fail the gate.
45
+
46
+ ## Output format
47
+
48
+ ```
49
+ GATE 3 — A11Y (axe-core, WCAG 2.1 AA)
50
+ SCOPED TO: #section-<id>
51
+
52
+ PASS no critical violations
53
+ PASS no serious violations
54
+
55
+ MODERATE (1) — flagged, does not fail gate
56
+ [1] color-contrast — 1 node
57
+ target: .badge.bg-primary text-on-primary
58
+ contrast: 4.3:1 (needs 4.5:1 for AA)
59
+ Fix: darken --color-primary or use a different text token on the badge.
60
+
61
+ MINOR (0)
62
+ ```
63
+
64
+ ## What blocks the gate
65
+
66
+ Critical or serious violations from any of these rules **must** be fixed:
67
+
68
+ | axe rule | Why it matters |
69
+ |---|---|
70
+ | `image-alt`, `image-redundant-alt` | Screen readers need alt text |
71
+ | `color-contrast` (4.5:1 normal, 3:1 large) | Low-vision readability |
72
+ | `label`, `form-field-multiple-labels` | Form input labelling |
73
+ | `link-name`, `button-name` | Interactive elements need accessible names |
74
+ | `heading-order` | Heading hierarchy must be logical (`h1` → `h2` → `h3`) |
75
+ | `aria-valid-attr`, `aria-allowed-attr` | ARIA attribute correctness |
76
+ | `landmark-one-main`, `landmark-unique` | Page landmark structure |
77
+ | `frame-title` | iframes need titles |
78
+ | `html-has-lang` | (layout-level — not section's concern) |
79
+ | `bypass` | Skip navigation (header section's concern) |
80
+
81
+ ## Section-author quick checks (catch before running axe)
82
+
83
+ Most section a11y issues are predictable. Before running axe, eyeball the section file for:
84
+
85
+ - **All `<img>` tags** — does each have `alt="{{ ... }}"` or `alt=""` (decorative)? Reject `alt="image"` placeholder strings.
86
+ - **All `<a>` and `<button>`** — does each have meaningful text content, or `aria-label` if icon-only? Reject `<a><i class="icon"></i></a>` with no label.
87
+ - **All `<input>` and `<select>`** — paired with a `<label for="...">` or wrapped in one?
88
+ - **Heading hierarchy** — does the section start with `<h2>` (most sections shouldn't have `<h1>` — that's the page title in the layout)?
89
+ - **Color contrast** — for any text-on-color combination, verify the theme tokens used pass 4.5:1. The most common failure is light text on a non-primary brand color.
90
+ - **Focus states** — all interactive elements need visible `:focus-visible` styles. Tailwind's `focus:ring-2 focus:ring-offset-2` is a safe default.
91
+ - **Decorative images** — when an image is decorative (next to text that already describes it), use `aria-hidden="true"` AND `alt=""`. Look at existing hero-banner sections across themes for good examples.
92
+
93
+ ## Color-contrast specifics
94
+
95
+ The most-failed rule. Procedure to verify:
96
+
97
+ 1. For each text-on-background pair in the section, resolve both to actual hex (consult `figma-variables.json` and `theme.json`).
98
+ 2. Compute contrast ratio: `(L1 + 0.05) / (L2 + 0.05)` where `L1` is the lighter luminance.
99
+ 3. Targets: ≥ 4.5:1 normal text, ≥ 3:1 for 18pt+ or 14pt+ bold (large text).
100
+
101
+ If a contrast pair fails, the fix is usually one of:
102
+ - Use a darker variant of the background (`--color-primary` → `--color-primary-dark`).
103
+ - Use a different text token (`--color-on-primary` instead of `--color-heading`).
104
+ - Add a semi-transparent overlay between background image and text (common for hero sections).
105
+
106
+ Don't fix by hardcoding hex values — that breaks customization.
107
+
108
+ ## Iteration
109
+
110
+ If the gate fails, fix in the section file (not in `theme.json` unless the fix is theme-wide), and re-run option B. Run option A once at the end for final sign-off.
@@ -0,0 +1,127 @@
1
+ # Gate 4 — Performance (Lighthouse)
2
+
3
+ Run Lighthouse against the section's host page, assert performance score and Core Web Vitals targets.
4
+
5
+ ## Two ways to run
6
+
7
+ ### Option A — CLI build path
8
+
9
+ Same `npx @zohofinance/commerce theme build` runs Lighthouse via puppeteer. Failing scores halt the build.
10
+
11
+ ```bash
12
+ cd <theme_name>
13
+ npx @zohofinance/commerce theme build
14
+ ```
15
+
16
+ Reads from the dev server URL (or its own ephemeral server) and prints the score per category. Use this once at the end of iteration.
17
+
18
+ ### Option B — Standalone Lighthouse via Node
19
+
20
+ For per-section iteration, run Lighthouse from this theme's `node_modules` rather than relying on a global install:
21
+
22
+ ```bash
23
+ # from the theme directory, with dev server running on port 3000:
24
+ node ./node_modules/lighthouse/cli.js \
25
+ http://localhost:3000/<page_route> \
26
+ --output=json \
27
+ --output-path=./.claude/scratchpad/<section>/lighthouse.json \
28
+ --chrome-flags="--headless --no-sandbox" \
29
+ --only-categories=performance \
30
+ --form-factor=mobile \
31
+ --quiet
32
+ ```
33
+
34
+ If lighthouse is not installed, add it as a one-shot:
35
+ ```bash
36
+ npm install --no-save lighthouse
37
+ node ./node_modules/lighthouse/cli.js http://localhost:3000/<page_route> --quiet
38
+ ```
39
+
40
+ **Quick check via Claude in Chrome.** For LCP and CLS only (skipping the full Lighthouse run), inject this with `javascript_tool`:
41
+
42
+ ```js
43
+ (async () => {
44
+ const lcp = await new Promise(r => {
45
+ new PerformanceObserver(list => {
46
+ const entries = list.getEntries();
47
+ r(entries[entries.length - 1].renderTime || entries[entries.length - 1].loadTime);
48
+ }).observe({ type: 'largest-contentful-paint', buffered: true });
49
+ setTimeout(() => r(null), 3000);
50
+ });
51
+ let cls = 0;
52
+ new PerformanceObserver(list => list.getEntries().forEach(e => { if (!e.hadRecentInput) cls += e.value; }))
53
+ .observe({ type: 'layout-shift', buffered: true });
54
+ await new Promise(r => setTimeout(r, 1000));
55
+ return { lcp, cls };
56
+ })();
57
+ ```
58
+
59
+ This is good enough for inner-loop iteration. Run real Lighthouse once at the end for the official gate.
60
+
61
+ ## Targets
62
+
63
+ Default targets per the project's expectations. Adjust only if the user asks.
64
+
65
+ | Metric | Target | Failure |
66
+ |---|---|---|
67
+ | Performance score | ≥ 90 | < 90 |
68
+ | Largest Contentful Paint (LCP) | < 2.5s | ≥ 2.5s |
69
+ | Cumulative Layout Shift (CLS) | < 0.1 | ≥ 0.1 |
70
+ | Total Blocking Time (TBT) | < 200ms | ≥ 200ms |
71
+ | First Contentful Paint (FCP) | < 1.8s | ≥ 1.8s |
72
+ | Speed Index | < 3.4s | ≥ 3.4s |
73
+
74
+ Mobile and desktop are both measured. Mobile is the harder bar — desktop usually passes if mobile does.
75
+
76
+ ## Output format
77
+
78
+ ```
79
+ GATE 4 — PERFORMANCE (Lighthouse)
80
+ PAGE: /home
81
+
82
+ desktop
83
+ Performance 96 · LCP 1.4s · CLS 0.01 · TBT 80ms · FCP 0.9s
84
+ PASS
85
+
86
+ mobile (simulated Slow 4G / 4x CPU)
87
+ Performance 88 · LCP 2.8s · CLS 0.04 · TBT 240ms · FCP 1.6s
88
+ FAIL — LCP 2.8s (> 2.5s), TBT 240ms (> 200ms)
89
+
90
+ Suspects (section-attributable):
91
+ [1] hero image — 480KB JPEG, no width/height → contributes to LCP delay AND CLS shift.
92
+ Fix: serve WebP, add explicit width/height attrs, mark loading="eager" on above-fold image.
93
+ [2] product-collection-carousel.js — 38KB, blocking on parse.
94
+ Fix: confirm `defer` attribute is set; consider lazy-loading the script if section is below fold.
95
+ ```
96
+
97
+ ## Section-author quick checks (catch before running Lighthouse)
98
+
99
+ Most perf issues are predictable. Before running Lighthouse, audit the section file for:
100
+
101
+ - **Above-fold images** — explicit `width` and `height` attributes (prevents CLS). `loading="eager"` and `fetchpriority="high"` on the LCP image. Use `image_url` filter so the platform serves optimized variants.
102
+ - **Below-fold images** — `loading="lazy"`. Still need width/height.
103
+ - **Background images** — use CSS `background-image` with `image-set()` and explicit `aspect-ratio` on the container.
104
+ - **Fonts** — preload only the actually-used weights. `<link rel="preload" as="font" type="font/woff2" crossorigin>` for above-fold typography. Prefer `.woff2`.
105
+ - **JS** — all `<script>` tags have `defer` (per `CLAUDE.md`). Confirm no inline `<script>` blocks doing render-blocking work.
106
+ - **CSS** — section-specific styles inside `{% style %}` and scoped to `#section-{{ section.id }}` (already in the file anatomy). Avoid `@import` inside section CSS.
107
+ - **Animations** — use `transform` and `opacity` (compositor-only). Avoid animating `width`, `height`, `top`, `left`, `margin` — they cause layout thrash.
108
+ - **Web components** — load only the components this section uses. Don't pull in unused storefront JS.
109
+
110
+ ## When a section fails perf
111
+
112
+ Diagnosis steps:
113
+
114
+ 1. Open Chrome DevTools Performance panel via `javascript_tool` (or rely on Lighthouse's "Opportunities" output).
115
+ 2. Identify which network requests or JS executions are attributable to this section. Sometimes the slow path is in a different section sharing a snippet.
116
+ 3. Fix in the section file first. If the fix needs to live in a shared snippet, **flag it** — changing a snippet affects every section using it. Run `impact <theme> <snippet>` (from `zcommerce-theme-builder`) to see the blast radius before editing.
117
+
118
+ ## Don't optimize blindly
119
+
120
+ If a section passes perf but fails token-match or visual-diff, fix those first. Perf regressions during iteration almost always come from added content or animation — not from the original build.
121
+
122
+ ## Mobile-specific gotchas
123
+
124
+ - Lighthouse simulates Slow 4G + 4× CPU throttling. Real devices may be faster, but the simulation is the gate.
125
+ - A large hero image is the #1 LCP killer. Set `fetchpriority="high"` on the LCP image, preload it in the layout if it's predictable.
126
+ - Web font flashes — use `font-display: swap` in `@font-face` declarations.
127
+ - Layout shifts from late-loading content — reserve space with `min-height` or `aspect-ratio` on containers.
@@ -0,0 +1,87 @@
1
+ # Gate 2 — Design-token exact match
2
+
3
+ Strictly Figma-only. Skipped for screenshot and URL inputs because they have no normative token source.
4
+
5
+ ## Why this gate exists
6
+
7
+ Visual diff catches "looks wrong" but misses numeric drift inside its tolerance band — 16px vs 17px both look fine but accumulate over a page. This gate asserts the **exact** Tailwind class / CSS variable / numeric value listed in each Design Spec Table row is present in the generated section file.
8
+
9
+ ## Procedure
10
+
11
+ For each row in the spec table:
12
+
13
+ 1. Read `sections/<section_name>.liquid`.
14
+ 2. Extract the relevant value:
15
+ - For a Tailwind class row (`py-theme-16`) — search for that class on the matching element.
16
+ - For a CSS variable row (`var(--color-primary)`) — search inside the `{% style %}` block or inline `style="..."`.
17
+ - For a numeric default in the schema (padding_top default = 64) — locate it in the `{% schema %}` block.
18
+ 3. Assert match.
19
+
20
+ The match scope is the element identified in the table's `Element` column. Map element names to selectors:
21
+
22
+ | Spec row Element | Selector in generated code |
23
+ |---|---|
24
+ | `section wrapper` | the top-level `<section id="section-{{ section.id }}">` |
25
+ | `content grid` / `content container` | the first child `<div>` inside the wrapper |
26
+ | `hero h1` | `<h1>` inside the section |
27
+ | `cta button` | `<a class="...btn...">` or `<button>` |
28
+ | `card` | `<article>` or `<div class="...card...">` |
29
+ | `hero image` | the largest `<img>` |
30
+
31
+ If the table uses a non-obvious element name, the build phase must include a code comment marker (`{{-- spec:hero-h1 --}}`) so verification can grep for it.
32
+
33
+ ## Output format
34
+
35
+ ```
36
+ GATE 2 — TOKEN MATCH (figma)
37
+ SPEC ROWS: 18
38
+
39
+ PASS rows 1, 2, 4, 5, 6, 9, 10, 11, 13, 14, 15, 16, 17, 18 (14)
40
+ FAIL row 3 — section wrapper background
41
+ spec: bg-primary
42
+ found: bg-indigo-600 (raw Tailwind, not theme variable)
43
+ FAIL row 7 — hero h1 font-family
44
+ spec: font-display
45
+ found: font-sans
46
+ FAIL row 8 — hero h1 font-size desktop
47
+ spec: text-[60px]
48
+ found: text-5xl (48px, off by 12)
49
+ MISS row 12 — cta button padding-y mobile
50
+ spec: py-theme-3.5
51
+ found: no responsive override → uses desktop value
52
+ ```
53
+
54
+ - **PASS** — exact match.
55
+ - **FAIL** — value present but wrong.
56
+ - **MISS** — value absent (no class found on the element).
57
+ - **TOLERATED** — value differs but inside tolerance and acceptable (use sparingly; record the reason).
58
+
59
+ ## Tolerance
60
+
61
+ Hard rule: this gate uses **zero numeric tolerance**. The whole point is to catch what visual diff lets through. If a value is intentionally different from the spec, the spec is wrong — update it after user approval.
62
+
63
+ The one exception is unit conversion that produces equivalent values:
64
+ - `text-base` ≡ `text-[16px]` ≡ `text-[1rem]` (assuming 16px root) — pass.
65
+ - `gap-4` ≡ `gap-[16px]` — pass.
66
+
67
+ ## Fix strategy
68
+
69
+ When this gate fails:
70
+
71
+ 1. Group failures by element. A "wrong color on hero" usually has 3–4 cascading effects in adjacent rows.
72
+ 2. Open the section file and fix all related rows in one edit.
73
+ 3. Re-run the gate.
74
+
75
+ Do not silently update the spec to match the code — that defeats the whole purpose. Spec changes require user approval.
76
+
77
+ ## Unmapped tokens
78
+
79
+ If the spec table references a variable not yet defined in `figma-variables.json` or `theme.json`, the gate fails with `UNMAPPED`:
80
+
81
+ ```
82
+ UNMAPPED row 4 — section wrapper min-height
83
+ spec: var(--hero-min-height) — token does not exist in theme.json
84
+ ACTION: add token or hardcode value with user approval
85
+ ```
86
+
87
+ These must be resolved in stage 4 (build), not by suppressing the gate.
@@ -0,0 +1,123 @@
1
+ # Gate 1 — Visual diff
2
+
3
+ Render the built section in a real browser, screenshot it, compare to the input artefacts from stage 1.
4
+
5
+ ## Prerequisite
6
+
7
+ Dev server running:
8
+ ```bash
9
+ cd <theme_name>
10
+ yarn run dev --storeUrl=<your store url>
11
+ ```
12
+ Default port is 3000 unless `--port` was passed. If port 3000 is in use, the CLI auto-picks the next available port — check the CLI output.
13
+
14
+ ## Tools (concrete capture path)
15
+
16
+ **Preferred:** Claude in Chrome.
17
+ - `mcp__Claude_in_Chrome__navigate` — open the dev server URL.
18
+ - `mcp__Claude_in_Chrome__resize_window` — set viewport.
19
+ - `mcp__Claude_in_Chrome__read_page` — returns the rendered page including a visual snapshot the agent can inspect. **This is the screenshot mechanism for gate 1** — `read_page` is the tool that gives the agent the visual content, not a separate `take_screenshot` call.
20
+ - `mcp__Claude_in_Chrome__find` — locate the section element by selector or text.
21
+ - `mcp__Claude_in_Chrome__javascript_tool` — run JS for `scrollIntoView`, `getBoundingClientRect`, applying `:hover` states.
22
+ - `mcp__Claude_in_Chrome__gif_creator` — captures a GIF for animated sections; use sparingly.
23
+
24
+ **Sharing screenshots back to the user** (so they can see the comparison): save the input-side images already exist in `.claude/scratchpad/<section>/`; for the rendered side, use `read_page` to view, then if you want to surface to chat, `mcp__cowork__present_files` on the scratchpad inputs alongside an `mcp__cowork__create_artifact` HTML page showing both side-by-side.
25
+
26
+ **Fallback (no Claude in Chrome):** `mcp__workspace__web_fetch` for HTML only — confirms the section renders without Liquid errors but cannot do visual comparison. Surface this to the user and ask whether to ship without gate 1.
27
+
28
+ ## Procedure
29
+
30
+ For **each** viewport (desktop 1440, mobile 375):
31
+
32
+ 1. **`resize_window`** to the target dimensions.
33
+ 2. **`navigate`** to the correct URL — see "Resolving the section URL" below.
34
+ 3. Wait for network idle. Use `read_console_messages` to confirm no JS errors blocking render.
35
+ 4. Scroll to the section. Use `javascript_tool`:
36
+ ```js
37
+ document.querySelector('#section-<section_id>').scrollIntoView({block:'start'});
38
+ document.querySelector('#section-<section_id>').getBoundingClientRect();
39
+ ```
40
+ The section's wrapper has `id="section-{{ section.id }}"` — find it in the page DOM. If the section's wrapper has a recognizable class instead, use that.
41
+ 5. **`read_page`** to get the rendered visual — this is the "screenshot" for comparison. The bounding rect from step 4 tells you which portion of the page corresponds to the section.
42
+ 6. **Side-by-side compare** with the input artefact:
43
+
44
+ ### Resolving the section URL
45
+
46
+ The dev server routes by page slug. Find the page that hosts the section:
47
+
48
+ | Section family | Page route |
49
+ |---|---|
50
+ | Header / footer / global | any page — `/home` is fine |
51
+ | Hero / banner / homepage CTA cards | `/home` |
52
+ | Product collection grid | `/collections/<handle>` (use a known collection from mock data, or `/collections/all`) |
53
+ | Product detail | `/products/<product_id>` (use any product slug from `?format=json` on the storefront) |
54
+ | Cart | `/cart` |
55
+ | Checkout | `/checkout` |
56
+ | Search | `/search?q=<term>` |
57
+ | Blog | `/blog` or `/blog/<post-slug>` |
58
+ | Account | `/account/profile`, `/account/orders`, etc. |
59
+ | 404 | `/this-route-does-not-exist` |
60
+
61
+ If unsure, ask the user. If the section is in `pages/<page>.json`, that JSON file's name is the route (e.g. `pages/index.json` → `/home`, `pages/cart.json` → `/cart`).
62
+ - Figma input → `<scratchpad>/intake/<section>/figma-desktop.png` (or mobile)
63
+ - Screenshot input → `<scratchpad>/intake/<section>/screenshot-desktop.png`
64
+ - URL input → `<scratchpad>/intake/<section>/desktop.png`
65
+
66
+ ## What to compare
67
+
68
+ Walk the Design Spec Table top-to-bottom. For each row, eyeball-verify the rendered section matches. Focus areas:
69
+
70
+ - **Structural** — same number of elements? Same hierarchy? (Quick reject on missing elements.)
71
+ - **Alignment** — text aligned same way, columns same width, elements vertically centered correctly.
72
+ - **Spacing** — gaps between elements look the same. Counter-intuitive padding/margin mistakes are the most common visual bug.
73
+ - **Typography** — heading vs body size ratio, font weight, line height.
74
+ - **Colors** — primary/secondary/accent applied correctly. Watch for inverted color states (light text on light bg).
75
+ - **Imagery** — aspect ratio, object-fit (cover vs contain), placement.
76
+ - **States** — hover/focus states on interactive elements (use `javascript_tool` to apply `:hover` programmatically if needed).
77
+
78
+ ## Output format
79
+
80
+ ```
81
+ GATE 1 — VISUAL DIFF
82
+ DEVICE: desktop 1440
83
+ SOURCE: figma-desktop.png
84
+ RENDERED: localhost:3000/home → #section-12345
85
+
86
+ CRITICAL
87
+ [1] Hero h1 missing — Figma has two-line headline, rendered shows only one. Cause: schema field `heading_line_2` not added.
88
+ MINOR
89
+ [2] CTA button gap from heading: Figma=32, Rendered=24. Spec row 16.
90
+ [3] Hero image aspect ratio: Figma=4:3, Rendered=16:9. Spec row 17.
91
+ SUGGESTION
92
+ [4] Drop shadow on card is softer in Figma — try `shadow-md` instead of `shadow-lg`.
93
+
94
+ DEVICE: mobile 375
95
+ PASS — no critical or minor mismatches.
96
+ ```
97
+
98
+ ## Categorization rules
99
+
100
+ | Severity | Trigger |
101
+ |---|---|
102
+ | CRITICAL | Missing/extra element, wrong content, broken layout (overflow/clip), wrong major color (primary/background) |
103
+ | MINOR | Numeric drift within tolerance band 1 (spacing 3–8px, font ±2px, color ΔE 1–3, radius 3–6px) |
104
+ | SUGGESTION | Subjective polish — different shadow, slightly different easing, "could look nicer" |
105
+
106
+ CRITICAL items must be fixed before passing the gate. MINOR items can pass if the user explicitly accepts the deviation. SUGGESTION items never block the gate.
107
+
108
+ ## Common false positives
109
+
110
+ - **Font rendering differences** — Chrome/Safari/Firefox render type slightly differently. Don't flag 1px line-height drift.
111
+ - **Antialiasing** — diagonal lines and small text look slightly different in rasterized output. Trust computed styles over screenshots when in doubt.
112
+ - **Image placeholders** — the dev server may serve a placeholder image while real product data loads. Re-screenshot after refresh.
113
+ - **Animations** — if the section animates in, screenshot the end state.
114
+
115
+ ## When the dev server can't render the section
116
+
117
+ If the section file has Liquid errors, the rendered page won't include the section at all. Check:
118
+
119
+ - Terminal output from `yarn run dev` — surface any Liquid stack traces.
120
+ - Browser console — `read_console_messages`.
121
+ - The page JSON — does it actually reference the new section type?
122
+
123
+ Fix the rendering bug first, then re-run the gate.