§1The problem
No interface code was shared: identical files copied between apps (CSS and utilities byte-for-byte equal), every screen with its own colours and style, and changing the brand meant touching everything.
§2What I built
- UI packageInstalled in the apps by local path (not published to npm): dozens of components such as Toast, Table, Calendar, Kanban board, Recurrence editor, Header and Shell.
- TokensSemantic colour tokens with light and dark themes, and fixed typography.
- StorybookOne file per component, in three groups: Foundations, Atoms and Composites.
- ScriptsWCAG contrast script and a generator for the apps' icons.
- Design tool syncSynced with an AI design tool (project with 33 components).
§3The hard parts
5 entries- 01
Hard rules
Tailwind only, semantic tokens only, @theme inline so dark mode doesn't freeze.
- 02
Contrast verified by script
On backgrounds, cards and surfaces, in both themes.
- 03
Icons generated by code
From the brand component, with a check mode (--check).
- 04
Sync with the design tool
The converter reads the package's types; Storybook was reorganised so components resolve by title, with no special adapter.
- 05
Everyday traps documented
For example, Tailwind's dark: variant doesn't work in this setup; the README has the install recipe.
§4Result
- apps using the package
- 14
- Storybook pages, one per component
- 67
Used by 14 selected apps. Storybook runs locally. Reorganised in July 2026: from 100 stories under 2 titles to 67 pages, one per component.
§5Stack
- React 19
- TypeScript
- Tailwind 4
- Storybook 10
- Python (contrast script)
- Node (icon generator)