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

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