@fracazo/design-system 0.2.0 → 0.6.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 (42) hide show
  1. package/DESIGN.md +366 -8
  2. package/README.md +77 -11
  3. package/css/motion.css +155 -0
  4. package/css/roles.css +3 -0
  5. package/dist/guardrails/eslint.d.ts +72 -5
  6. package/dist/guardrails/eslint.js +197 -29
  7. package/dist/guardrails/init.d.ts +2 -0
  8. package/dist/guardrails/init.js +65 -0
  9. package/dist/guardrails/intake.d.ts +2 -0
  10. package/dist/guardrails/intake.js +131 -0
  11. package/package.json +8 -3
  12. package/skills/product-design/SKILL.md +142 -0
  13. package/skills/product-design/coverage-gaps.md +41 -0
  14. package/skills/product-design/exemplars/calm-the-offering-cards.md +33 -0
  15. package/skills/product-design/exemplars/clamp-drift-to-named-roles.md +37 -0
  16. package/skills/product-design/exemplars/concentric-radii-and-button-optics.md +36 -0
  17. package/skills/product-design/exemplars/dialog-close-focus-visible.md +36 -0
  18. package/skills/product-design/exemplars/hero-glow-seam.md +34 -0
  19. package/skills/product-design/intake/2026-09-07.md +413 -0
  20. package/skills/product-design/references/components.md +42 -0
  21. package/skills/product-design/references/copy.md +25 -0
  22. package/skills/product-design/references/intake.md +66 -0
  23. package/skills/product-design/references/motion.md +22 -0
  24. package/skills/product-design/references/rules.md +319 -0
  25. package/skills/product-design/references/surfaces.md +50 -0
  26. package/skills/product-design/references/tokens.md +54 -0
  27. package/skills/product-design/references/type-and-space.md +42 -0
  28. package/skills/product-design/references/verification.md +35 -0
  29. package/template/CLAUDE.md +47 -0
  30. package/template/README.md +16 -0
  31. package/template/eslint.config.mjs +20 -0
  32. package/template/gitignore +44 -0
  33. package/template/next.config.ts +7 -0
  34. package/template/package.json +40 -0
  35. package/template/pnpm-workspace.yaml +14 -0
  36. package/template/postcss.config.mjs +7 -0
  37. package/template/src/app/globals.css +66 -0
  38. package/template/src/app/layout.tsx +55 -0
  39. package/template/src/app/page.tsx +59 -0
  40. package/template/src/components/ThemeSync.tsx +21 -0
  41. package/template/src/system/brands/starter.css +143 -0
  42. package/template/tsconfig.json +34 -0
