Architecture
Three threads, zero entanglement. How the system stays modular at every layer.
The Three Threads
The design system separates concerns into three independent threads. Each thread can evolve without affecting the others. V1 ships threads 1 and 2; thread 3 is deferred to V2.
Tokens (Aesthetics)
JSON values become CSS custom properties via Style Dictionary. Framework-agnostic. Foundation tokens ship with core. Themes override these. Every visual value comes from a var() reference — zero hardcoded values.
Components (Structure)
Accessible HTML primitives in React. Emit semantic DOM, ARIA attributes, data-* hooks, and .tcn-* classes. Each component ships with co-located atom CSS that maps tokens to visual styles.
Interaction (Motion)
Transitions, transforms, animations, easing, gestures. Deferred to V2. This separation means V1 components work perfectly — they just change state instantly.
The Token Cascade
Tokens flow through three tiers with strict referencing direction: raw → alias → system. Each tier adds semantic meaning.
Raw Tokens
Primitive values with no semantic meaning. Color scales, sizing steps, font stacks. These are the building blocks — the "paint tubes" that everything else draws from.
{
"color": {
"blue": {
"500": { "$value": "#3b82f6", "$type": "color" },
"600": { "$value": "#2563eb", "$type": "color" }
},
"neutral": {
"0": { "$value": "#ffffff", "$type": "color" },
"900": { "$value": "#171717", "$type": "color" }
}
}
}Alias Tokens (Base Theme)
Semantic roles that reference raw values. The base theme provides neutral, functional defaults — neutral-900 primary, system-ui fonts, white surfaces. It ships as part of foundation CSS so a site with no theme loaded still renders correctly. A branded theme overrides these alias slots to express a different visual identity.
{
"color": {
"primary": { "$value": "{color.neutral.900}", "$type": "color" },
"on-primary": { "$value": "{color.white}", "$type": "color" },
"surface": {
"default": { "$value": "{color.neutral.0}", "$type": "color" }
},
"on-surface": {
"default": { "$value": "{color.neutral.900}", "$type": "color" }
}
}
}System Tokens
Component-specific tokens that reference aliases. System tokens handle structural properties — sizing, spacing, radius, font weight — giving each component its own tuning knobs. For colors, atom CSS references alias tokens directly so customisations cascade without an extra indirection layer.
{
"button": {
"radius": { "$value": "{radius.md}", "$type": "dimension" },
"font-weight": { "$value": "{font.weight.medium}", "$type": "fontWeight" },
"gap": { "$value": "{spacing.sm}", "$type": "dimension" },
"md": {
"padding-x": { "$value": "{spacing.lg}", "$type": "dimension" },
"padding-y": { "$value": "{spacing.sm}", "$type": "dimension" },
"font-size": { "$value": "{font.size.base}", "$type": "dimension" },
"height": { "$value": "{sizing.lg}", "$type": "dimension" }
}
}
}Foundation CSS
Foundation CSS is the ground-zero import for any Toucan app. It concatenates the token tiers into a single file: raw values, base theme (alias defaults), system bindings, and dark mode overrides. The toucan CLI compiles this from your chosen token preset at build time.
# Compile tokens into foundation CSS
npx toucan build/* Single import — compiled by `npx toucan build` */
@import './css/toucan.css';
/* toucan.css includes:
1. Raw tokens — color scales, spacing, typography
2. Base theme — neutral alias defaults (primary, surface, etc.)
3. System tokens — component-specific bindings
4. Dark mode — [data-mode="dark"] overrides
5. Component CSS — atom styles + responsive rules */Core owns the full compilation pipeline — Style Dictionary, responsive templates, and CSS concatenation. The tokens package provides JSON presets as pure data.
Co-located Atom CSS
Atom CSS files live alongside their components in @toucan-ui/core. Each component's entry point imports its own CSS, so importing a component automatically includes only that component's styles.
// core/src/components/button/index.ts
import './button.css';
export { Button } from './button';/* button.css — maps data attributes to token values */
.tcn-button {
border-radius: var(--button-radius);
font-weight: var(--button-font-weight);
gap: var(--button-gap);
}
.tcn-button[data-variant="primary"] {
background-color: var(--color-primary);
color: var(--color-on-primary);
border-color: transparent;
}
.tcn-button[data-size="md"] {
height: var(--button-md-height);
padding: var(--button-md-padding-y) var(--button-md-padding-x);
font-size: var(--button-md-font-size);
}Unused components don't add CSS weight to your bundle. Import only Button and only button styles ship.
Component Contract Model
Components emit structure and accessibility. They render semantic HTML elements, ARIA attributes, data-* state hooks, and .tcn-* class names. The co-located atom CSS maps these hooks to token values — this is the bridge between the structure and aesthetics threads.
<!-- Component renders structure + ARIA -->
<button
class="tcn-button"
data-variant="primary"
data-size="md"
aria-busy="false"
>
Click me
</button>/* Atom CSS maps structure to tokens */
.tcn-button[data-variant="primary"] {
background-color: var(--color-primary);
color: var(--color-on-primary);
border-color: transparent;
}
.tcn-button[data-size="md"] {
height: var(--button-md-height);
padding: var(--button-md-padding-y) var(--button-md-padding-x);
font-size: var(--button-md-font-size);
}This separation means components can be ported to any framework (Vue, Flutter) — the visual layer is pure CSS custom properties.
Why Interaction Is Separate
Motion is the third axis. Transitions, transforms for hover effects, animations, and gesture handling all live in their own thread. In V1, state changes happen instantly — a button goes from default to hover with no transition. This is intentional: it proves the system works without motion, and V2 can layer motion on top without touching component or token code.
The only transforms allowed in V1 are structural: a toggle thumb's translateX to move between checked positions, or a checkbox chevron's rotate. These aren't motion — they're positional state.
Monorepo Structure
packages/
tokens/ → Pure JSON token presets (raw, alias, system, dark)
core/ → React primitives + atom CSS + compiler pipeline + CLI
interactions/ → Motion thread (V2)
patterns/ → 36 theme-agnostic layout patterns
apps/
docs/ → Documentation site + Wiz'rd theme configuratorBuild order: tokens (no build) → core → patterns. Turborepo manages the dependency graph.