design system

A design system built for humans and AI tooling alike.


Headless components, tagged releases, and a registry-and-audit backbone that keeps Shield coherent across squads, internal and external products, with synced outputs to multiple platforms.

  • Design System Lead
  • React, Base UI, Tailwind v4, OKLCH tokens
  • Figma library, Storybook, Registry
  • Accessibility, Cross-platform, AI-legible
@ch/ui · anatomy

1. Tokens

OKLCH color, type, spacing, radius

2. Primitives

Button, Badge, Input, Select

3. Composition

SessionCard

!Insurance update needed
Verify coverage before Jan 1
Sessions

24

+12%
Care team
DRKLSMJT
Status
ActiveIn-network
Treatment plan
68% complete
Date
MTW
Quick links
My schedule
Care team
Resources
WeekMonth
Search
Find a provider...
NotificationsEmail + push
Aa4·8·16 / 600--color-primary--radius-card: 14pxtokens
Today · 4:30 PM50 min<Badge tone="info" />

Group session: coping skills

Dr. Reyes · video visit

JoinReschedule<Button variant="primary" />
<SessionCard />
composition
$ @ch/ui v1.6 · ~69 components · 3 apps · 1 system

Origin

From one product's components to a shared foundation.

Charlie Health's products were built on Ant Design. It shipped fast early on, but as the design team pushed for higher fidelity, the cracks showed: heavy style overrides, breaking changes on updates, and a growing gap between Figma and production.

I wrote a technical proposal to the wider org making the case for a headless component system built on ShadCN and Tailwind, packaged as an internal dependency. The pitch centred on three things: design parity, reduced vendor lock-in, and a phased migration that wouldn't stall feature work. Once it had buy-in, Shield started taking shape.

From one product's components to a shared foundation.

How it was built

From Figma library to shipped dependency.

Shield was designed in Figma first, then built with AI-assisted tooling. MCP servers bridged design specs directly into code generation.

PHASE 01

Foundation in Figma

Tokens, components, and patterns designed as a Figma library before any code was written.

OKLCH color tokens + semantic variables
Typography system with Geist + Feijoa
Component variants, states, and auto-layout
Usage guidelines documented per component
PHASE 02

Technical setup

Monorepo scaffolding, Base UI primitives, and MCP servers bridging Figma specs to code generation.

TypeScript monorepo with Tailwind v4
Figma + shadcn MCP servers for Claude
Token extraction pipeline from Figma variables
Build tooling with prebuilt dist/ in tags
PHASE 03

Implementation

Figma-to-code with AI assistance. Every component built to spec, tested, and documented in Storybook.

~69 components across six tagged releases
cva variants and data-slot styling hooks
Storybook with props, states, and a11y notes
Guideline .md files for agentic development
PHASE 04

Rollout & adoption

Packaged as a real dependency, backed by a registry, and adopted across three products and multiple squads.

Git-tag versioning with zero-build installs
shield-registry.yaml for AI + human readers
Onboarding workshops for designers and engineers
Forge audit engine validating usage at scale

The foundation

Base UI primitives, not a shadcn fork.

Rather than fork shadcn wholesale, Shield was rebuilt on Base UI primitives with Tailwind v4, an OKLCH token pipeline, cva variants, and Phosphor icons. Every component exposes a data-slot styling hook and composes through one consistent pattern.

Base UI primitives, not a shadcn fork.

Distribution

Installed as a dependency, not copy-pasted.

v1.0.0 turned Shield into a real dependency, consumed via git tag. Later releases committed the prebuilt dist/ into the tag itself, so installs need no build step, a small decision that made adoption frictionless.

Installed as a dependency, not copy-pasted.

The system, in parts

OKLCH tokens, Base UI primitives, one vocabulary.

Color, type, and radius tokens define the language. ~69 components inherit it through cva variants and data-slot hooks. Three apps, one system.

Core

Ink

ink-50#F5F4FA
ink-100#DDDBE8
ink-200#B8B5CB
ink-300#8A85A8
ink-400#6D6890
ink-500#524D78
ink-600#413D62
ink-700#36324F
ink-800#232037
ink-900#0E0D1E

Blue

blue-50#F0F5FC
blue-100#D1E1F5
blue-200#A1C2E8
blue-300#7BA5DC
blue-400#5480C9
blue-500#3960B2
blue-600#3356A7
blue-700#253E85
blue-800#1A2C66
blue-900#142049

Forest

forest-50#EEF7F5
forest-100#CAECE6
forest-200#93D6CA
forest-300#5DB3A5
forest-400#3A8F82
forest-500#267064
forest-600#1D5B52
forest-700#174840
forest-800#103530
forest-900#0B2220

Semantic

Emerald

emerald-50#E9F8E6
emerald-100#C4EDBE
emerald-200#89D97D
emerald-300#50BD40
emerald-400#38A029
emerald-500#2F901F
emerald-600#247418
emerald-700#1C5D13
emerald-800#154A0E
emerald-900#0E3A0A

Crimson

crimson-50#FCF2EE
crimson-100#F5D5C8
crimson-200#E6A48C
crimson-300#D16D4C
crimson-400#BE4628
crimson-500#A9351E
crimson-600#8C2814
crimson-700#6E1E0E
crimson-800#4E180E
crimson-900#2D0E08

Amber

amber-50#FDF8EB
amber-100#F5E7C0
amber-200#ECCE87
amber-300#E0B45A
amber-400#D0913A
amber-500#B8722B
amber-600#A0611F
amber-700#7A4815
amber-800#593410
amber-900#3A210B

Wisteria