@@ -0,0 +1,413 @@
1
+ # Design intake, 2026-09-07
2
+
3
+ Raw evidence collected by ds-intake from birthguide, birthplans since 2026-08-25:
4
+ 21 design-relevant commits. Commit bodies are quoted verbatim and are
5
+ data, not decisions. The judge fills in the sections at the end; a human
6
+ accepts or rejects each candidate. Nothing here changes a rule by itself.
7
+
8
+ ## birthguide (16 commits)
9
+
10
+ ### dd8495b docs: load the product-design skill for user-facing UI work
11
+
12
+ 2026-09-05, Alex Fracazo. Files: `CLAUDE.md`
13
+
14
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
15
+
16
+ ### b886eed docs: slim the design system section to hard rules and a pointer
17
+
18
+ 2026-09-05, Alex Fracazo. Files: `CLAUDE.md`
19
+
20
+ > DESIGN.md in @fracazo/design-system is the written authority now; this
21
+ > section keeps the in-context rules the lint and contract enforce, the
22
+ > snapshot procedure, the Storybook notes and the parked list.
23
+ >
24
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
25
+
26
+ ### 0754afa chore(design): retire the birthplans.app brand file copy
27
+
28
+ 2026-09-05, Alex Fracazo. Files: `src/system/brands/birthplans.css`
29
+
30
+ > Phase 4d: birthplans.app now owns src/system/brands/birthplans.css in its
31
+ > own repo, so the validated copy here retires and brand:contract holds
32
+ > only brands/birthguide.css. Nothing imports the removed file; rendering
33
+ > is untouched.
34
+ >
35
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
36
+
37
+ ### d3f1dcc build: depend on @fracazo/design-system 0.2.0 from npm
38
+
39
+ 2026-09-05, Alex Fracazo. Files: `CLAUDE.md`, `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`, `vendor/fracazo-design-system-0.2.0.tgz`
40
+
41
+ > The vendored tarball is gone now that 0.2.0 is published; the registry
42
+ > files match it byte for byte. Snapshot identical to design-baseline.json
43
+ > (349 keys), tsc clean, 429 tests, eslint 0 errors, brand checks pass.
44
+ >
45
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
46
+
47
+ ### 9ef71fb refactor: consume @fracazo/design-system for roles, components and guardrails
48
+
49
+ 2026-09-05, Alex Fracazo. Files: `src/app/globals.css`, `src/app/not-found.tsx`, `src/app/plan/edit/[token]/page.tsx`, `src/app/plan/edit/example/page.tsx`, `src/app/plan/expired/page.tsx`, `src/app/resume/page.tsx`, `src/app/unsubscribe/page.tsx`, `src/components/download/DownloadPanel.tsx`, `src/components/download/SharePanel.tsx`, `src/components/landing/OfferingChooser.tsx`, `src/components/landing/OutputExampleCards.tsx`, `src/components/mobile-link/ExampleDownloadDialog.tsx`, `src/components/plan/PlanDocuments.tsx`, `src/components/plan/PlanLink.tsx`, `src/components/program/LessonCapture.tsx`, `src/components/program/LessonChrome.tsx`, `src/components/program/LessonFlow.tsx`, `src/components/program/LessonReflect.tsx`, `src/components/program/LessonScreen.tsx`, `src/components/program/ModuleRecap.tsx`, `src/components/program/PaywallSlot.tsx`, `src/components/program/ProgramMap.tsx`, `src/components/program/SavePointScreen.tsx`, `src/components/questionnaire/DidYouKnowBar.tsx`, `src/components/questionnaire/InfoBottomSheet.tsx`, `src/components/questionnaire/QuestionScreen.tsx`, `src/components/questionnaire/QuestionnaireShell.tsx`, `src/components/questionnaire/steps/TopPriorities.tsx`, `src/components/questionnaire/steps/YourDetails.tsx`, `src/components/ui/accordion.stories.tsx`, `src/components/ui/accordion.tsx`, `src/components/ui/button.stories.tsx`, `src/components/ui/button.tsx`, `src/components/ui/calendar.stories.tsx`, `src/components/ui/calendar.tsx`, `src/components/ui/card.stories.tsx`, `src/components/ui/card.tsx`, `src/components/ui/checkbox.stories.tsx`, `src/components/ui/checkbox.tsx`, `src/components/ui/dialog.stories.tsx`, `src/components/ui/dialog.tsx`, `src/components/ui/form.stories.tsx`, `src/components/ui/form.tsx`, `src/components/ui/input.stories.tsx`, `src/components/ui/input.tsx`, `src/components/ui/label.stories.tsx`, `src/components/ui/label.tsx`, `src/components/ui/place-autocomplete.tsx`, `src/components/ui/popover.stories.tsx`, `src/components/ui/popover.tsx`, `src/components/ui/progress.stories.tsx`, `src/components/ui/progress.tsx`, `src/components/ui/radio-group.stories.tsx`, `src/components/ui/radio-group.tsx`, `src/components/ui/select.stories.tsx`, `src/components/ui/select.tsx`, `src/components/ui/sheet.stories.tsx`, `src/components/ui/sheet.tsx`, `src/components/ui/sortable-list.stories.tsx`, `src/components/ui/sortable-list.tsx`, `src/components/ui/tabs.stories.tsx`, `src/components/ui/tabs.tsx`, `src/components/ui/textarea.stories.tsx`, `src/components/ui/textarea.tsx`, `src/system/roles.css`
50
+
51
+ > Phase 4c of the design system extraction. globals.css imports the package's
52
+ > roles.css, then the brand file, and gains an @source for the package's dist
53
+ > so Tailwind generates the utilities the components use. The seventeen local
54
+ > ui components and src/system/roles.css are deleted; every import is
55
+ > codemodded to @fracazo/design-system/ui/*, the stories included. cn is
56
+ > re-exported from @/lib/utils. place-autocomplete stays local, it imports
57
+ > app code.
58
+ >
59
+ > The brand scripts are replaced by the package bins (ds-build-brand-css,
60
+ > ds-check-brand) and public/brand.css is regenerated: declarations identical,
61
+ > only the header comment changes. The ESLint colour and clamp guardrails
62
+ > come from @fracazo/design-system/eslint with our exemptions. The snapshot
63
+ > harness reads roles from the package.
64
+ >
65
+ > Verified: design snapshot identical to baseline (349 keys), tsc clean,
66
+ > 429 tests pass, eslint 0 errors, next build and storybook build succeed.
67
+ >
68
+ > The dependency is the vendored 0.2.0 tarball until that version is on npm;
69
+ > swapping it for ^0.2.0 and deleting vendor/ is the last step of this phase.
70
+ >
71
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
72
+
73
+ ### ce64587 docs: point the design system section at src/system and the package
74
+
75
+ 2026-09-05, Alex Fracazo. Files: `CLAUDE.md`
76
+
77
+ > Tokens no longer live in globals.css: roles.css and brands/birthguide.css
78
+ > under src/system are the source, with the brand contract, the generated
79
+ > public/brand.css and the two lint-chain checks described. Records the
80
+ > extraction to @fracazo/design-system and the two snapshot gotchas found
81
+ > this week (preview-server collision on .next, stale Turbopack cache after
82
+ > restructuring the CSS import graph).
83
+ >
84
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
85
+
86
+ ### 6038c47 feat(design): add the birthplans.app brand file and the status roles
87
+
88
+ 2026-09-05, Alex Fracazo. Files: `src/system/brands/birthguide.css`, `src/system/brands/birthplans.css`, `src/system/roles.css`
89
+
90
+ > SPEC_020, phase 3c of the white-label roadmap.
91
+ >
92
+ > brands/birthplans.css is the full contract with birthplans.app's real
93
+ > values, copied from that repo (fefe1cb): the same cream page and espresso
94
+ > ink with the brand hue rotated to periwinkle. Three roles it never used
95
+ > carry reasoned values and say so (headline-accent in light, chip-3 as the
96
+ > sister-product tint in BirthGuide rose, dark-brand).
97
+ >
98
+ > The contract grows by the traffic-light status ramp that product already
99
+ > relied on (status-want / -ifnec / -no, each with a -soft wash; ifnec
100
+ > aliases the highlight primitive). brands/birthguide.css defines the same
101
+ > literals, unused on BirthGuide screens today; PDFs and previews keep
102
+ > constants.ts. Contract is now 63 light, 48 dark, 2 theme, and both brand
103
+ > files satisfy it.
104
+ >
105
+ > Proof, from clean compiles: adding the roles is 12 snapshot additions and
106
+ > nothing else; swapping the import to brands/birthplans.css moves exactly
107
+ > the brand-following keys (brand, brand-ink, brand-soft, glow-1, the
108
+ > primary/ring/accent aliases, the selected-card shadow, accent-foreground
109
+ > and headline-accent in light, chip-3, dark-brand, five brand utilities,
110
+ > the form-control ring: 38 keys) and nothing else; swapping back compares
111
+ > identical. design-baseline.json and brand.css regenerated; lint and 429
112
+ > tests pass.
113
+ >
114
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
115
+
116
+ ### 8349175 refactor(design): split roles.css from the birthguide brand file
117
+
118
+ 2026-09-05, Alex Fracazo. Files: `src/app/globals.css`, `src/system/brands/birthguide.css`, `src/system/roles.css`
119
+
120
+ > SPEC_019, phase 3b of the white-label roadmap. globals.css becomes an
121
+ > importer; the token system now lives in src/system:
122
+ >
123
+ > roles.css the brand-agnostic half: dark variant, the Tailwind
124
+ > @theme mapping, radius ramp, fluid type and band
125
+ > rhythm, and the eight aliasing semantics declared
126
+ > once (they recompute inside .force-light subtrees)
127
+ > brands/birthguide.css every value, light and dark, plus the next/font
128
+ > mapping, which must stay in a @theme block so
129
+ > font-sans resolves on the element carrying the var
130
+ >
131
+ > roles.css opens with a machine-readable brand contract (57 light, 44 dark,
132
+ > 2 theme properties); scripts/check-brand.ts holds every brand file to it,
133
+ > missing or extra, and runs in pnpm lint as brand:contract. The brand.css
134
+ > generator now composes roles + brand and prints the contract in its
135
+ > header; the snapshot script collects token names from all three files.
136
+ >
137
+ > Zero visual change: snapshot compares identical (337 keys) from a clean
138
+ > compile; the published brand.css keeps every declaration except the eight
139
+ > redundant .dark alias redeclarations; Storybook builds; lint and 429
140
+ > tests pass. Note for the harness: Turbopack's persistent dev cache served
141
+ > a pre-rename stylesheet after the file restructure, so clear .next before
142
+ > snapshotting a change to the CSS import graph.
143
+ >
144
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
145
+
146
+ ### 2ec19f7 refactor(design): rename colour-literal tokens to roles
147
+
148
+ 2026-09-05, Alex Fracazo. Files: `src/app/contact/page.tsx`, `src/app/feedback/page.tsx`, `src/app/globals.css`, `src/app/guides/layout.tsx`, `src/app/plan/edit/example/page.tsx`, `src/app/privacy/page.tsx`, `src/app/refund-policy/page.tsx`, `src/app/terms/page.tsx`, `src/app/tools/baby-essentials-price-tracker/page.tsx`, `src/app/tools/contraction-timer/page.tsx`, `src/app/tools/due-date-calculator/page.tsx`, `src/app/tools/find-health-services/page.tsx`, `src/app/tools/ivf-due-date-calculator/page.tsx`, `src/app/tools/layout.tsx`, `src/app/tools/ovulation-calculator/page.tsx`, `src/components/ChatWidget.tsx`, `src/components/Footer.tsx`, `src/components/blog/ArticleHeader.tsx`, `src/components/download/PrintablePdfs.tsx`, `src/components/guides/AuthorBlock.tsx`, `src/components/landing/DualHero.tsx`, `src/components/landing/FAQ.tsx`, `src/components/landing/Guides.tsx`, `src/components/landing/OfferingChooser.tsx`, `src/components/landing/OutputExampleCards.tsx`, `src/components/landing/PhonePreview.tsx`, `src/components/landing/ProgramCurriculum.tsx`, `src/components/landing/ProgramOutcome.tsx`, `src/components/landing/ProgramPricing.tsx`, `src/components/landing/ProgramWhyItMatters.tsx`, `src/components/landing/Testimonials.tsx`, `src/components/landing/ToolAnnouncementStrip.tsx`, `src/components/landing/offerings.ts`, `src/components/mobile-link/BagTab.tsx`, `src/components/mobile-link/ContactsTab.tsx`, `src/components/mobile-link/ExamplePlanBanner.tsx`, `src/components/mobile-link/MobileLinkPage.tsx`, `src/components/mobile-link/SummaryTab.tsx`, `src/components/preview/BirthPlanPreview.tsx`, `src/components/preview/OutputPreviewTabs.tsx`, `src/components/price-tracker/DealThumb.tsx`, `src/components/price-tracker/DealsTable.tsx`, `src/components/price-tracker/PriceTracker.tsx`, `src/components/price-tracker/WeeklyDrop.tsx`, `src/components/program/ProgramIntro.tsx`, `src/components/questionnaire/DidYouKnowBar.tsx`, `src/components/shopping-guide/ProductCard.tsx`, `src/components/shopping-guide/RetailerPriceList.tsx`, `src/components/shopping-guide/ShoppingGuide.tsx`, `src/components/tools/CalculatorEmailCapture.tsx`, `src/components/tools/PregnancyProgress.tsx`, `src/stories/tokens.mdx`, `src/stories/usage.mdx`
149
+
150
+ > SPEC_018, phase 3a of the white-label roadmap. Every value keeps its
151
+ > literal; only the names move so the system reads as roles for any brand:
152
+ >
153
+ > sand/sand-2 -> band/band-2 warm-muted -> ink-3
154
+ > rose -> dark-accent dark-rose -> dark-brand
155
+ > amber(-ink/-soft/-stat) -> highlight(-ink/-soft/-deep)
156
+ > sage/lilac/periwinkle-* -> chip-1/2/3-* glow-rose/-sage -> glow-1/2
157
+ >
158
+ > The Did-you-know pill keys follow ('rose' | 'amber' -> 'brand' |
159
+ > 'highlight'), as do the two snapshot probes, the CLAUDE.md design
160
+ > section and the Storybook swatch labels. brand.css rebuilt.
161
+ >
162
+ > Zero visual change: with the rename map applied to the baseline's keys,
163
+ > 337 keys compare pairwise identical (38 renamed, 0 value differences);
164
+ > design-baseline.json regenerated. Lint clean, 429 tests pass. Legacy em
165
+ > dashes replaced in the six touched files that carried one.
166
+ >
167
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
168
+
169
+ ### 4ba04df feat(lint): guard arbitrary fluid type sizes in className
170
+
171
+ 2026-09-04, Alex Fracazo. Files: `src/components/landing/DualFinalCTA.tsx`, `src/components/landing/OfferingChooser.tsx`, `src/components/landing/ProgramPricing.tsx`, `src/components/landing/ProgramWhyItMatters.tsx`, `src/components/mobile-link/MobileLinkPage.tsx`
172
+
173
+ > Extends the SPEC_014 guardrail: text-[clamp(...)] in a className is now
174
+ > an error alongside raw colours, so new fluid sizes must become tokens
175
+ > first. The five deliberate one-offs (final CTA closing size, the
176
+ > OfferingChooser card heading, the mobile-link h1, and the two benched
177
+ > Program sections) each carry an inline disable stating why. Legacy em
178
+ > dashes replaced in the files touched.
179
+ >
180
+ > Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
181
+
182
+ ### 70ea27c test(design): probe the fluid type and band utilities in snapshots
183
+
184
+ 2026-09-04, Alex Fracazo. Files: `design-baseline.json`, `scripts/design-snapshot.ts`
185
+
186
+ > Phase 1b showed the element probes never covered landing typography, so
187
+ > the harness compared identical through an intentional type change. The
188
+ > utility layer now probes text-display, text-section-title, text-lede,
189
+ > py-band and mt-band-gap directly. design-baseline.json regenerated:
190
+ > 14 additions (7 keys x 2 themes), zero changes to existing keys.
191
+ >
192
+ > Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
193
+
194
+ ### f2daff7 feat(design): publish the brand token stylesheet
195
+
196
+ 2026-09-04, Alex Fracazo. Files: `package.json`, `public/brand.css`, `scripts/build-brand-css.ts`
197
+
198
+ > public/brand.css, served at birthguide.com.au/brand.css, is the public
199
+ > token API for the design.md initiative: the :root/.force-light and .dark
200
+ > blocks verbatim plus the mode-constant type, band rhythm and radius
201
+ > tokens, generated from globals.css by scripts/build-brand-css.ts. The
202
+ > header documents dark-mode wiring and how a sister product (birthplans.app)
203
+ > skins the same roles by overriding the brand primitives. pnpm brand:build
204
+ > regenerates it; pnpm lint now fails while it is stale (brand:check).
205
+ >
206
+ > Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
207
+
208
+ ### a0ffa71 fix(landing): converge near-miss clamp drift onto the named tokens
209
+
210
+ 2026-09-04, Alex Fracazo. Files: `src/app/globals.css`, `src/components/landing/DualHero.tsx`, `src/components/landing/Guides.tsx`
211
+
212
+ > Phase 1b of the design.md initiative, per Alex's render review:
213
+ >
214
+ > - new text-display token (with its 0.9 line-height companion) at the
215
+ > hero's exact current scale; the hero h1 adopts it, byte-identical
216
+ > - hero lede converges onto text-lede: cap drops 20px to 19px at wide
217
+ > viewports with a slight rewrap (approved from renders)
218
+ > - Guides grid gap converges onto mt-band-gap: cap grows 48px to 52px
219
+ > (approved from renders)
220
+ > - the final CTA deliberately keeps its bespoke 62px clamp (one display
221
+ > token exists; resizing the CTA was declined), and the benched Program
222
+ > sections and mobile-link h1 stay untouched
223
+ >
224
+ > Snapshot harness compares identical (its probes do not cover the two
225
+ > moved elements), so design-baseline.json needs no regeneration; the
226
+ > delta was verified against live computed styles at 1440px instead.
227
+ >
228
+ > Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
229
+
230
+ ### 0466daf refactor(design): name the fluid type and band rhythm tokens
231
+
232
+ 2026-09-04, Alex Fracazo. Files: `src/app/globals.css`, `src/components/landing/DualFinalCTA.tsx`, `src/components/landing/DualHero.tsx`, `src/components/landing/FAQ.tsx`, `src/components/landing/Guides.tsx`, `src/components/landing/ProgramCurriculum.tsx`, `src/components/landing/ProgramHowItWorks.tsx`, `src/components/landing/ProgramOutcome.tsx`, `src/components/landing/ProgramPricing.tsx`, `src/components/landing/ProgramWhyItMatters.tsx`, `src/components/landing/Testimonials.tsx`
233
+
234
+ > Adds text-section-title (with its 1.05 line-height companion), text-lede,
235
+ > spacing-band and spacing-band-gap to the theme (SPEC_017, phase 1 of the
236
+ > design.md initiative), then swaps the byte-identical clamp() literals
237
+ > across ten landing components. Legacy em dashes in globals.css comments
238
+ > replaced under the replace-on-touch rule.
239
+ >
240
+ > Zero visual change: snapshot chain baseline -> tokens-added -> after
241
+ > compares identical (323 keys); lint clean; 429 tests pass. The five
242
+ > bespoke display clamps stay untouched pending the phase 1b convergence
243
+ > decision.
244
+ >
245
+ > Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
246
+
247
+ ### 8e0c64f feat(example-plan): open a program dialog when a PDF card is tapped
248
+
249
+ 2026-09-04, Alex Fracazo. Files: `src/components/download/PrintablePdfs.tsx`, `src/components/mobile-link/ExampleDownloadDialog.tsx`, `src/components/mobile-link/SummaryTab.tsx`
250
+
251
+ > Visitors on the example plan keep tapping the download cards, and the
252
+ > inline note they got back was a dead end. The tap is intent, so it now
253
+ > opens a dialog that points at the program (the only path that produces
254
+ > all three PDFs; birthplans.app makes the plan alone). One dialog serves
255
+ > the three cards, focus returns to the tapped card on close, and the
256
+ > footer stays stacked at every width so the labels never overflow.
257
+ >
258
+ > PrintablePdfs swaps the exampleNote string for an onExampleClick
259
+ > callback and drops the per-card note state. Analytics gain
260
+ > example_download_clicked (per document type) and the cta_clicked
261
+ > location example-plan-download, so the funnel reads: wanted the PDF,
262
+ > then went to the program.
263
+ >
264
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
265
+
266
+ ### 84dfd66 feat(analytics): track every click that leads to the program or birthplans.app
267
+
268
+ 2026-09-03, Alex Fracazo. Files: `src/app/feedback/page.tsx`, `src/app/resume/page.tsx`, `src/components/BirthplansLink.tsx`, `src/components/ChatWidget.tsx`, `src/components/Footer.tsx`, `src/components/PlanShortcut.tsx`, `src/components/ProgramLink.tsx`, `src/components/SiteHeader.tsx`, `src/components/landing/OfferingCTA.tsx`, `src/components/mobile-link/ExamplePlanBanner.tsx`, `src/components/mobile-link/MobileLinkPage.tsx`, `src/components/plan/ChangeSomething.tsx`
269
+
270
+ > cta_clicked now carries ctaDestination (program or birthplans) so the two
271
+ > paths read apart in GA4 where one location labels both. BirthplansLink
272
+ > fires the event itself, which covers the footer, curriculum, guide and tool
273
+ > shortcuts, the contraction timer and the affiliates page; the plain program
274
+ > links on the footer, resume and feedback pages go through ProgramLink, and
275
+ > the plan page decision rows and chat reply links fire inline.
276
+ >
277
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
278
+
279
+ ## birthplans (5 commits)
280
+
281
+ ### 9d63e97 docs: load the product-design skill for user-facing UI work
282
+
283
+ 2026-09-05, Alex Fracazo. Files: `CLAUDE.md`
284
+
285
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
286
+
287
+ ### b295c5a docs: slim the design system section to hard rules and a pointer
288
+
289
+ 2026-09-05, Alex Fracazo. Files: `CLAUDE.md`
290
+
291
+ > DESIGN.md in @fracazo/design-system is the written authority now; this
292
+ > section keeps the in-context rules the lint and contract enforce, the
293
+ > snapshot procedure and the parked list.
294
+ >
295
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
296
+
297
+ ### 3a98e2c refactor: consume @fracazo/design-system for roles, components and guardrails
298
+
299
+ 2026-09-05, Alex Fracazo. Files: `src/app/globals.css`, `src/app/not-found.tsx`, `src/app/plan/edit/[token]/page.tsx`, `src/app/plan/edit/example/page.tsx`, `src/app/plan/expired/page.tsx`, `src/app/questionnaire/page.tsx`, `src/components/download/AccessPassChoices.tsx`, `src/components/download/DownloadPanel.tsx`, `src/components/download/PaywallPanel.tsx`, `src/components/download/PaywallSheet.tsx`, `src/components/download/SharePanel.tsx`, `src/components/preview/FittedPlanPreview.tsx`, `src/components/questionnaire/DidYouKnowBar.tsx`, `src/components/questionnaire/InfoBottomSheet.tsx`, `src/components/questionnaire/PlanReadyScreen.tsx`, `src/components/questionnaire/QuestionCard.tsx`, `src/components/questionnaire/QuestionScreen.tsx`, `src/components/questionnaire/QuestionnaireShell.tsx`, `src/components/questionnaire/ReviewScreen.tsx`, `src/components/questionnaire/SectionCardGrid.tsx`, `src/components/questionnaire/TwoPaneBuilder.tsx`, `src/components/questionnaire/steps/YourDetails.tsx`, `src/components/ui/accordion.tsx`, `src/components/ui/button.tsx`, `src/components/ui/calendar.tsx`, `src/components/ui/card.tsx`, `src/components/ui/checkbox.tsx`, `src/components/ui/dialog.tsx`, `src/components/ui/form.tsx`, `src/components/ui/input.tsx`, `src/components/ui/label.tsx`, `src/components/ui/place-autocomplete.tsx`, `src/components/ui/popover.tsx`, `src/components/ui/progress.tsx`, `src/components/ui/radio-group.tsx`, `src/components/ui/select.tsx`, `src/components/ui/sheet.tsx`, `src/components/ui/sortable-list.tsx`, `src/components/ui/textarea.tsx`, `src/system/brands/birthplans.css`
300
+
301
+ > Step two of the design system extraction on this side. globals.css imports
302
+ > the package's roles.css, then src/system/brands/birthplans.css (this repo
303
+ > now owns its brand file; BirthGuide's validated copy retires), and gains
304
+ > an @source for the package's dist so Tailwind generates the utilities the
305
+ > components use. The sixteen local ui components are deleted; every import
306
+ > points at @fracazo/design-system/ui/*. cn is re-exported from @/lib/utils.
307
+ > place-autocomplete stays local, it imports app code.
308
+ >
309
+ > The ESLint color guardrail comes from the package with our exemptions. Its
310
+ > fluid-type rule stays off until a type-role pass converges the landing
311
+ > sections' clamp literals. pnpm brand:contract (ds-check-brand) holds the
312
+ > brand file to the contract and runs in pnpm lint. The snapshot harness
313
+ > reads token names from the package's roles, the brand file and globals.
314
+ >
315
+ > Proof, from clean compiles: against the renamed baseline the snapshot adds
316
+ > exactly six token keys (the reasoned chip-3 pair and dark-brand, light and
317
+ > dark) and changes nothing; 335 keys. tsc clean, 100 tests, eslint 0
318
+ > errors, next build succeeds. design-baseline.json regenerated; it also
319
+ > absorbs the questionnaire header's transparent background, which an
320
+ > earlier commit changed without regenerating.
321
+ >
322
+ > The dialog close button now uses focus-visible (the package's version), so
323
+ > a modal opened with the mouse no longer paints a focus ring on it.
324
+ >
325
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
326
+
327
+ ### aa49ed0 refactor(design): rename color-literal tokens to the design system roles
328
+
329
+ 2026-09-05, Alex Fracazo. Files: `src/app/globals.css`, `src/app/plan/edit/example/page.tsx`, `src/app/questionnaire/page.tsx`, `src/components/SiteHeader.tsx`, `src/components/download/PrintablePdfs.tsx`, `src/components/landing/Faq.tsx`, `src/components/landing/FreeAnswers.tsx`, `src/components/landing/Hero.tsx`, `src/components/landing/PlanComparison.tsx`, `src/components/landing/Pricing.tsx`, `src/components/questionnaire/DidYouKnowBar.tsx`, `src/components/questionnaire/QuestionCard.tsx`
330
+
331
+ > Step one of consuming @fracazo/design-system. Every value keeps its
332
+ > literal; only the names move to the roles the package declares:
333
+ >
334
+ > sand/sand-2 -> band/band-2 warm-muted -> ink-3
335
+ > rose -> dark-accent sand-gold -> headline-accent
336
+ > amber(-ink/-soft/-stat) -> highlight(-ink/-soft/-deep)
337
+ > sage/lilac-* -> chip-1/2-* glow-rose/-sage -> glow-1/2
338
+ >
339
+ > The two snapshot probes follow. Zero visual change: with the rename map
340
+ > applied to the baseline's keys, 329 keys compare pairwise identical
341
+ > (34 renamed, 0 value differences).
342
+ >
343
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
344
+
345
+ ### 0f51208 feat: harden the Microsoft Clarity integration
346
+
347
+ 2026-09-03, Alex Fracazo. Files: `src/components/ClarityInit.tsx`, `src/components/download/PaywallPanel.tsx`
348
+
349
+ > Route every Clarity call through lib/analytics.ts, mirror funnel events
350
+ > to Clarity so recordings can be filtered by step, stop storing the
351
+ > buyer's email as an unhashed custom tag, and respect Do Not Track before
352
+ > loading the tag.
353
+ >
354
+ > Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
355
+
356
+ ## Candidates (pending)
357
+
358
+ Judged 7 Sep 2026. Most of this packet is the extraction itself (phases 3
359
+ to 4d), already turned into rules and exemplars on 5 Sep; those commits are
360
+ listed under Rejected topics as "recorded". Three candidates remain.
361
+
362
+ ### candidate/tap-is-intent
363
+ Status: proposed
364
+ Scope: any card, tile or control that represents something the reader cannot get on this surface (an example document, a locked output, a feature on another product).
365
+ Decision: a tap on it opens the path that gets it (a dialog pointing at the program, a link to the sister product), never an inline note that ends there. One dialog serves sibling cards; focus returns to the tapped card on close; the dialog footer stacks at every width so labels never overflow.
366
+ Rationale: visitors kept tapping the example plan's download cards and got a dead end. The tap is intent; the surface should answer it with a route, and analytics should record the intent (which document was wanted) before the route.
367
+ Evidence: 8e0c64f (birthguide)
368
+ Exceptions: none identified.
369
+ Bad example: a card that shows "Example only" text on tap.
370
+ Good example: ExampleDownloadDialog opened from PrintablePdfs, focus restored on close.
371
+ Destination: exemplar (one commit; becomes a rule if the pattern recurs on the birthplans preview)
372
+ Open decisions: does the same apply to locked outputs behind the paywall, where the path is checkout?
373
+
374
+ ### candidate/light-island-inside-dark
375
+ Status: proposed
376
+ Scope: rule/on-dark-ramp.
377
+ Decision: a light surface nested inside an always-dark one (a phone mock's screen, a force-light document preview) resets the context; text inside it follows the light surface, not the on-dark ramp.
378
+ Rationale: the first run of the on-dark lint flagged eight lines in OutputPreviewTabs that were a white phone screen inside a bg-dark bezel; the rule now prunes at bg-white, bg-card, force-light and friends, and the prose should say so.
379
+ Evidence: 3c9b271, cf0b4fe (design-system); the flagged file is birthguide src/components/preview/OutputPreviewTabs.tsx.
380
+ Exceptions: none.
381
+ Bad example: text-ink-3 directly on bg-dark.
382
+ Good example: text-ink-3 inside a bg-white force-light screen inside bg-dark.
383
+ Destination: rule (refinement of rule/on-dark-ramp: add the exception line)
384
+ Open decisions: none.
385
+
386
+ ### candidate/contract-modules
387
+ Status: proposed
388
+ Scope: the brand contract in roles.css.
389
+ Decision: split the contract into a required core and optional modules a brand opts into (landing: band rhythm, fluid type, glows, headline accent; dark band: the dark family and on-dark ramp; preference: the status ramp; phone mock: dark-3, dark-4), so a product does not value roles it never paints.
390
+ Rationale: 6038c47 added the status ramp to the contract for birthplans and made BirthGuide define six values it does not use; chip-3 in each brand file holds the sister product's colour; dark-3 and dark-4 are one landing component's paint. Alex raised this on 5 Sep as the white-label question.
391
+ Evidence: 6038c47, 8349175 (birthguide); 3a98e2c (birthplans); DESIGN.md brand chapters.
392
+ Exceptions: the eight aliasing semantics and the six divergent ones stay core.
393
+ Bad example: a new product's brand file valuing --dark-4 for a phone notch it does not have.
394
+ Good example: a brand file that declares `modules: landing, preference` and is held to those lists.
395
+ Destination: coverage gap (a major version; ds-check-brand learns modules; decide before a third product starts)
396
+ Open decisions: module names; whether a module can be added later without a major bump.
397
+
398
+ ## Rejected topics
399
+
400
+ - The extraction commits (2ec19f7, 8349175, 6038c47, ce64587, 9ef71fb, d3f1dcc, 0754afa, aa49ed0, 3a98e2c, b886eed, b295c5a, dd8495b, 9d63e97): recorded. Their decisions are rules and exemplars already (rules.md, exemplars/, DESIGN.md).
401
+ - 0466daf, a0ffa71, 4ba04df, 70ea27c, f2daff7: recorded as the exemplar clamp-drift-to-named-roles and rule/no-arbitrary-clamp.
402
+ - 84dfd66 (analytics) and 0f51208 (Clarity): instrumentation, not design decisions.
403
+
404
+ ## Coverage gaps observed
405
+
406
+ - A house pattern for "this is available elsewhere" surfaces (see candidate/tap-is-intent).
407
+ - Optional modules in the brand contract (see candidate/contract-modules).
408
+ - birthplans type-role pass still pending; its fluid-type lint stays at warn.
409
+
410
+ ## Next intake
411
+
412
+ - birthguide: next intake runs with `--since dd8495b`
413
+ - birthplans: next intake runs with `--since 9d63e97`
@@ -0,0 +1,42 @@
1
+ # Components
2
+
3
+ Load when: choosing or composing a component.
4
+ Canonical owner: the intent block at the top of each component's source in
5
+ the package (`src/ui/*.tsx`, or `dist/src/ui/*.js` in a consumer). Read it;
6
+ it is the contract (one line, Use for, Avoid when, Variants). `DESIGN.md`,
7
+ Components, has the one-paragraph digest.
8
+
9
+ ## Choosing
10
+
11
+ | Job | Reach for | Not |
12
+ |---|---|---|
13
+ | The one primary action of a step | `Button` default | a styled anchor |
14
+ | Standard secondary (copy, save, dismiss) | `Button` outline | a second primary |
15
+ | Back, cancel, edit in place | `Button` ghost | outline |
16
+ | Irreversible act | `Button` destructive, proportional, with undo if honest | a red default |
17
+ | Enumerable answer in a questionnaire | the product's icon-card groups (rule/house-pattern-for-answers) | RadioGroup, Checkbox, Select |
18
+ | Long enumeration in utilitarian UI | `Select` | a scrolling card grid |
19
+ | A single date | native date input | `Calendar` |
20
+ | Focused task over dimmed content | `Dialog` | Sheet |
21
+ | Supplementary content on mobile | `Sheet` side bottom, aside on desktop (rule/sheet-on-mobile-aside-on-desktop) | Dialog |
22
+ | Transient UI attached to a control | `Popover`, anchored on the control | Dialog |
23
+ | Progressive disclosure in the app | `Accordion` | a custom toggle |
24
+ | Public FAQ | native details and summary | Accordion |
25
+ | Two or three peer views | `Tabs` | Accordion |
26
+ | Secondary detail under a heading | `Accordion` | Tabs |
27
+ | Ranking a small capped set | `SortableList` | drag on a plain list |
28
+ | Long free text | `Textarea` (auto-grows) | Input |
29
+
30
+ ## Composing
31
+
32
+ - Never copy a component into a product to change it. Change it upstream,
33
+ bump. A component that must import app code stays in the product (like
34
+ `place-autocomplete`).
35
+ - `className` on a package component adds layout; it does not override its
36
+ colour, radius or shadow. If the look is wrong, the variant or the
37
+ component is wrong.
38
+ - `cn` from the package (products re-export it from `@/lib/utils`).
39
+ - Cards do not nest. A card is a unit the reader picks up, not a border
40
+ around a section.
41
+ - Button wraps bare text children in a span so icon-leading and
42
+ icon-trailing padding can differ; keep icons as direct children.
@@ -0,0 +1,25 @@
1
+ # Copy
2
+
3
+ Load when: any user-facing string, accessible name, error, empty state,
4
+ label, or commit message.
5
+ Canonical owner: each product's CLAUDE.md language and copy sections;
6
+ `DESIGN.md`, Reject list (voice).
7
+
8
+ - English per product (rule/english-per-product). Check the product before
9
+ writing a word.
10
+ - Short sentences, one idea each. Read aloud; if the rhythm is wrong,
11
+ rewrite (rule/voice-bans).
12
+ - Sentence case everywhere except the mono kicker (rule/sentence-case).
13
+ - No em dashes (rule/no-em-dash).
14
+ - Warm and direct, never clinical, never patronising. The voice is a
15
+ midwife who respects the reader.
16
+ - Plain-language medical terms with the clinical term in parentheses where
17
+ it helps ("nitrous oxide (gas and air)"). Terminology lists live in each
18
+ product's CLAUDE.md.
19
+ - Claims name only what ships (rule/claim-only-what-ships).
20
+ - "Birth plan" for search, "birth preferences" in clinical and community
21
+ copy; both coexist by design.
22
+ - Buttons name the action and the object where the consequence matters
23
+ ("Delete plan", not "Confirm"). Loading states keep the label stable.
24
+ - Errors say what happened and what to do next, keep the reader's input,
25
+ and never blame.
@@ -0,0 +1,66 @@
1
+ # Intake
2
+
3
+ Load when: running or judging a design intake, or deciding what to do with
4
+ a review comment that keeps coming back.
5
+ Canonical owner: this file for the procedure; `rules.md` and `exemplars/`
6
+ for what accepted candidates become.
7
+
8
+ Standards change as components, names and surfaces change. The intake loop
9
+ turns what actually happened (commits, review comments, snapshot deltas)
10
+ into candidates, and keeps three jobs apart so no single message becomes a
11
+ rule by itself.
12
+
13
+ ## 1. Collect (mechanical)
14
+
15
+ `ds-intake --repo <product> [--repo <product>] --since <date|ref> --out
16
+ skills/product-design/intake/<date>.md` gathers every design-relevant
17
+ commit verbatim (subject, body, touched files) and writes a packet with
18
+ empty judge and review sections. It never scores or proposes. Run it at a
19
+ merge to a product's `main`, or weekly; the packet's last section names the
20
+ `--since` for the next run. Add review comments and screenshots by hand
21
+ under the commit they belong to, quoted, with a link.
22
+
23
+ ## 2. Judge (agent, in the packet)
24
+
25
+ Read the whole packet before writing a candidate. Then:
26
+
27
+ - Group evidence that points at the same decision. One candidate per
28
+ decision, not per commit.
29
+ - Separate verified facts (what the commit changed, what was measured) from
30
+ inferences (why it was probably done) and open questions.
31
+ - Check each candidate against `rules.md`: already covered, a refinement, or
32
+ new. A refinement names the rule ID it changes.
33
+ - Fill the candidate block: scope, decision, rationale, evidence hashes,
34
+ exceptions, a bad and good example, and the narrowest destination:
35
+ `rule` (a new record), `exemplar` (a decision worth repeating, from one
36
+ shipped change), `lint` (code can check it without rendering and without
37
+ many exceptions), `eval` (a before and after fixture), `coverage gap`, or
38
+ `no change` (already recorded, or not a design decision).
39
+ - Leave every candidate `proposed`. Put process notes and non-design
40
+ changes under Rejected topics with one line saying why.
41
+ - A single commit is enough for an exemplar. A rule needs the correction
42
+ to have recurred, or a human to say it will.
43
+
44
+ ## 3. Review (human)
45
+
46
+ Alex sets each candidate to `accepted` or `rejected`, may change the
47
+ destination, and may add the one thing the packet cannot: a decision that
48
+ has not shipped yet. Accepted candidates move in the same branch:
49
+
50
+ | Destination | Where it lands |
51
+ |---|---|
52
+ | rule | a new `### rule/<id>` in `rules.md`, and DESIGN.md if the narrative needs it |
53
+ | exemplar | `exemplars/<slug>.md` with Status, Product, Source, Rules, Decision, Why, Repeat, Avoid |
54
+ | lint | a rule in `guardrails/eslint.ts`, a fixture case, `rules.md` enforcement status, changelog |
55
+ | eval | a fixture under `evals/` (none yet) |
56
+ | coverage gap | a line in `coverage-gaps.md` |
57
+ | no change | nothing; the packet keeps the record |
58
+
59
+ The packet stays in `intake/` as the decision log. Never edit a rule from a
60
+ packet that has no accepted status on the candidate.
61
+
62
+ ## Cadence and ownership
63
+
64
+ Alex owns the guidance. The agent runs the collector and drafts the judge
65
+ sections. Rules that stop helping are removed the same way they were
66
+ added: a candidate, a decision, a commit.
@@ -0,0 +1,22 @@
1
+ # Motion
2
+
3
+ Load when: hover and state transitions, entrance sequences, ambient
4
+ layers, reduced motion.
5
+ Canonical owner: `DESIGN.md`, Motion; the product's `globals.css`
6
+ (reduced-motion block, entrance keyframes).
7
+
8
+ - Default to stillness. Motion explains a state change, preserves
9
+ continuity or confirms an action. It never gates reading.
10
+ - Reversible state: transitions (rule/transition-for-interactive-state).
11
+ One-shot staged sequences: keyframes, gated once per visit
12
+ (rule/entrance-once-per-visit), at rest under reduced motion.
13
+ - Ambient layers (glow blobs) are decorative and stay behind content; they
14
+ must not clip at a boundary (rule/no-clipped-ambient). Verify rendered at
15
+ the seam, in both themes; a luminance step on the exact pixel row where
16
+ sections meet is the failure.
17
+ - Hover on buttons: colour, shadow and a half-pixel lift
18
+ (`hover:-translate-y-0.5 active:translate-y-0 active:scale-[0.96]`),
19
+ already in the Button primitive. Do not add a second hover language.
20
+ - No animation libraries without a written justification; the conversion
21
+ surface budget for animation JavaScript is 30KB compressed
22
+ (PRINCIPLES.md).