Initial commit: FA 2.0 Design System foundation

Token pipeline (Style Dictionary v4, DTCG format):
- Primitive tokens: colour palettes (brand, sage, neutral, feedback),
  typography (3 font families, 21-variant type scale), spacing (4px grid),
  border radius, shadows, opacity
- Semantic tokens: text, surface, border, interactive, feedback colours;
  typography roles; layout spacing
- Component tokens: Button (4 sizes), Input (2 sizes)
- Generated outputs: CSS custom properties, JS ES6 module, flat JSON

Atoms (3 components):
- Button: contained/soft/outlined/text × primary/secondary, 4 sizes,
  loading state, underline for text variant
- Typography: 21 variants across display/heading/body/label/caption/overline,
  maxLines truncation
- Input: external label, helper text, error/success validation,
  start/end icons, required indicator, 2 sizes, multiline support

Infrastructure:
- MUI v5 theme with full token mapping
- Storybook 8 with autodocs
- Claude Code agents and skills for token/component workflows
- Design system documentation and cross-session memory

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 15:08:15 +11:00
commit 732c872576
56 changed files with 12690 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
# Component registry
Tracks the status, specification, and key details of every component in the
design system. Agents MUST check this before building a component (to avoid
duplicates) and MUST update it after completing one.
## Status definitions
- **planned**: Component is identified but not yet started
- **in-progress**: Component is being built
- **review**: Component is built, awaiting human review
- **done**: Component is reviewed and approved
- **needs-revision**: Component needs changes based on review feedback
## Atoms
| Component | Status | Variants | Tokens used | Notes |
|-----------|--------|----------|-------------|-------|
| Button | review | contained, soft, outlined, text × xs, small, medium, large × primary, secondary + loading, underline, fullWidth | button.height/paddingX/paddingY/fontSize/iconSize/iconGap/borderRadius, color.interactive.*, color.brand.100-300, color.neutral.200-700 | Primary interactive element. Merges Text Button from Figma. Soft variant = Figma's Secondary/Brand & Secondary/Grey. |
| IconButton | planned | contained, soft, outlined, text × small, medium, large | Reuses Button tokens | Icon-only button (close, menu, actions). Wrap MUI IconButton with FA theme. Build when Navigation/modals are needed. |
| Typography | review | displayHero, display1-3, displaySm, h1-h6, bodyLg, body1, body2, bodyXs, labelLg, label, labelSm, caption, captionSm, overline, overlineSm + maxLines, gutterBottom | typography.* (all semantic typography tokens), fontFamily.body, fontFamily.display | Text display system. Thin MUI wrapper with maxLines truncation. |
| Input | review | medium, small × default, hover, focus, error, success, disabled + startIcon, endIcon, required, multiline | input.height/paddingX/paddingY/fontSize/borderRadius/gap/iconSize, color.neutral.300-400, color.brand.500, color.feedback.error/success, color.text.secondary | External label pattern, branded focus ring, two sizes aligned with Button. Adds startIcon/endIcon and success state beyond Figma. |
| Badge | planned | default, success, warning, error | | Status indicators |
| Icon | planned | various sizes | | Icon wrapper component |
| Avatar | planned | image, initials, icon × small, medium, large | | User/entity representation |
| Divider | planned | horizontal, vertical | | Visual separator |
| Chip | planned | filled, outlined × deletable, clickable | | Tags and filters |
| Card | planned | elevated, outlined | | Content container |
| Link | planned | default, subtle | | Navigation text |
## Molecules
| Component | Status | Composed of | Notes |
|-----------|--------|-------------|-------|
| FormField | planned | Input + Typography (label) + Typography (helper) | Standard form field with label and validation |
| PriceCard | planned | Card + Typography + Badge + Button | Service pricing display |
| ServiceOption | planned | Card + Typography + Chip + Icon | Selectable service item |
| SearchBar | planned | Input + Icon + Button | Search with submit |
| StepIndicator | planned | Typography + Badge + Divider | Multi-step flow progress |
## Organisms
| Component | Status | Composed of | Notes |
|-----------|--------|-------------|-------|
| ServiceSelector | planned | ServiceOption × n + Typography + Button | Full service selection panel |
| PricingTable | planned | PriceCard × n + Typography | Comparative pricing display |
| ArrangementForm | planned | FormField × n + StepIndicator + Button | Multi-step arrangement flow |
| Navigation | planned | Link × n + Button + Avatar | Site header navigation |
| Footer | planned | Link × n + Typography + Divider | Site footer |
## Future enhancements
Deferred items that should be addressed when the relevant components or patterns
are needed. Check this section before building new components — an item here may
be relevant to your current work.
| Item | Relates to | Trigger | Notes |
|------|-----------|---------|-------|
| Destructive button colours | Button | When building delete/cancel flows | `color="error"` already works via MUI palette. May need `soft` variant styling for error/warning/success colours. |
| Link-as-button | Button | When building Navigation or link-heavy pages | Use MUI's `component="a"` or `href` prop. May warrant a separate Link atom or a `Button` story showing the pattern. |
| IconButton atom | IconButton | When building Navigation, modals, toolbars | Wrap MUI IconButton with FA theme tokens. Registered as planned atom above. |
| ~~Google Fonts loading~~ | ~~Typography~~ | ~~Resolved~~ | ~~Added to .storybook/preview-head.html and index.html~~ |

View File

