Token architecture

Four layers. Values flow one way — down. No layer reaches past the one above it.

01

Primitives

belin/tokens/primitives.css

Named by what they are, not what they do. The only place actual hex or rem values live. Nothing else in the system contains raw numbers.

Colors

--belin-neutral-0 #fff
--belin-neutral-50 → 950
--belin-blue-50 → 800
--belin-red-100 → 700
--belin-amber-100/400/500/600
--belin-green-100 → 700
--belin-purple-100 → 700

Spacing

--belin-space-1 0.25rem
--belin-space-2 0.5rem
--belin-space-3 0.75rem
--belin-space-4 1rem
⋮ (5 → 11)
--belin-space-12 5rem

Radius

--belin-radius-sm 0.25rem
--belin-radius-md 0.375rem
--belin-radius-lg 0.5rem
--belin-radius-full 9999px

Shadow

--belin-shadow-none none
--belin-shadow-sm
--belin-shadow-md
--belin-shadow-lg
--belin-shadow-xl

Type scale

--belin-text-xs-size 0.75rem
--belin-text-xs-lh 1rem
⋮ (sm → 3xl)
--belin-text-4xl-size 2.25rem
--belin-text-4xl-lh 2.5rem

Fonts

--belin-font-sans
--belin-font-mono
--belin-font-serif
referenced by ↓
02

Semantics

belin/tokens/semantics.css + dark.css

Named by role, not value. References primitives only — never raw numbers. Same token names in both modes: components never need to check which mode is active.

:root { } — Light defaults

--color-background var(--belin-neutral-0)
--color-foreground var(--belin-neutral-900)
--color-surface / -foreground
--color-muted / -foreground
--color-border / -input / -ring
--color-action-primary var(--belin-blue-600)
--color-action-primary-foreground
--color-action-primary-hover var(--belin-blue-700)
--color-action-primary-active var(--belin-blue-800)
--color-action-secondary + -fg/hover/active
--color-disabled-bg / -fg
--color-destructive + -fg/hover/active
--color-warning + -foreground
--color-success + -fg/hover/active
--color-accent + -fg/hover/active
--color-card + -foreground
--color-popover + -foreground
--radius-sm / -md / -lg / -full
--shadow-none / -sm / -md / -lg / -xl
--font-sans / -mono / -serif
--text-xs/sm/body/lg/xl/2xl/3xl/4xl-size+lh

.dark { } — Dark overrides

--color-background var(--belin-neutral-950)
--color-foreground var(--belin-neutral-50)
--color-surface / -foreground
--color-muted / -foreground
--color-border / -input / -ring
--color-action-primary var(--belin-blue-500)
--color-action-primary-hover var(--belin-blue-400)
--color-action-primary-active var(--belin-blue-300)
--color-action-secondary + -fg/hover/active
--color-disabled-bg / -fg
--color-destructive-hover / -active
--color-warning var(--belin-amber-400)
--color-success + hover/active
--color-accent + hover/active
--color-card + -foreground
--color-popover + -foreground

Radius, fonts, and type scale inherit light defaults.

density.css — [data-density] presets

--space-component-padding-x
--space-component-padding-y
--space-component-gap
--space-section-padding

compact / default / comfortable each map these four to different primitives.

component-spacing.css — Button slot

--btn-variant-pt / -pr / -pb / -pl
--btn-variant-radius

Baseline values; ComponentStylesProvider overrides per size at runtime.

text-styles.css — .ts-{slug} classes

.ts-display
.ts-heading-1
.ts-heading-2
.ts-heading-3
.ts-heading-4
.ts-body-large
.ts-body-default
.ts-body-small
.ts-blockquote
.ts-caption
.ts-label
.ts-code-inline
.ts-code-block
.ts-footnote
.ts-nav-link
.ts-link
.ts-button-sm
.ts-button-default
.ts-button-lg

Each class reads --ts-{slug}-* variables with fallbacks to the type scale. TextStyleProvider injects overrides on mount.

Runtime overrides — injected ‹style› tags (sit above the defaults above)

