InfinitiBit Design System

Design Tokens

How @infinitibit_gmbh/ui design tokens work, how to use them, and how to update them.

Design Tokens

@infinitibit_gmbh/ui ships its design decisions as tokens — colors, typography, spacing, radius, and elevation — emitted as CSS custom properties and a typed TypeScript object. Tokens are generated from a single source of truth; you never hand-edit the output.

The two-tier model

Tokens come in two layers:

  • Primitives — the raw scale: palette.primary.500, font.size.body-m, space.4. These are values, not decisions about where they're used.
  • Semantic — role-based tokens that reference primitives: color.action.primary.bg → {palette.primary.500}, color.text.link, etc.

Every token is emitted as an --ib--prefixed CSS variable with path-based names, and semantic variables keep their reference as a var() chain:

:root {
  --ib-palette-primary-500: #3062ce;                       /* primitive */
  --ib-color-action-primary-bg: var(--ib-palette-primary-500); /* semantic → primitive */
}

Components consume semantic (and foundation) tokens only — never primitives. That indirection is what makes theming and dark mode cheap: a theme re-points a handful of variables instead of restating every value. Semantic colors ship in both light and dark — light at the theme selector, dark under .dark (see Themes → Dark mode). Components inherit dark mode for free because they only read --ib-color-*.

Using tokens (consuming teams)

In a product app, import one theme — it provides the full token set (see Themes):

import '@infinitibit_gmbh/theme-default/theme.css';

@infinitibit_gmbh/ui/tokens.css is the raw default token layer; import it directly only if you're not using a theme package. Either way you then reference the same --ib-* variables in CSS:

.cta {
  background: var(--ib-color-action-primary-bg);
  color: var(--ib-color-action-primary-text);
  border-radius: var(--ib-radius-sm);
  box-shadow: var(--ib-elevation-02);
}

Or use the typed token object (handy with class-variance-authority or inline styles) — leaf values are the var() strings, and TokenName is the union of every token path:

import { tokens, type TokenName } from '@infinitibit_gmbh/ui/tokens';

tokens.color.action.primary.bg; // → "var(--ib-color-action-primary-bg)"

@infinitibit_gmbh/ui/tokens also exports tokenManifest (typed metadata per token — type, original value/reference) and TOKEN_NAMES for catalog/tooling use.

Per-theme values (e.g. the docs theme's brand color) live in theme packages (@infinitibit_gmbh/theme-*), which override a subset of --ib-* variables at build time — not in the token source. See the Themes docs.

Updating tokens (maintainers)

Tokens are generated from DTCG JSON in packages/ui/tokens-source/:

FileHolds
primitives.tokens.jsonpalette colors (incl. the neutral ramp), type, space
semantic.light.tokens.jsoncolor.* roles → reference primitives (light values); input.hover-fill, input.stroke
semantic.dark.tokens.jsonthe same roles with dark values (emitted under .dark)
foundations.tokens.jsonelevation, radius, shadow.*, z-index.* (mode-independent)

Workflow:

  1. Edit the relevant source file (never src/tokens/generated/).
  2. Regenerate: pnpm --filter @infinitibit_gmbh/ui tokens:generate.
  3. Commit both the source and the regenerated output.

verify:tokens (part of pnpm gate) fails the build if generated output is stale or hand-edited, so the source stays the single source of truth.

Two common edits:

  • A brand value changed (e.g. primary 500 is a new hex) → edit the primitive in primitives.tokens.json. Because semantics reference it via var(), every role that uses it updates automatically — you don't touch the semantic files.
  • A role should use a different value (e.g. buttons use primary 600) → edit the reference in semantic.light.tokens.json and semantic.dark.tokens.json (each mode is authored independently).

Neutral greys reference the palette.neutral.* ramp (50–950, plus 350 for an AA-safe dark tertiary), so they re-theme like any other token.

How it's built

DTCG source → Style Dictionary v5 → generated, committed artifacts under packages/ui/src/tokens/generated/ (tokens.css, tokens.ts, token-names.ts, manifest.ts). tsdown copies tokens.css into the build, served as @infinitibit_gmbh/ui/tokens.css. Rationale and the full decision record: ADR 0007.

The v1 Figma Variables REST pull (#24) will regenerate tokens-source/ directly from Figma — same DTCG contract, automated producer.

Token categories (current)

Colors (semantic)

GroupTokensPurpose
color.action.*primary.{bg,hover-bg,text}, secondary.*, critical.*interactive elements
color.surface.*canvas, subtle, input, overlays, raisedbackground layers
color.text.*primary, secondary, tertiary, inverse, link, color.text.disabledtext roles
color.status.*{success,info,critical,warning}.{bg,text,border}feedback/status
color.border.*primary, secondary, color.border.default, color.border.strong, color.border.subtleborder roles
color.input.*bg, border-default, border-focus, border-error, placeholderform inputs
color.nav.*bg, item-text, item-hover-bg, item-active-bg, item-active-textnavigation
color.overlay.*color.overlay.backdropmodal/drawer backdrops
color.avatar.*color.avatar.bg, color.avatar.textavatar fallback

Colors (primitives)

palette.{primary,blue,red,neutral,black,white}.* — raw scale, never used by components directly. The neutral ramp (50–950, plus 350) backs the grey text/border/surface roles in both modes.

Foundation

GroupTokensPurpose
elevation.*0205raw box-shadow values
shadow.*shadow.sm, shadow.md, shadow.lgsemantic shadow aliases → elevation.{02,03,04}
z-index.*z-index.dropdown (200), z-index.modal (300), z-index.tooltip (400)stacking order (unitless integers)
radius.*none, xs, sm, md, lg, xl, 2xl, fullcorner radii (sm/lg/xl/2xl alias space.* — Tailwind scale for now)

Typography

font.family.base (InterInter Variable, system-ui, sans-serif), font.weight.{regular,medium,semibold,bold}, font.size.*, font.line-height.*. Load the font in your app (e.g. @fontsource-variable/inter).

Space

space.* (Tailwind defaults for now).

Interim / deferred

Heading sizes h1–h4, heading line-heights, DTCG typography composites, elevation/01, dark-mode shadow values, and gradients are not yet in the set (tracked for token consolidation / #24). Dark-mode colors are shipped; only dark elevation is deferred.

On this page