@@ -0,0 +1,221 @@
# Design decisions log
Every design decision that affects tokens, components, or system architecture
MUST be logged here with rationale. This ensures consistency across sessions
and agents. Agents MUST check this file before making any decision that could
contradict a previous one.
## Format
```
### [Decision ID] — [Short title]
**Date:** [date]
**Category:** token | component | architecture | convention
**Decision:** [What was decided]
**Rationale:** [Why this decision was made]
**Affects:** [Which tokens/components/files this impacts]
**Alternatives considered:** [What else was considered and why it was rejected]
```
---
## Decisions
### D001 — Primary brand colour is warm gold #BA834E
**Date:** 2025-03-25
**Category:** token
**Decision:** Primary brand colour (color.brand.500) is #BA834E — warm gold.
**Rationale:** Extracted from Parsons brand swatches. Matches the existing FA 1.0 CTA button colour ("Add to package"). Warm gold conveys trust and professionalism appropriate for funeral services. The colour has a 3.7:1 contrast ratio on white, suitable for large text and interactive elements (buttons with white text).
**Affects:** color.brand.500, color.interactive.default, MUI palette.primary.main
**Alternatives considered:** #B0610F (copper) was considered for primary but it's darker and more aggressive. Placed at brand.600 for hover/emphasis instead.
### D002 — Primary text colour is charcoal #2C2E35 (not pure black)
**Date:** 2025-03-25
**Category:** token
**Decision:** Primary text colour is #2C2E35 (color.neutral.800) — charcoal with cool blue undertone.
**Rationale:** From brand swatch. Softer than pure black for extended reading comfort while maintaining 13.2:1 contrast ratio on white (well exceeds WCAG AA). The cool undertone complements the sage secondary palette.
**Affects:** color.text.primary, MUI palette.text.primary
**Alternatives considered:** Pure black (#000000) — higher contrast but causes eye fatigue on white backgrounds. Added as color.black for rare use.
### D003 — Brand link/accent colour uses copper #B0610F
**Date:** 2025-03-25
**Category:** token
**Decision:** Brand-coloured text (links, inline emphasis) uses #B0610F (color.brand.600 / copper) not the base gold.
**Rationale:** The base gold #BA834E has only 3.7:1 contrast on white which doesn't meet WCAG AA for normal text (4.5:1 required). The copper #B0610F provides 4.8:1 contrast, meeting AA. It's also visually more assertive for link text.
**Affects:** color.text.brand, color.interactive.hover
**Alternatives considered:** brand.700 (#8B4E0D) at 6.7:1 — meets AAA but is too dark and loses the warm "brand" feel.
### D004 — Sage palette as secondary colour family
**Date:** 2025-03-25
**Category:** token
**Decision:** Secondary palette uses cool sage/slate tones (#4C5B6B as sage.700) rather than a second warm colour.
**Rationale:** From brand swatches (cool row). The sage provides visual contrast against the warm brand palette and adds a calming, professional quality appropriate for the funeral services context. Used for secondary buttons and less prominent actions.
**Affects:** color.sage.*, color.interactive.secondary, MUI palette.secondary
**Alternatives considered:** Using a lighter warm tone for secondary — rejected as it wouldn't provide enough visual distinction from primary.
### D005 — Display font is Noto Serif SC for H1-H2 headings
**Date:** 2025-03-25
**Category:** token
**Decision:** Display/heading font is Noto Serif SC for Display, H1, and H2 levels. H3+ use Montserrat (body font).
**Rationale:** User specified Noto Serif SC as display font and Montserrat as primary. The serif at H1-H2 adds warmth and gravitas suitable for funeral services. H3+ switches to Montserrat to create a clear hierarchy break.
**Affects:** fontFamily.display, fontFamily.body, typography.display/h1/h2/h3/h4
**Alternatives considered:** Using serif for all headings — rejected as it would make H3/H4 feel too formal at smaller sizes.
### D006 — Warning text uses amber.700 for AA compliance
**Date:** 2025-03-25
**Category:** token
**Decision:** Warning text colour is amber.700 (#A36B00) not amber.600 (#CC8500).
**Rationale:** amber.600 (3.6:1 contrast on white) only passes AA for large text. For warning text that must be readable at body size, amber.700 at 5.1:1 meets WCAG AA for normal text.
**Affects:** color.text.warning
**Alternatives considered:** amber.800 — passes AAA but is too dark to read as "amber/warning".
### D007 — Style Dictionary requires usesDtcg: true
**Date:** 2025-03-25
**Category:** architecture
**Decision:** Added `usesDtcg: true` to Style Dictionary v4.4.0 config.
**Rationale:** Without this flag, SD v4 doesn't properly populate `allTokens` for the CSS `css/variables` format, resulting in empty CSS output. JS and JSON formats work without it but CSS requires it for DTCG token format.
**Affects:** style-dictionary/config.js, CSS output
**Alternatives considered:** Custom format — rejected as the built-in format works correctly with the flag.
### D008 — Responsive typography via @media in MUI theme
**Date:** 2025-03-25
**Category:** architecture
**Decision:** Mobile font sizes are handled via `@media (max-width:600px)` queries in the MUI theme typography config, not via `responsiveFontSizes()`.
**Rationale:** Explicit media queries allow precise control over mobile sizes (28px, 24px, 20px, 18px) matching the token definitions. MUI's `responsiveFontSizes()` uses its own scaling algorithm that can't be configured to match our exact values.
**Affects:** src/theme/index.ts, typography.*.fontSizeMobile tokens
**Alternatives considered:** `responsiveFontSizes()` utility — rejected due to lack of precise size control.
### D009 — Merge Text Button into Button atom
**Date:** 2026-03-25
**Category:** component
**Decision:** Text Button is not a separate atom. It is implemented as `<Button variant="text">` with an optional `underline` prop for link-style appearance.
**Rationale:** In MUI, text buttons are a variant of Button (`variant="text"`), not a separate component. Having both Button and TextButton creates API confusion. The Figma "ghost" variant and "text button" both map to MUI's text variant. The `underline` prop handles the design's "default.underline" state.
**Affects:** Button component API, Storybook story structure
**Alternatives considered:** Separate TextButton component — rejected as it duplicates Button logic and creates ambiguity for developers.
### D010 — Button sizes adjusted for touch targets
**Date:** 2026-03-25
**Category:** component
**Decision:** Button sizes are xs: 28px, sm: 32px, md: 40px, lg: 48px — all multiples of 4px. Large size (48px) meets the 44px minimum touch target for mobile.
**Rationale:** Original Figma sizes (sm: 26px, md: 37px) don't meet the 44px minimum touch target specified in the design system conventions. Adjusted to a clean 4px-grid scale that includes a large size suitable for mobile CTAs. FA's audience may be older or in emotional distress — generous touch targets are important.
**Affects:** tokens/component/button.json, MUI theme MuiButton overrides
**Alternatives considered:** Keeping original Figma sizes — rejected because neither met the 44px touch target minimum.
### D011 — Use @mui/icons-material over Font Awesome
**Date:** 2026-03-25
**Category:** architecture
**Decision:** Primary icon source is `@mui/icons-material`. Custom SVGs wrapped in MUI's `SvgIcon` for any FA-specific icons.
**Rationale:** MUI icons extend `SvgIcon`, so they work natively with MUI Button's `startIcon`/`endIcon`, inherit theme colours, and are tree-shakeable. Font Awesome requires an adapter layer to work with MUI's icon API. All standard UI icons from the Figma designs exist in the MUI icon set.
**Affects:** Icon approach, Button icon integration, dependencies
**Alternatives considered:** `@fortawesome/react-fontawesome` — rejected due to adapter complexity and extra dependency. User was using FA icons in Figma but agreed MUI icons are more pragmatic for the code implementation.
### D012 — Button responsive sizing is a composition concern
**Date:** 2026-03-25
**Category:** architecture
**Decision:** Button size is an explicit prop (`size="xs" | "small" | "medium" | "large"`), not responsive by default. Layout components choose the appropriate size per breakpoint.
**Rationale:** Keeps the Button component simple and predictable. Responsive behaviour is a layout decision — a card might use `size="large"` on mobile for touch targets and `size="medium"` on desktop. This avoids the complexity of Figma variable modes for mobile/desktop scaling inside components.
**Affects:** Button component API, consumer usage patterns
**Alternatives considered:** Figma variable modes for responsive scaling within the component — rejected as over-engineered. MUI's `useMediaQuery` or responsive `sx` props handle this at the composition level.
### D013 — Soft variant for tonal/muted buttons
**Date:** 2026-03-25
**Category:** component
**Decision:** Added `variant="soft"` custom MUI Button variant for tonal/muted fill buttons. Maps to Figma's "Secondary/Brand" and "Secondary/Grey" columns.
**Rationale:** Figma review revealed that "Secondary/Brand" (bg #e4cdb3, text #845830) and "Secondary/Grey" (bg #dcdde0, text #434b52) are NOT outlined buttons — they are filled buttons with softer, tonal colours. This pattern is common in modern design systems (Material Design 3 calls it "tonal", Radix calls it "soft"). Primary soft uses brand.200 bg + brand.700 text. Secondary soft uses neutral.200 bg + neutral.700 text.
**Affects:** MUI theme MuiButton variants, Button component API, Storybook stories
**Alternatives considered:** Separate color options for each Figma column — rejected as it doesn't map to MUI's color+variant model. The soft variant is more extensible — any future color automatically gets a soft treatment.
### D014 — Secondary palette changed from sage to neutral grey
**Date:** 2026-03-25
**Category:** token
**Decision:** MUI secondary palette changed from sage (#4C5B6B sage.700) to neutral grey (#525252 neutral.600). Sage remains available in the token system for surfaces but is no longer the secondary button colour.
**Rationale:** User feedback that secondary buttons should match Figma's "Secondary/Grey" which uses neutral tones, not the blue-green sage. The sage palette is still valuable for cool surfaces (color.surface.cool) but shouldn't be the primary secondary interactive colour.
**Affects:** MUI palette.secondary, all MuiButton secondary overrides (outlined, text, soft)
**Alternatives considered:** Adding a third "neutral" custom colour and keeping sage as secondary — rejected as unnecessarily complex when no other components use the sage secondary yet.
### D015 — Loading spinner positioned on the right
**Date:** 2026-03-25
**Category:** component
**Decision:** Button loading spinner (CircularProgress) appears after the label text, on the right side of the button.
**Rationale:** User preference. Right-side positioning follows the convention of trailing indicators (like endIcon) and feels more natural for async state feedback.
**Affects:** Button component render order
**Alternatives considered:** Left-side spinner (before text) — rejected per user feedback.
### D016 — Primary button colour confirmed as copper (brand.600)
**Date:** 2026-03-25
**Category:** token
**Decision:** Primary button fill (palette.primary.main) stays as brand.600 copper (#B0610F), not brand.500 warm gold (#BA834E). User confirmed.
**Rationale:** Brand.600 provides 4.8:1 contrast ratio with white text, meeting WCAG AA for all text sizes. Brand.500 only achieves 3.7:1 (AA for large text only). Since button labels can vary in size, the stronger contrast is preferred. The visual warmth is preserved through the soft variant (brand.200 bg) and hover states.
**Affects:** palette.primary.main, all contained primary buttons, interactive token
**Alternatives considered:** Brand.500 warm gold — rejected as it doesn't meet AA for normal-sized text.
### D017 — Headings use Montserrat Bold, not Noto Serif SC
**Date:** 2026-03-25
**Category:** token
**Decision:** All headings (h1-h6) use Montserrat Bold (700), not Noto Serif SC. Noto Serif SC is reserved exclusively for display variants (displayHero through displaySm).
**Rationale:** User's FA 2.0 Figma design separates display (serif, for marketing/hero) from headings (sans-serif, for content structure). This is cleaner than the previous approach where h1-h2 used serif. Montserrat Bold provides strong hierarchy without the formality of serif at content-level sizes.
**Affects:** MUI theme h1-h6 fontFamily, semantic typography tokens. Supersedes D005.
**Alternatives considered:** Keep D005 (serif for h1-h2) — rejected per user's updated Figma design.
### D018 — Display weight is Regular (400), not Bold
**Date:** 2026-03-25
**Category:** token
**Decision:** Display text (Noto Serif SC) uses Regular weight (400) across all 5 display levels, not Bold (700).
**Rationale:** Serif fonts carry inherent visual weight. At 40-80px, Regular Noto Serif SC has enormous presence. Bold at these sizes would be overwhelming and less elegant.
**Affects:** typography.displayHero/1/2/3/Sm fontWeight tokens
**Alternatives considered:** Bold (700) — the previous implementation. Rejected per user's Figma which uses Regular.
### D019 — Body text weight is Medium (500)
**Date:** 2026-03-25
**Category:** token
**Decision:** Body text uses Montserrat Medium (500) instead of Regular (400). User found Regular too light for comfortable reading.
**Rationale:** User feedback that Montserrat Regular is "just a little bit too light." The Figma specified weight 450 which isn't available in Google Fonts, so 500 (Medium) is the closest available weight that provides the desired visual density.
**Affects:** typography.body/bodyLg/bodySm/bodyXs fontWeight tokens
**Alternatives considered:** 400 Regular — too light per user. 450 — not available in Google Fonts Montserrat.
### D020 — Expanded type scale: 21 variants across 6 categories
**Date:** 2026-03-25
**Category:** token
**Decision:** Typography system expanded from ~12 to 21 variants organized in 6 categories: Display (5 levels), Heading (6 levels), Body (4 sizes), Label (3 sizes), Caption (2 sizes), Overline (2 sizes). Caption/sm and overline/sm floored at 11px for accessibility.
**Affects:** primitive typography tokens, semantic typography tokens, MUI theme, Typography component
**Alternatives considered:** Keeping the simpler scale — rejected as user explicitly wanted more variety matching their FA 2.0 Figma type system.
### D021 — Input uses external label pattern, not MUI floating label
**Date:** 2026-03-25
**Category:** component
**Decision:** Input label sits above the input field as a separate element, not as MUI's floating/outlined label that sits on the border. Uses MUI's `InputLabel` with `position: static` and `shrink: true`.
**Rationale:** The Figma design shows the label as a distinct element above the input with a 6px gap. The floating label pattern is harder to read and interact with, especially for FA's audience who may be elderly or in distress. External labels have better usability research support for form comprehension.
**Affects:** Input component structure, MUI InputLabel styling, no notch in OutlinedInput
**Alternatives considered:** MUI's default floating label (outlined variant) — rejected as it doesn't match the Figma design and has usability concerns for the target audience.
### D022 — Input sizes: medium (48px) and small (40px)
**Date:** 2026-03-25
**Category:** component
**Decision:** Input has two sizes: medium (48px, default) and small (40px). Medium aligns with Button large (48px) for search bar pairing. Small aligns with Button medium (40px) for compact layouts.
**Rationale:** Figma designed inputs at 64px which is too tall relative to other components and creates misalignment with buttons. 48px meets the 44px touch target minimum and aligns with our Button large. Both sizes sit on the 4px grid.
**Affects:** tokens/component/input.json, MUI theme MuiOutlinedInput size overrides
**Alternatives considered:** Single size at 64px (Figma original) — rejected as it dwarfs adjacent buttons. Three sizes (sm/md/lg) — rejected as premature; two sizes cover all current form patterns.
### D023 — Input focus ring uses brand.500 warm gold, not brand.600 copper
**Date:** 2026-03-25
**Category:** component
**Decision:** Input focus ring uses brand.500 (#BA834E warm gold) via a double box-shadow (2px white + 4px brand), not the brand.600 copper used for Button focus outlines.
**Rationale:** The Figma FA 2.0 design explicitly specifies brand.500 for the input focus ring as an `elevation-special/focus-ring` effect token. The warm gold is softer and more appropriate for form inputs where the focus indicator should be visible but not aggressive. Buttons use brand.600 outline because they need to stand out against coloured backgrounds.
**Affects:** MUI theme MuiOutlinedInput focus styles
**Alternatives considered:** Using brand.600 (same as Button) — rejected as the Figma explicitly uses brand.500 for inputs.
### D024 — Input label stays neutral on error/success states
**Date:** 2026-03-25
**Category:** component
**Decision:** Input label colour remains `text.secondary` regardless of error/success state. Only the border and helper text change colour.
**Rationale:** Per the Figma design. A full-red label would feel alarming, which is inappropriate for FA's sensitive context. The subtle approach (border + helper text colour change) communicates the issue without distress. Users who are already in an emotional state don't need aggressive visual feedback.
**Affects:** InputLabel Mui-error and Mui-focused style overrides
**Alternatives considered:** Colouring the label red on error — rejected as too aggressive for the audience.
### D025 — Added startIcon/endIcon convenience props and success state
**Date:** 2026-03-25
**Category:** component
**Decision:** Input component adds three features beyond the Figma design: (1) `startIcon` prop for leading icons, (2) `endIcon` prop for trailing icons (convenience wrapper around MUI adornments), (3) `success` boolean prop for validation success state (green border + green helper text). Error-state icons (ErrorOutline) and success-state icons (CheckCircleOutline) are recommended patterns in stories.
**Rationale:** Leading icons (email, phone, dollar) are essential for FA's arrangement forms. Success state provides positive feedback after validation. The Figma only had trailing icon, but leading icons are a near-universal production need. The `startIcon`/`endIcon` props are simpler than MUI's `InputAdornment` pattern while remaining compatible with raw adornments via `startAdornment`/`endAdornment`.
**Affects:** Input component API, InputAdornment usage, Storybook stories
**Alternatives considered:** Only supporting MUI's raw adornment API — rejected as too verbose for the common case. The convenience props are ergonomic while the raw props remain available for complex cases (e.g., password toggle with IconButton).

290
docs/memory/session-log.md Normal file
View File

@@ -0,0 +1,290 @@
# Session log
This file tracks work completed across Claude Code sessions. Every agent MUST
read this file before starting work and update it after completing work.
## Format
Each entry follows this structure:
```
### Session [date] — [brief description]
**Agent(s):** [which agents were active]
**Work completed:**
- [bullet points of what was done]
**Decisions made:**
- [any design/architecture decisions with brief rationale]
**Open questions:**
- [anything unresolved that needs human input]
**Next steps:**
- [what should happen next]
```
---
## Sessions
### Session 2026-03-24 — Project scaffold setup and hygiene
**Agent(s):** Manual (no agents invoked — setup phase)
**Work completed:**
- Fixed missing `.storybook/` directory (main.ts + preview.tsx with MUI ThemeProvider)
- Created `.gitignore` (was missing entirely)
- Created `.mcp.json` with Figma remote MCP config
- Removed malformed `{.claude/agents,.claude/...}` directory tree (bash brace expansion bug)
- Created 3 agent definitions: token-architect, component-builder, story-writer
- Created 8 slash commands: create-tokens, build-atom, build-molecule, build-organism, write-stories, sync-tokens, status, review-component
- Migrated commands from `.claude/commands/` to `.claude/skills/<name>/SKILL.md` format with YAML frontmatter
- Fixed **critical** Style Dictionary v4 config: converted from CommonJS to ESM (`StyleDictionary` class API)
- Changed `build:tokens` script from `style-dictionary build --config ...` to `node style-dictionary/config.js`
- Added `"type": "module"` to package.json for ESM support
- Changed SD output from `tokens.ts` to `tokens.js` (Style Dictionary v4 generates JS, not TS)
- Registered `@storybook/addon-designs` in `.storybook/main.ts` (was installed but not configured)
- Added CSS token import to `.storybook/preview.tsx`
- Added Card and Link to CLAUDE.md atom list (were missing)
- Fixed broken Penpot/Storybook MCP references in `docs/reference/mcp-setup.md`
- Improved agent instructions with pre-flight dependency checks, story checklist enforcement, token validation steps, and MUI theme mapping guide
- Created `src/components/{atoms,molecules,organisms}/` directories
- Fixed `bootstrap.sh` to check for `.storybook/` dir instead of `node_modules/@storybook`
- Verified `npm run build:tokens` runs successfully (empty output expected — no tokens yet)
- Verified Storybook runs at http://localhost:6006
**Decisions made:**
- Skills use `.claude/skills/<name>/SKILL.md` format (not `.claude/commands/`)
- Style Dictionary v4 config is a standalone ESM script run via `node`, not CLI
- British spelling for filenames (`colours.json`), standard `color` namespace for token paths
**Open questions:**
- Slash commands `/create-tokens` etc. showing "Unknown skill" — may need Claude Code restart to discover newly created skills. User should test after restarting.
**Next steps:**
- ~~Verify slash commands work after Claude Code restart~~ ✓ Skills working
- ~~Begin Step 1: `/create-tokens` with brand colours, fonts, and reference material~~ ✓ Done
### Session 2025-03-25 — Token creation (Step 1)
**Agent(s):** token-architect (via /create-tokens skill)
**Work completed:**
- Extracted brand colours from user-provided swatch image (12 colours across 3 palettes)
- Reviewed 5 Parsons FA 1.0 Figma screens for colour application context
- Reviewed 2 FA 2.0 Design System Template screens (buttons + inputs)
- Created complete primitive tokens:
- Colour scales (50-950): brand (warm gold), sage (cool grey-green), neutral, red, amber, green, blue + white/black
- Typography: 3 font families (Montserrat, Noto Serif SC, JetBrains Mono), font size scale (xs-4xl + mobile), weights, line heights, letter spacing
- Spacing: 4px-based scale (2px-80px), border radius scale
- Effects: 4 shadow levels, 3 opacity values
- Created complete semantic tokens:
- Colour mappings: text (9 variants), surface (6), border (6), interactive (7), feedback (8)
- Typography roles: display, h1-h4, bodyLarge, body, bodySmall, caption, label, overline — each with font family, size, weight, line height, letter spacing + mobile size overrides
- Spacing: component (xs-lg), layout (gutter, section, page, padding) with mobile/desktop variants
- Fixed Style Dictionary v4.4.0 CSS output: added `usesDtcg: true` flag (required for DTCG format CSS generation)
- Generated outputs:
- CSS: 245 custom properties in `src/theme/generated/tokens.css` with `var()` references for semantic tokens
- JS: 258 named exports in `src/theme/generated/tokens.js`
- JSON: flat export in `tokens/export/tokens-flat.json`
- Updated MUI theme (`src/theme/index.ts`):
- All palette colours mapped from token imports
- All typography variants mapped (h1-h6, body1, body2, subtitle1/2, caption, overline, button)
- 3 custom typography variants added: display, bodyLarge, label (with TS module declarations)
- Responsive font sizes via @media queries for display, h1, h2, h3
- Component overrides for Button and Card border radius
- Updated docs/design-system.md with actual brand colours, palettes, and font stacks
- Updated docs/memory/decisions-log.md with 8 design decisions (D001-D008)
- Updated docs/memory/token-registry.md with complete token reference
**Decisions made:**
- D001: Primary brand colour is #BA834E (warm gold from swatch)
- D002: Primary text colour is #2C2E35 (charcoal, not pure black)
- D003: Brand link colour uses #B0610F (copper, meets WCAG AA)
- D004: Sage palette as secondary (calming contrast to warm primary)
- D005: Noto Serif SC for display/H1-H2, Montserrat for H3+ and body
- D006: Warning text uses amber.700 for AA compliance
- D007: Style Dictionary requires usesDtcg: true for CSS output
- D008: Responsive typography via explicit @media queries in MUI theme
**Open questions:**
- Font name confirmation: user wrote "Not Serif SC" — interpreted as "Noto Serif SC" (Google Font). Needs confirmation.
- brand.500 (#BA834E) has 3.7:1 contrast on white — AA for large text only. Primary buttons use white text on this background, which works for button-sized text. Should we darken the primary CTA colour for better accessibility, or is the current value acceptable?
- Token collision warning: benign collision on `$type: "color"` set in both primitives and semantic colour files. Doesn't affect output.
**Next steps:**
- ~~Confirm display font name (Noto Serif SC vs something else)~~
- ~~Begin Step 2: Build first atom component (Button recommended as it exercises the most tokens)~~ ✓ Done
- Consider adding Google Fonts import for Montserrat and Noto Serif SC to index.html or Storybook preview
### Session 2026-03-25 — Button atom (Step 2)
**Agent(s):** Claude Opus (via conversation)
**Work completed:**
- Reviewed 3 Figma component sets: Icons (29:54), Button (28:50), Text Button (32:1361)
- Created button component tokens (`tokens/component/button.json`): height, paddingX, paddingY, fontSize, iconSize, iconGap, borderRadius for xs/sm/md/lg sizes
- Rebuilt token pipeline — 25 new button tokens generated across CSS/JS/JSON outputs
- Updated MUI theme (`src/theme/index.ts`) with comprehensive MuiButton overrides:
- 4 sizes: xs (28px), small (32px), medium (40px), large (48px)
- Contained, outlined, text variants for primary and secondary colours
- Hover backgrounds using brand.100 and sage.100 for outlined/text variants
- Focus-visible outline (2px solid interactive.focus, 2px offset)
- Disabled elevation via `disableElevation: true` default prop
- Custom `xs` size via MUI variant system + module augmentation
- Created Button component (`src/components/atoms/Button/Button.tsx`):
- Wraps MUI Button with React.forwardRef
- Custom props: `loading` (spinner + disabled), `underline` (text-decoration for text variant)
- Accepts all MUI ButtonProps (variant, color, size, startIcon, endIcon, fullWidth, etc.)
- Created barrel export (`src/components/atoms/Button/index.ts`)
- Created comprehensive Storybook stories (`Button.stories.tsx`): 16 stories covering Default, Variants (primary/secondary), AllSizes, Icons, Disabled, Loading, TextWithUnderline, FullWidth, LongContent, CompleteMatrix
- Storybook builds successfully with Button stories
**Decisions made:**
- D009: Merge Text Button into Button atom — text buttons are `variant="text"` with optional `underline` prop, not a separate component
- D010: Button sizes adjusted for touch targets — xs: 28px, sm: 32px, md: 40px, lg: 48px (lg meets 44px minimum)
- D011: Use `@mui/icons-material` over Font Awesome — native MUI integration, no adapter needed, tree-shakeable
- D012: Responsive sizing is a composition concern — Button size is an explicit prop, not responsive by default. Layouts choose the right size per breakpoint.
**Open questions:**
- primary.main maps to brand.600 (copper #B0610F) for AA compliance, but Figma designs show brand.500 (warm gold #BA834E) as primary button fill. Should we adjust? Current mapping prioritises accessibility.
- Google Fonts import still needed for Montserrat and Noto Serif SC in Storybook
- Icon approach confirmed as @mui/icons-material — should we create any custom SvgIcon components for FA-specific icons?
**Next steps:**
- ~~User to review Button in Storybook and provide feedback on colours/sizing~~ ✓ Feedback received
- Build Typography atom
- Add Google Fonts to Storybook preview
- ~~Log D009-D012 in decisions-log.md~~ ✓ Done
### Session 2026-03-25b — Button refinements from user review
**Agent(s):** Claude Opus (via conversation)
**Work completed:**
- Fetched Figma Secondary/Brand and Secondary/Grey button details — discovered they are **soft-filled** (tonal) buttons, not outlined
- Added custom `variant="soft"` to MUI Button — tonal/muted fill treatment
- Primary soft: brand.200 bg (#EBDAC8), brand.700 text (#8B4E0D), brand.300 hover
- Secondary soft: neutral.200 bg (#E8E8E8), neutral.700 text (#404040), neutral.300 hover
- Changed MUI secondary palette from sage to neutral grey (neutral.600 main, neutral.700 dark, neutral.300 light)
- Updated all secondary button overrides (outlined, text) from sage to neutral
- Moved loading spinner from left to right side of button text
- Updated stories: added FigmaMapping story, soft variant stories, LoadingToSuccess pattern story
- Logged D013-D015 in decisions log
**Decisions made:**
- D013: Added `soft` variant for tonal buttons — maps to Figma Secondary/Brand and Secondary/Grey
- D014: Secondary palette changed from sage to neutral grey
- D015: Loading spinner positioned on right
**Open questions:**
- primary.main maps to brand.600 (copper #B0610F) for AA compliance, but Figma designs show brand.500 (warm gold #BA834E) as primary button fill. Should we adjust?
- Google Fonts import still needed for Montserrat and Noto Serif SC in Storybook
- Icon approach confirmed as @mui/icons-material — should we create any custom SvgIcon components for FA-specific icons?
- Sage palette no longer used for buttons — consider if it should be re-introduced as a custom colour later
**Next steps:**
- ~~User to re-review Button in Storybook~~ ✓ Approved as good baseline
- ~~Build Typography atom~~ ✓ Done
- ~~Add Google Fonts to Storybook preview~~ ✓ Done
### Session 2026-03-25c — Typography atom + housekeeping
**Agent(s):** Claude Opus (via conversation)
**Work completed:**
- Added Google Fonts loading: Montserrat (400/500/600/700) + Noto Serif SC (600/700) in `.storybook/preview-head.html` and `index.html`
- Created Typography component (`src/components/atoms/Typography/Typography.tsx`):
- Thin wrapper around MUI Typography with React.forwardRef
- Custom prop: `maxLines` for CSS line-clamp truncation
- All MUI Typography props passed through (variant, color, align, component, gutterBottom, etc.)
- Created barrel export (`src/components/atoms/Typography/index.ts`)
- Created comprehensive Storybook stories (9 stories): Default, HeadingHierarchy, BodyVariants, UIText, Colours, SemanticHTML, MaxLines, RealisticContent, FontFamilies
- Added "Future enhancements" tracking table to component-registry.md — deferred items (IconButton, destructive colours, link-as-button) tracked with triggers for when to address them
- Logged D016 (primary colour confirmed as copper brand.600) in decisions-log.md
- Storybook builds successfully
**Decisions made:**
- D016: Primary button colour confirmed as copper (brand.600) — user approved
- Typography atom is a thin wrapper + maxLines — no over-engineering
**Open questions:**
- Icon approach confirmed as @mui/icons-material — should we create any custom SvgIcon components for FA-specific icons?
- Sage palette no longer used for buttons — consider if it should be re-introduced as a custom colour later
**Next steps:**
- User to review Typography in Storybook
- Build next atom (Input or Card recommended — Input for forms, Card for service listings)
- Consider building FormField molecule next if Input is done (label + input + helper text)
### Session 2026-03-25d — Typography expansion (21 variants)
**Agent(s):** Claude Opus (via conversation)
**Work completed:**
- Reviewed user's FA 2.0 Figma type scale (node 23:30) — 21 variants across 6 categories, 3 responsive breakpoints
- Expanded primitive typography tokens: added fontSize.2xs (11px), fontSize.display.* sub-group (hero 80px through sm 32px), mobile overrides for all display and heading sizes
- Rewrote semantic typography tokens: 21 variants with specific line-heights and letter-spacing per variant
- Rewrote MUI theme typography section: refactored to wildcard import (`import * as t`), added all 21 variants with module augmentations, responsive scaling via @media queries
- Updated Google Fonts: added Noto Serif SC weight 400 (Regular) for display text
- Updated Typography component JSDoc with full variant guide
- Rewrote Typography stories: 10 stories including CompleteScale (all 21 variants matching Figma layout)
- Key font changes: headings now Montserrat Bold (not serif), display now Regular 400 (not Bold), body now Medium 500 (not Regular)
**Decisions made:**
- D017: Headings use Montserrat Bold, not Noto Serif SC (supersedes D005)
- D018: Display weight is Regular 400, not Bold
- D019: Body weight is Medium 500 (user preference, 450 not available)
- D020: 21 typography variants across 6 categories
**Next steps:**
- ~~User to review expanded type scale in Storybook~~ ✓
- Fine-tune heading sizes if they don't match Figma exactly (h2-h6 were estimated)
- ~~Build next atom (Input or Card)~~ ✓ Input done
### Session 2026-03-25e — Input atom
**Agent(s):** Claude Opus (via conversation)
**Work completed:**
- Reviewed FA 2.0 Figma Input Field design (node 39:713) — 9 states × 4 toggle properties = ~90 variants
- Analysed Figma design and provided feedback: identified 7 improvements (leading icons, size variation, required indicator, select vs input naming, multiline, character count, error icon treatment)
- Created Input component tokens (`tokens/component/input.json`): height (sm/md), paddingX, paddingY (sm/md), fontSize, borderRadius, gap, iconSize — 9 tokens total
- Rebuilt token pipeline — 9 new input tokens generated across CSS/JS/JSON outputs
- Updated MUI theme (`src/theme/index.ts`) with comprehensive MuiOutlinedInput overrides:
- Border colours per state: neutral.300 default, neutral.400 hover, brand.500 focus, error/success for validation
- Focus ring: double box-shadow (2px white gap + 4px brand.500) matching Figma's elevation-special/focus-ring effect
- Error + focus ring: error-coloured ring
- Disabled background: neutral.100
- Two sizes via minHeight: medium (48px), small (40px)
- Icon sizing: 20px via InputAdornment
- Notch legend hidden for external label pattern
- Multiline padding normalisation
- Created Input component (`src/components/atoms/Input/Input.tsx`):
- Composes FormControl + InputLabel + OutlinedInput + FormHelperText
- External label pattern (InputLabel with position:static, no floating)
- Custom props: `label`, `helperText`, `success`, `startIcon`, `endIcon`, `fullWidth`
- Label stays neutral on error/focus/success (per Figma design)
- Required asterisk on label (via MUI InputLabel)
- Success state: green border + green helper text (not a native MUI state)
- Error helper text has `role="alert"` for screen readers
- `aria-describedby` connection between input and helper text
- Supports multiline via `multiline` + `rows`/`minRows`/`maxRows`
- Created barrel export (`src/components/atoms/Input/index.ts`)
- Created comprehensive Storybook stories (11 stories): Default, FigmaMapping, AllStates, Required, Sizes, SizeAlignment, WithIcons, PasswordToggle, Multiline, ValidationFlow, ArrangementForm, CompleteMatrix
- Logged D021-D025 in decisions log
- Updated component registry (Input → review)
- Storybook builds successfully
**Decisions made:**
- D021: External label pattern, not MUI floating label
- D022: Two sizes — medium 48px (matches Button large), small 40px (matches Button medium)
- D023: Focus ring uses brand.500 warm gold (Figma spec), not brand.600
- D024: Label stays neutral on error/success (calmer for FA audience)
- D025: Added startIcon/endIcon convenience props and success validation state
**Open questions:**
- Should error+focus ring be error-coloured or brand-coloured? Currently error-coloured (reinforces the validation state during keyboard navigation)
- FormField molecule may now be unnecessary — Input already includes label + helper text. Consider repurposing FormField as a layout/validation wrapper instead.
- Should we add a character count feature? (Useful for textarea fields like special instructions)
**Next steps:**
- User to review Input in Storybook
- Build next atom (Card, Badge, or Chip recommended)
- Consider whether FormField molecule is still needed given Input's built-in label/helperText

View File

@@ -0,0 +1,193 @@
# Token registry
Master reference of all design tokens. Updated by the token-architect agent
whenever tokens are created or modified. Other agents reference this to find
the correct token for any design property.
## How to use this file
- **token-architect**: Update this file whenever you create/modify tokens
- **component-builder**: Reference this file to find the correct token for any CSS property
- **story-writer**: Reference this file to document which tokens a component uses
## Primitives
### Colours
**Brand (warm gold/copper)**`tokens/primitives/colours.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| color.brand.50 | #FEF9F5 | Lightest warm tint — warm section backgrounds |
| color.brand.100 | #F7ECDF | Light warm — hover backgrounds |
| color.brand.200 | #EBDAC8 | Warm light — secondary backgrounds |
| color.brand.300 | #D8C3B5 | Warm beige (swatch) — surface warmth |
| color.brand.400 | #D0A070 | Mid gold (swatch) — secondary interactive |
| color.brand.500 | #BA834E | **Base brand** (swatch) — primary CTA |
| color.brand.600 | #B0610F | Rich copper (swatch) — hover, brand links |
| color.brand.700 | #8B4E0D | Deep copper — active states |
| color.brand.800 | #6B3C13 | Dark brown — bold accents |
| color.brand.900 | #51301B | Chocolate (swatch) — deep emphasis |
| color.brand.950 | #251913 | Espresso (swatch) — darkest brand |
**Sage (cool grey-green)**`tokens/primitives/colours.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| color.sage.50 | #F2F5F6 | Lightest sage — cool backgrounds |
| color.sage.100 | #E3EAEB | Light sage — hover states |
| color.sage.200 | #D7E1E2 | (swatch) — light cool surface |
| color.sage.300 | #C8D4D6 | Mid-light sage — dividers |
| color.sage.400 | #B9C7C9 | (swatch) — mid sage |
| color.sage.500 | #8EA2A7 | Base sage — secondary content |
| color.sage.600 | #687D84 | Dark sage — secondary text |
| color.sage.700 | #4C5B6B | (swatch) — secondary buttons |
| color.sage.800 | #4C5459 | (swatch) — supplementary text |
| color.sage.900 | #343C40 | Very dark sage |
| color.sage.950 | #1E2528 | Near-black cool |
**Neutral (true grey)**`tokens/primitives/colours.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| color.neutral.50-950 | #FAFAFA#0A0A0B | Full grey scale for text, borders, UI |
| color.neutral.800 | #2C2E35 | (swatch) — **Primary text colour** |
**Feedback hues**`tokens/primitives/colours.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| color.red.50-950 | #FEF2F2#3D0E0E | Error/destructive states |
| color.amber.50-950 | #FFF9EB#331F00 | Warning/caution states |
| color.green.50-950 | #F0F7F0#0F2A0F | Success/positive states |
| color.blue.50-950 | #EFF6FF#172554 | Info/informational states |
**Standalone**`tokens/primitives/colours.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| color.white | #FFFFFF | Pure white |
| color.black | #000000 | Pure black (use sparingly) |
### Typography
`tokens/primitives/typography.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| fontFamily.body | Montserrat, ... | Primary body/UI font |
| fontFamily.display | Noto Serif SC, ... | Display/heading serif |
| fontFamily.mono | JetBrains Mono, ... | Monospace |
| fontSize.xs4xl | 0.75rem3rem | Desktop font size scale |
| fontSize.mobile.display | 1.75rem | 28px mobile display |
| fontSize.mobile.h1 | 1.5rem | 24px mobile H1 |
| fontSize.mobile.h2 | 1.25rem | 20px mobile H2 |
| fontSize.mobile.h3 | 1.125rem | 18px mobile H3 |
| fontWeight.regularbold | 400700 | Weight scale |
| lineHeight.tightrelaxed | 1.251.75 | Leading scale |
| letterSpacing.tighterwidest | -0.02em0.08em | Tracking scale |
### Spacing
`tokens/primitives/spacing.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| spacing.0-520 | 2px80px | 4px-based spacing scale |
| borderRadius.nonefull | 0px9999px | Radius scale |
### Effects
`tokens/primitives/effects.json`
| Token path | Value | Description |
|-----------|-------|-------------|
| shadow.smxl | CSS box-shadow strings | Elevation shadows |
| opacity.disabled | 0.4 | Disabled elements |
| opacity.hover | 0.08 | Hover overlay tint |
| opacity.overlay | 0.5 | Modal backdrop |
## Semantic tokens
### Colour semantics
`tokens/semantic/colours.json`
| Token path | References | Description |
|-----------|-----------|-------------|
| color.text.primary | → neutral.800 | Main body/heading text |
| color.text.secondary | → neutral.600 | Helper text, descriptions |
| color.text.tertiary | → neutral.500 | Placeholders, timestamps |
| color.text.disabled | → neutral.400 | Disabled text |
| color.text.inverse | → white | Text on dark backgrounds |
| color.text.brand | → brand.600 | Links, brand emphasis |
| color.text.error | → red.600 | Error messages |
| color.text.success | → green.600 | Success messages |
| color.text.warning | → amber.700 | Warning messages |
| color.surface.default | → white | Main page background |
| color.surface.subtle | → neutral.50 | Alternate backgrounds |
| color.surface.raised | → white | Cards (with shadow) |
| color.surface.warm | → brand.50 | Warm-tinted sections |
| color.surface.cool | → sage.50 | Cool/calming sections |
| color.surface.overlay | #00000080 | Modal backdrop |
| color.border.default | → neutral.200 | Standard borders |
| color.border.strong | → neutral.400 | Emphasis borders |
| color.border.subtle | → neutral.100 | Soft dividers |
| color.border.brand | → brand.500 | Focused inputs, brand borders |
| color.border.error | → red.500 | Error field borders |
| color.border.success | → green.500 | Success field borders |
| color.interactive.default | → brand.500 | Primary button, links |
| color.interactive.hover | → brand.600 | Hover state |
| color.interactive.active | → brand.700 | Active/pressed state |
| color.interactive.disabled | → neutral.300 | Disabled interactive |
| color.interactive.focus | → brand.500 | Focus ring colour |
| color.interactive.secondary | → sage.700 | Secondary buttons |
| color.interactive.secondary-hover | → sage.800 | Secondary hover |
| color.feedback.success | → green.600 | Success indicator |
| color.feedback.success-subtle | → green.50 | Success background |
| color.feedback.warning | → amber.600 | Warning indicator |
| color.feedback.warning-subtle | → amber.50 | Warning background |
| color.feedback.error | → red.600 | Error indicator |
| color.feedback.error-subtle | → red.50 | Error background |
| color.feedback.info | → blue.600 | Info indicator |
| color.feedback.info-subtle | → blue.50 | Info background |
### Typography semantics
`tokens/semantic/typography.json`
| Token path | References | Description |
|-----------|-----------|-------------|
| typography.display.* | fontSize.3xl, fontFamily.display, bold, tight | Hero display text |
| typography.h1.* | fontSize.2xl, fontFamily.display, bold, tight | Page headings |
| typography.h2.* | fontSize.xl, fontFamily.display, semibold, snug | Section headings |
| typography.h3.* | fontSize.lg, fontFamily.body, semibold, snug | Sub-headings |
| typography.h4.* | fontSize.md, fontFamily.body, semibold, snug | Minor headings |
| typography.bodyLarge.* | fontSize.md, fontFamily.body, regular, relaxed | Lead paragraphs |
| typography.body.* | fontSize.base, fontFamily.body, regular, normal | Default body |
| typography.bodySmall.* | fontSize.sm, fontFamily.body, regular, normal | Helper text |
| typography.caption.* | fontSize.xs, fontFamily.body, regular, snug | Fine print |
| typography.label.* | fontSize.sm, fontFamily.body, medium, normal | Form labels |
| typography.overline.* | fontSize.xs, fontFamily.body, semibold, snug | Section overlines |
### Spacing semantics
`tokens/semantic/spacing.json`
| Token path | References | Description |
|-----------|-----------|-------------|
| spacing.component.xslg | → spacing.16 | Component internal spacing |
| spacing.layout.gutter | → spacing.4 | Grid gutter (mobile) |
| spacing.layout.gutter-desktop | → spacing.6 | Grid gutter (desktop) |
| spacing.layout.section | → spacing.12 | Page section gap |
| spacing.layout.page | → spacing.16 | Major section gap |
| spacing.layout.page-padding | → spacing.4 | Page horizontal padding (mobile) |
| spacing.layout.page-padding-desktop | → spacing.8 | Page horizontal padding (desktop) |
## Component tokens
`tokens/component/button.json`
### Button — Sizing
| Token path | Value / Reference | Used by | Description |
|-----------|-----------|---------|-------------|
| button.height.xs | 28px | Button | Extra-small height — compact text buttons |
| button.height.sm | 32px | Button | Small height — secondary actions |
| button.height.md | 40px | Button | Medium height — default |
| button.height.lg | 48px | Button | Large height — CTAs, mobile (meets 44px touch target) |
| button.paddingX.xslg | → spacing.26 (824px) | Button | Horizontal padding per size |
| button.paddingY.xslg | → spacing.13 (412px) | Button | Vertical padding per size |
| button.fontSize.xslg | → fontSize.xsbase (1216px) | Button | Font size per size |
| button.iconSize.xslg | 1420px | Button | Icon dimensions per size |
| button.iconGap.xslg | → spacing.12 (48px) | Button | Icon-to-text gap per size |
| button.borderRadius.default | → borderRadius.md (8px) | Button | Button corner radius |