:root #belin-light-overrides
.dark #belin-dark-overrides
:root #belin-spacing-style
:root #belin-typography-style
:root #belin-token-overridesapplyTheme()
:root #belin-text-styles-tagTextStyleProvider
:root #belin-button-variant-styles-tagComponentStylesProvider

shadcn bridge — maps Belin names to the names shadcn components expect

--primary--color-action-primary
--primary-foreground--color-action-primary-foreground
--secondary--color-action-secondary
--secondary-foreground--color-action-secondary-foreground
--background--color-background
--foreground--color-foreground
--muted--color-muted
--muted-foreground--color-muted-foreground
--accent--color-surface
--accent-foreground--color-surface-foreground
--card--color-card
--card-foreground--color-card-foreground
--popover--color-popover
--popover-foreground--color-popover-foreground
--destructive--color-destructive
--border--color-border
--input--color-input
--ring--color-ring
--radius--radius-md
--sidebar (+ 7 sidebar-*)→ card / muted / primary / etc.
consumed by ↓
03

Components

belin/components/

Thin wrappers around shadcn source. Reference semantic tokens only — never primitives directly. App code imports from @/belin/components — never from @/components/ui. Density (compact / default / comfortable) comes from the data-density attribute on <html>.

Wrapped components

Accordion
Alert
Badge
Blockquote
Button
Card
Checkbox
Dialog
DropdownMenu
Field
Input
Label
Progress
RadioGroup
Select
Separator
Sidebar
Skeleton
Switch
Tabs
Textarea
Tooltip
Typography

Button — tokens consumed

--primary / -foreground (default)
--secondary / -foreground
--destructive
--border / --muted (outline, ghost)
--btn-variant-pt / -pr / -pb / -pl
--btn-variant-radius
.ts-button-sm / -default / -lg

Typography — tokens consumed

.ts-{slug} class via textStyle prop
--color-foreground / -muted-foreground
--font-sans / -mono / -serif
--text-{step}-size / -lh
--belin-space-{n} (paragraph spacing)
configured by ↓
04

Component variants

belin/tokens/component-variants.ts

Named token configurations bound to a specific component. The customizer's Button editor stores per-variant color references and per-size spacing + radius references. ComponentStylesProvider compiles these to CSS at runtime and writes them to the variant style tag — no instance overrides, no per-component CSS token layer.

Button variants — color references stored per variant

default--primary, --primary-foreground
secondary--secondary, --secondary-foreground
destructive--destructive
outline--border, --muted (hover)
ghost--muted (hover)
link--primary

Button sizes — spacing references stored per size × density

sm × { compact, default, comfortable }
default × { compact, default, comfortable }
lg × { compact, default, comfortable }

Each cell stores top/right/bottom/left as references to --belin-space-* tokens. Radius is stored once per size and references --radius-{sm|md|lg}.

Follow a token

How a raw value travels from a hex number in primitives.css all the way to a rendered component.

Primary button background

primitives.css
--belin-blue-600
#2563eb
semantics.css
--color-action-primary
var(--belin-blue-600)
shadcn bridge
--primary
var(--color-action-primary)
Button (shadcn)
background-color
var(--primary)

Button padding (default size, default density)

primitives.css
--belin-space-2
0.5rem
density.css
--space-component-padding-y
var(--belin-space-2)
component-spacing.css
--btn-variant-pb
var(--space-component-padding-y)
Belin Button wrapper
padding-bottom
var(--btn-variant-pb)

Heading 2 font size

primitives.css
--belin-text-2xl-size
1.5rem
semantics.css
--text-2xl-size
var(--belin-text-2xl-size)
text-styles.css
.ts-heading-2 font-size
var(--ts-heading-2-font-size, var(--text-2xl-size))
Typography
textStyle="heading-2"
applies .ts-heading-2

Page background (dark mode)

primitives.css
--belin-neutral-950
#0a0a0a
dark.css
--color-background
var(--belin-neutral-950)
shadcn bridge
--background
var(--color-background)
body
background-color
var(--background)

Not shown: belin/tokens/base.css — component-critical CSS that ships alongside the tokens (Base UI overlay fixes, Belin variant defaults, table chrome, and the shadcn variable bridge). It consumes tokens but defines none, so it sits outside the four layers above.