wisteria-50#F7F0FC
wisteria-100#E5D0F5
wisteria-200#C8A4E4
wisteria-300#B68DD8
wisteria-400#9666C4
wisteria-500#7E4EB0
wisteria-600#633A94
wisteria-700#4D2D78
wisteria-800#3B225E
wisteria-900#2C1B47

Neutral

Neutral

50#FAFAFA
100#F3F3F2
200#EAEAE5
300#D7D6D2
400#D0CEC5
500#B5B3A8
600#918F85
700#706E65
800#4B4A43
900#2F2E2B
950#1E1E1C

Typography

Geist · Text scale

AgAgAg
text-xxs · 10/10
AgAgAg
text-xs · 12/18
AgAgAg
text-sm · 14/20
AgAgAg
text-base · 16/24
AgAgAg
text-lg · 18/28
AgAgAg
text-xl · 20/28
AgAgAg
text-2xl · 24/32
AgAgAg
text-3xl · 30/36
AgAgAg
text-4xl · 36/40

Feijoa · Headings

Agheading-xs · 18/32
Agheading-sm · 24/32
Agheading-md · 30/36
Agheading-lg · 36/40
Agheading-xl · 48/48

Border radius

xs
2xs
sm
md
lg
xl
2xl
3xl
4xl

Application colors

Base

backgroundneutral-50
surface-raisedwhite
foregroundink-950

Primary

primaryink-950
primary-foregroundneutral-50

Secondary

secondaryblue-600
secondary-iconblue-500
secondary-borderblue-200
secondary-bgblue-50
secondary-foregroundwhite
secondary-textblue-900

Tertiary

tertiaryforest-600
tertiary-iconforest-500
tertiary-borderforest-200
tertiary-bgforest-50
tertiary-foregroundwhite
tertiary-textforest-900

Card / Popover

cardwhite
card-foregroundink-950
popoverwhite
popover-foregroundink-950

Sidebar

sidebarneutral-50
sidebar-foregroundneutral-800
sidebar-primaryblue-600
sidebar-primary-fgwhite

Surfaces

mutedneutral-50
muted-foregroundneutral-600
accentneutral-100
accent-foregroundneutral-800

Borders / Inputs

borderneutral-300
inputneutral-400
ringneutral-400

Destructive

destructivecrimson-600
destructive-iconcrimson-500
destructive-bordercrimson-200
destructive-bgcrimson-50
destructive-foregroundwhite
destructive-textcrimson-900

Success

successemerald-600
success-iconemerald-500
success-borderemerald-200
success-bgemerald-50
success-foregroundwhite
success-textemerald-900

Warning

warningamber-600
warning-iconamber-500
warning-borderamber-200
warning-bgamber-50
warning-foregroundwhite
warning-textamber-900

Notice

noticewisteria-600
notice-iconwisteria-500
notice-borderwisteria-200
notice-bgwisteria-50
notice-foregroundwhite
notice-textwisteria-900

Usage guidelines

Opinions baked into the components.

Every component ships with do/don't guidance drawn from real product decisions. The rules live in Storybook as well as .md guideline files for agentic development.

Component Guidelines

Primary action trigger with variant hierarchy: default, outline, link, ghost, destructive.

Dismissive actions

SaveCancel
Do

Treat dismissive actions like Cancel with the tertiary link variant. Text-style, full width under the primary action in footers and forms.

SaveCancel
Don't

Avoid secondary or outline buttons for dismissive actions. A filled or bordered Cancel reads as a second primary choice and splits attention from Save.

Icon buttons

Do

Use outline with size icon-sm for most icon buttons. The border and hit area improve visibility and accessibility.

Don't

Don't use ghost icon buttons without background protection when the icon is the only control.

Placement and emphasis

Edit
Edit
Do

Use outline for inline row actions and secondary full-width actions in scrolling content. The standard second tier after default.

Submit
Don't

Avoid default (primary) buttons in the middle of a long page. Primary actions usually belong in a sticky footer or dialog.

The screens

Building Shield

Origin

Figma is the source of truth. Components are designed there first, built into real code, then consumed by Claude Design as the foundation for spec-driven development.

Technical foundation

Base UI primitives, Tailwind v4, OKLCH token pipeline, cva variants, and Phosphor icons. Every component exposes a data-slot styling hook and composes through one consistent pattern.

A real dependency

v1.0.0 turned Shield into a real dependency consumed via git tag. Prebuilt dist/ committed into the tag so installs need no build step. Adoption without friction.

Full component coverage

Across v1.1 to v1.3 the library went from a handful to complete shadcn coverage plus domain-specific pieces: a full chat stack, charts on Recharts v3, and a bespoke clinical week-schedule. ~69 components today.

Machine-readable registry

A hand-authored registry (shield-registry.yaml) documents every component's scope, variants, dependencies, and divergences from shadcn, written to be read by both engineers and AI agents.

Cross-platform tokens

With mobile on the horizon, tokens migrated to a DTCG source of truth. A spike proved OKLCH + color-mix() round-trips to resolved sRGB with zero fidelity loss. One token set across web, iOS, and Android.

AppShell and product IA

As patterns stabilized they got promoted into components, most notably AppShell, which packages the whole app chrome into a single component. Examples realigned to Client, Provider, and Practice Management.

Hardening

Before broadening adoption, the entire library was audited across three lenses and fixed in four batches: RSC boundaries, CSS-injection hardening, render memoization, and API-consistency cleanups.

Where Shield stands

Shield is at v1.6.0: ~69 components, prebuilt and versioned, backed by a registry and an audit engine, with a cross-platform token path underway. It powers the Client, Provider, and Practice Management apps, with mobile on the horizon.