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. Framework-agnostic. Themes override these. Every visual value in atom CSS comes from a var() token reference — zero hardcoded values.
Components (Structure)
Accessible HTML primitives. React implementation. Emit semantic DOM, ARIA attributes, data-* hooks, and .tcn-* classes. No CSS, no visual logic, no animation.
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.
{
"color": {
"blue": {
"500": { "value": "#3b82f6" },
"600": { "value": "#2563eb" }
},
"neutral": {
"0": { "value": "#ffffff" },
"900": { "value": "#171717" }
}
}
}Alias Tokens
Semantic roles that reference raw values. "Primary" maps to neutral.900 by default, but a theme can remap it to any raw value.
{
"color": {
"primary": { "value": "{color.neutral.900}" },
"on-primary": { "value": "{color.white}" },
"surface": {
"default": { "value": "{color.neutral.0}" }
},
"on-surface": {
"default": { "value": "{color.neutral.900}" }
}
}
}System Tokens
Component-specific tokens that reference aliases. This is where the surface/on-surface convention maps to specific components.
{
"button": {
"primary": {
"surface": {
"default": { "value": "{color.primary}" },
"hover": { "value": "{color.primary-hover}" }
},
"on-surface": {
"default": { "value": "{color.on-primary}" }
}
}
}
}Component Contract Model
Components emit structure and accessibility. They render semantic HTML elements, ARIA attributes, data-* state hooks, and .tcn-* class names. The CSS lives in the tokens package as atom CSS that maps these hooks to token values.
// Component renders structure + ARIA (no CSS)
<button
class="tcn-button tcn-button-primary"
data-size="md"
data-loading="false"
aria-busy="false"
>
Click me
</button>
/* Atom CSS maps structure to tokens (no component logic) */
.tcn-button-primary {
background: var(--button-primary-surface-default);
color: var(--button-primary-on-surface-default);
border-radius: var(--button-radius);
}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/ → Style Dictionary config + JSON tokens + atom CSS
core/ → React primitives (structure + accessibility)
interactions/ → Motion thread (V2)
patterns/ → 36 theme-agnostic layout patternsBuild order: tokens → core → patterns. Turborepo manages the dependency graph. The documentation site and theme configurator live in separate repositories and consume the published npm packages.