Efniv0.1

Overview

Token architecture

Three tiers, two independent axes. Components only ever touch the top tier. Tokens resolve down through the layers to a raw OKLCH value.

Primitives

ref/*
Never swaps.

Raw values, named by hue + step. Never referenced by components.

--ref-blue-500, --ref-gray-300

Brand

tool/*
Swaps per tool.

Per-tool accent. One tool per Figma brand mode.

--tool-accent, --tool-accent-strong

Semantic

color/*
Swaps per theme.

What components consume. Aliases a primitive or the brand layer.

--color-bg-canvas, --color-text-primary, --color-brand

Two independent axes

Theme (light/dark) lives on the Semantic collection, driven by [data-theme]. Tool (which accent) lives on the Brand collection, driven by [data-tool]. They combine freely — dark theme + Tool Two accent is just data-theme="dark" data-tool="tool-two" on any subtree.

Bridge to shadcn

shadcn ships with its own variable names — --background, --primary, --muted-foreground. Efni's bridge.css assigns those to Efni tokens, so shadcn components inherit theming without edits.

/* bridge.css (excerpt) */
:root {
  --background: var(--color-bg-canvas);
  --foreground: var(--color-text-primary);
  --primary: var(--color-brand);
  --primary-foreground: var(--color-text-on-accent);
  --accent: var(--color-bg-hover);   /* NOT the brand */
  --border: var(--color-border-default);
  --ring: var(--color-focus-ring);
}

Hard rules

  • Only reference semantic tokens from components. Never a primitive, never a hex.
  • shadcn's --accent is a neutral hover surface, not the brand. Brand is --color-brand (mapped to shadcn --primary).
  • Text tokens use emphasis, not lightness (primary, secondary, tertiary) so names stay correct across themes.
  • Turn on font-variant-numeric: tabular-nums wherever numbers align.