Skip to main content

@renge-ui/tokens

System

Token API reference, animation system, and composition patterns. The mathematical foundation made programmable.

Token API

@renge-ui/tokens exports two ways to consume tokens: createRengeTheme() for generating CSS, and rengeVars for typed CSS variable references.

Every component in @renge-ui/react references CSS custom properties — never hardcoded values. Switch profiles by swapping which theme block is injected. No component re-renders required.

createRengeTheme(config?)

Generates a complete token set from mathematical first principles. Returns CSS ready to inject and a JS vars map.

import { createRengeTheme } from "@renge-ui/tokens";

const theme = createRengeTheme({
  profile:    "ocean",   // 'ocean' | 'earth' | 'twilight' | 'fire' | 'void' | 'leaf'
  mode:       "light",   // 'light' | 'dark'
  baseUnit:   4,         // Spacing multiplier in px — try 6 for denser UIs
  typeBase:   16,        // Root font size in px
  scaleRatio: 1.618,     // Typography scale ratio (φ = golden ratio)
  variance:   0,         // 0–1 tolerance drift (0 = exact math, deterministic)
  selector:   ":root",   // CSS selector to wrap the variables in
});

// theme.css   — full :root { --renge-* ... } string, ready to inject
// theme.vars  — Record<string, string> of every --renge-* variable
// theme.config — resolved config with all defaults applied

rengeVars

A statically typed object of CSS variable references. No runtime dependency — IDE autocomplete for every token.

import { rengeVars } from "@renge-ui/tokens";

// Color (22 semantic tokens, profile-reactive)
rengeVars.color.bg            // "var(--renge-color-bg)"
rengeVars.color.accent        // "var(--renge-color-accent)"
rengeVars.color.danger        // "var(--renge-color-danger)"

// Spacing (Fibonacci × baseUnit, steps 0–10)
rengeVars.space[3]   // "var(--renge-space-3)"  → 12px (Fib 3 × 4)
rengeVars.space[5]   // "var(--renge-space-5)"  → 32px (Fib 8 × 4)

// Typography (PHI scale, 8 steps)
rengeVars.fontSize.base       // "var(--renge-font-size-base)"
rengeVars.fontSize.lg         // "var(--renge-font-size-lg)"
rengeVars.lineHeight.base     // "var(--renge-line-height-base)"

// Motion (Fibonacci × 100ms, steps 0–10)
rengeVars.duration[2]         // "var(--renge-duration-2)"  → 200ms
rengeVars.duration[5]         // "var(--renge-duration-5)"  → 800ms
rengeVars.easing.out          // "var(--renge-easing-ease-out)"
rengeVars.easing.spring       // "var(--renge-easing-spring)"

// Radius (Fibonacci × baseUnit, steps none/1–5/full)
rengeVars.radius[2]           // "var(--renge-radius-2)"   → 8px
rengeVars.radius.full         // "var(--renge-radius-full)" → pill

// Layout (PHI-derived containers, Fibonacci column min-widths, φ aspect ratios)
rengeVars.container.sm        // "var(--renge-container-sm)"  → 524px (200 × φ²)
rengeVars.container.lg        // "var(--renge-container-lg)"  → 1371px (200 × φ⁴)
rengeVars.colMin.sm           // "var(--renge-col-min-sm)"    → 272px (34 × 8px)
rengeVars.aspect.golden       // "var(--renge-aspect-golden)" → 1.618034 (φ)
rengeVars.aspect.vertical     // "var(--renge-aspect-vertical)" → 0.618034 (1/φ)

Bridging to another system

Use rengeVars to map Renge tokens to your own CSS variable names — no string construction.

import { createRengeTheme, rengeVars } from "@renge-ui/tokens";

const theme = createRengeTheme({ profile: "earth", mode: "light" });

const aliases: [string, string][] = [
  ["--color-bg-primary",   rengeVars.color.bg],
  ["--color-bg-secondary", rengeVars.color.bgSubtle],
  ["--color-primary",      rengeVars.color.accent],
  ["--color-error",        rengeVars.color.danger],
  ["--space-sm",           rengeVars.space[2]],
  ["--space-md",           rengeVars.space[4]],
];

const aliasCSS = `:root {
${aliases.map(([k, v]) => `  ${k}: ${v};`).join("\n")}
}`;
document.head.insertAdjacentHTML("beforeend", `<style>${theme.css}\n${aliasCSS}</style>`);

Spacing tokens — --renge-space-{0–10}

Each step is FIBONACCI[n] × baseUnit. Default baseUnit = 4px. Growth is non-linear by design.

TokenFibonacciDefault (baseUnit=4)
--renge-space-000px
--renge-space-114px
--renge-space-228px
--renge-space-3312px
--renge-space-4520px
--renge-space-5832px
--renge-space-61352px
--renge-space-72184px
--renge-space-834136px
--renge-space-955220px
--renge-space-1089356px

Typography tokens — --renge-font-size-{size}

Formula: typeBase × scaleRatio^n. Default typeBase = 16px, scaleRatio = φ. Steps use fractional exponents for finer gradation at the small end.

TokenFormulaDefault
--renge-font-size-xsbase × φ^−0.5~12.6px
--renge-font-size-smbase × φ^−0.25~14.2px
--renge-font-size-basebase × φ⁰16px
--renge-font-size-lgbase × φ¹~25.9px
--renge-font-size-xlbase × φ²~41.9px
--renge-font-size-2xlbase × φ³~67.8px
--renge-font-size-3xlbase × φ⁴~109.7px
--renge-font-size-4xlbase × φ⁵~177.4px

Radius tokens — --renge-radius-{step}

Steps 1–5 follow Fibonacci × baseUnit. full = 9999px pill.

TokenDefault
--renge-radius-none0px
--renge-radius-14px (Fib 1 × 4)
--renge-radius-28px (Fib 2 × 4)
--renge-radius-312px (Fib 3 × 4)
--renge-radius-420px (Fib 5 × 4)
--renge-radius-532px (Fib 8 × 4)
--renge-radius-full9999px — pill

Layout tokens

Container widths, responsive column min-widths, and aspect ratios — all derived from φ and Fibonacci.

rengeVars.container.sm   // "var(--renge-container-sm)"   → 524px  (200 × φ²)
rengeVars.container.md   // "var(--renge-container-md)"   → 847px  (200 × φ³)
rengeVars.container.lg   // "var(--renge-container-lg)"   → 1371px (200 × φ⁴) ← default
rengeVars.container.xl   // "var(--renge-container-xl)"   → 2218px (200 × φ⁵)
rengeVars.container.full // "var(--renge-container-full)" → 100%

rengeVars.colMin.xs   // "var(--renge-col-min-xs)" → 168px (Fib 21 × 8)
rengeVars.colMin.sm   // "var(--renge-col-min-sm)" → 272px (Fib 34 × 8)
rengeVars.colMin.md   // "var(--renge-col-min-md)" → 440px (Fib 55 × 8)
rengeVars.colMin.lg   // "var(--renge-col-min-lg)" → 712px (Fib 89 × 8)

rengeVars.aspect.square   // "var(--renge-aspect-square)"   → 1
rengeVars.aspect.golden   // "var(--renge-aspect-golden)"   → 1.618034 (φ)
rengeVars.aspect.vertical // "var(--renge-aspect-vertical)" → 0.618034 (1/φ)
rengeVars.aspect.video    // "var(--renge-aspect-video)"    → 1.777778 (16/9)
rengeVars.aspect.classic  // "var(--renge-aspect-classic)"  → 1.333333 (4/3)

All 22 semantic color tokens

rengeVars keyCSS variableRole
color.bg--renge-color-bgPage background
color.bgSubtle--renge-color-bg-subtleSlightly elevated surface
color.bgMuted--renge-color-bg-mutedMuted surface
color.bgInverse--renge-color-bg-inverseInverted background
color.fg--renge-color-fgPrimary text
color.fgSubtle--renge-color-fg-subtleSecondary text
color.fgMuted--renge-color-fg-mutedPlaceholder / disabled
color.fgInverse--renge-color-fg-inverseText on inverse bg
color.border--renge-color-borderDefault divider
color.borderSubtle--renge-color-border-subtleHairline divider
color.borderFocus--renge-color-border-focusKeyboard focus ring
color.accent--renge-color-accentPrimary interactive
color.accentHover--renge-color-accent-hoverHover state
color.accentSubtle--renge-color-accent-subtleTinted background
color.success--renge-color-successPositive outcome
color.successSubtle--renge-color-success-subtleSuccess tint bg
color.warning--renge-color-warningCaution
color.warningSubtle--renge-color-warning-subtleWarning tint bg
color.danger--renge-color-dangerError / destructive
color.dangerSubtle--renge-color-danger-subtleDanger tint bg
color.info--renge-color-infoInformational
color.infoSubtle--renge-color-info-subtleInfo tint bg

Animations

15 named animations derived from the token system. Apply via the animation prop on any component, or directly via the CSS variable.

All 15 tokens — live

vortex-reveal
helix-rise
sacred-fade
spiral-in
morph-fade-in
bloom
pulse
vibrate
wave
breathe
fall
float
float-wave
pulse-color-shift
swelling

Live examples

breathe

floatpulse
bloom

wave

swelling
{/* animation prop — available on Heading, Text, Card, Section, Badge, and more */}
<Heading level={2} animation="breathe">Living proportion.</Heading>
<Badge variant="accent" animation="pulse">New</Badge>
<Card animation="bloom"><Text>Enters with bloom.</Text></Card>

{/* Via CSS variable — works on any element */}
<div style={{ animation: "var(--renge-animation-float)" }}>
  Floats gently.
</div>

{/* Duration tokens govern speed — Fibonacci × 100ms */}
/* breathe → var(--renge-duration-10) = 8900ms  (slow, organic) */
/* vibrate → var(--renge-duration-6)  = 1300ms  (quick, alert)  */
All 15 @keyframes blocks and their --renge-animation-* CSS variables are generated by createRengeTheme() — no separate import needed. Durations reference --renge-duration-* tokens derived from Fibonacci × 100ms.

Components with animation prop

The animation prop is available on: Heading, Text, Card, Section, Button, IconButton. All other elements can use the CSS variable directly via the style prop.

{/* animation prop — typed to AnimationName */}
<Heading level={2} animation="breathe">Living proportion.</Heading>
<Text size="lg" animation="float">Natural mathematics.</Text>
<Card animation="bloom"><Text>Enters with bloom.</Text></Card>
<Button animation="pulse">Subscribe</Button>

{/* Any element — via CSS variable */}
<div style={{ animation: "var(--renge-animation-spiral-in)" }}>
  Custom element.
</div>

{/* Combine with duration tokens for speed control */}
<div style={{
  animation: "var(--renge-animation-float)",
  animationDuration: "var(--renge-duration-7)",  // 2100ms — faster than default
}}>
  Faster float.
</div>

Easing tokens — --renge-easing-{curve}

All control points derived from φ: A = 1/φ² ≈ 0.382, B = 1/φ ≈ 0.618. Use these on any CSS transition or animation.

TokenCurveUse
--renge-easing-linearlinearProgress bars, scrubbing
--renge-easing-ease-outcubic-bezier(0.382, 1, 0.618, 1)Entrances — fast start, slow settle
--renge-easing-ease-incubic-bezier(0.382, 0, 1, 0.618)Exits — slow start, fast end
--renge-easing-ease-in-outcubic-bezier(0.382, 0, 0.618, 1)Balanced state transitions
--renge-easing-springcubic-bezier(0.382, 0.618, 0.618, 1.382)Playful overshoot
{/* Tailwind utility classes */}
<div class="ease-renge-ease-out duration-renge-3 transition-all">
  Entrances.
</div>

{/* CSS directly */}
.my-element {
  transition: transform var(--renge-duration-3) var(--renge-easing-ease-out);
}

{/* rengeVars — no string construction */}
import { rengeVars } from "@renge-ui/tokens";
style={{ transition: `opacity ${rengeVars.duration[3]} ${rengeVars.easing.out}` }}

Duration reference

TokenValueFibonacci derivation
--renge-duration-1100msFib 1 × 100ms
--renge-duration-2200msFib 2 × 100ms
--renge-duration-3300msFib 3 × 100ms
--renge-duration-4500msFib 5 × 100ms
--renge-duration-5800msFib 8 × 100ms — motion default
--renge-duration-61300msFib 13 × 100ms
--renge-duration-72100msFib 21 × 100ms — toast timeout
--renge-duration-83400msFib 34 × 100ms
--renge-duration-95500msFib 55 × 100ms — most animations
--renge-duration-108900msFib 89 × 100ms — breathe, bloom

Accessibility

Renge components and tokens are built to WCAG 2.1 Level AA accessibility standards. Every interactive component meets or exceeds the Web Content Accessibility Guidelines, ensuring interfaces built with Renge are usable by everyone.

Color Contrast — All 6 Profiles

Ensuring sufficient contrast between foreground and background hues is vital for users with low vision or color deficiencies. All 6 profiles meet WCAG AA (4.5:1 minimum for text, 3:1 for large text and UI elements) in both light and dark modes. Use a tool like WebAIM's Contrast Checker to validate specific color combinations in your application.
OceanAA ✅
Light: Sky-blue on near-white. The default.Dark: Bright sky-blue accent on deep teal-blue.
EarthAA ✅
Light: Amber on warm cream — parchment and ochre.Dark: Amber-ochre on deep soil brown.
LeafAA ✅
Light: Forest green on near-white. Cool, alive.Dark: Vivid leaf green on dark forest floor.
TwilightAA ✅
Light: Sunset pink-orange accent on periwinkle dusk.Dark: Amber horizon on deep inky indigo.
FireAA ✅
Light: Burning ember red-orange on near-white.Dark: Bright orange accent on near-black charcoal.
VoidAA ✅
Light: Dark cool grey on pure near-white. Minimal.Dark: Moonlight grey on the void (near-black).

WCAG 2.1 Compliance Checklist

1.4.3: Contrast (Minimum)AA
All text and interactive elements across light and dark modes meet the 4.5:1 minimum contrast ratio.Implementation: All 6 profiles (Ocean, Earth, Leaf, Twilight, Fire, Void) verified in both modes. fgMuted values adjusted: fire/twilight light L≤46%; void dark raised from L=40% to L=58%. Validate combinations with WebAIM Contrast Checker.
2.5.5: Target SizeAA⚠️
Full-size interactive elements (Button md/lg, Input, Select) meet 44×44px. Compact inline variants may fall below.Implementation: Full-size components meet WCAG 2.1 target. Compact variants (Button sm, Badge, CopyButton) are sized for context — WCAG 2.2 (2.5.8) relaxes this to 24×24px minimum.
2.4.7: Focus VisibleAA
All keyboard-interactive elements display a visible focus indicator.Implementation: Focus ring: 3px glow via box-shadow using --renge-color-accent at 25–28% opacity. Hidden inputs (Radio, Checkbox, Switch) show ring on visual indicator via CSS sibling selector. All overlay triggers (Drawer, Modal, DropdownMenu, Popover) return focus to trigger on close.
1.4.1: Use of ColorA
Status and states are never communicated by color alone.Implementation: Multi-modal indicators throughout: borders, outlines, checkmarks, icons, text labels.
1.3.1: Info & RelationshipsA
Page structure properly identified with semantic HTML.Implementation: H1 page title, logical heading hierarchy, semantic landmarks (main, nav). Form fields associated with labels via FormField.
2.3.3: Animation from InteractionsAA
Animations respect prefers-reduced-motion.Implementation: @media (prefers-reduced-motion) disables all transitions and SVG transforms site-wide.
4.1.2: Name, Role, ValueA
All interactive elements have accessible names, roles, and states.Implementation: aria-label, aria-expanded, aria-haspopup, aria-checked, aria-selected, aria-current, aria-modal, role attributes throughout all interactive components.
2.4.1: Bypass BlocksA
Skip link to main content visible on focus.Implementation: SkipLink component shows on :focus, focuses #main element.
2.1.1: KeyboardA
All functionality is operable via keyboard alone.Implementation: Every interactive component implements the ARIA keyboard patterns defined in the table below. Overlay components (Modal, Drawer) trap focus. Menus support arrow-key navigation.

Component-Level Features

Touch Targets

Full-size buttons, links, and inputs enforce a 44×44px minimum touch target. Compact variants are sized for context (WCAG 2.2 minimum is 24×24px).

Color Contrast

All text across light and dark modes meets WCAG AA (4.5:1) across all 6 color profiles. Validate your specific combinations using WebAIM's Contrast Checker.

Keyboard Navigation

Every interactive component implements ARIA keyboard patterns. Overlays (Modal, Drawer) trap focus. Menus support arrow-key navigation. All triggers return focus on close.

Screen Reader Support

Proper ARIA labels, roles, and live regions throughout. aria-expanded, aria-haspopup, aria-selected, aria-current, aria-modal, and aria-live used correctly.

Motion Preferences

All animations and transitions respect prefers-reduced-motion. Users who prefer reduced motion experience instant, flicker-free interactions.

Semantic HTML

Components use correct semantic elements. Interactive controls are buttons or links. Form fields are associated with labels. Page landmarks are properly structured.

Keyboard Patterns

ComponentKeysBehavior
Button / IconButtonEnter, SpaceActivate
Tabs← → Arrow, Home, EndNavigate between tabs (roving tabindex)
RadioGroup↑ ↓ ← → ArrowSelect next/previous radio (roving tabindex)
Rating← → Arrow, Home, EndDecrease/increase value; Home = 0, End = max
AccordionEnter, SpaceToggle panel open/closed
ModalEsc, Tab, Shift+TabClose on Esc; Tab cycles through focusable elements; focus returns to trigger on close
DrawerEsc, Tab, Shift+TabClose on Esc; focus trap within panel; focus returns to trigger on close
DropdownMenuEnter/Space to open, ↑ ↓ Arrow, Home, End, EscOpen/close; arrow keys navigate items; Esc closes and returns focus to trigger
PopoverEnter/Space to toggle, EscToggle open/close; Esc closes and returns focus to trigger
Combobox↑ ↓ Arrow, Enter, Esc, Home, EndArrow keys navigate filtered list; Enter selects highlighted; Esc closes; focus stays on input
MultiSelectEnter/Space to open, ↑ ↓ Arrow, Enter on option, EscOpen/close; arrow keys navigate options; Enter/Space toggles selection; Esc closes
ContextMenuEsc, ↑ ↓ ArrowEsc closes; arrow keys navigate menu items when open
Checkbox / SwitchSpaceToggle checked state
Select↑ ↓ Arrow, Enter, EscNative browser behavior (delegated to <select>)
Slider← → Arrow, Home, EndDecrease/increase value (native <input type=range>)
NumberInput↑ ↓ Arrow (on input)Increment/decrement value via native number input
StepperEnter, SpaceNavigate to step when onStepChange is provided
PaginationTab, Enter, SpaceTab between page buttons; Enter/Space activates
TagInputEnter or , to add, Backspace to remove lastAdd tag on Enter or comma; remove last tag on Backspace when input is empty
TooltipFocus / BlurShow on focus; hide on blur

WCAG 2.1 Level AA

Renge components meet WCAG 2.1 Level AA. All 6 color profiles (Ocean, Earth, Leaf, Twilight, Fire, Void) pass 4.5:1 contrast in both light and dark modes. All interactive components implement proper ARIA patterns and keyboard navigation. Interfaces built with Renge are accessible to users with disabilities by default.

Patterns

Common composition patterns using @renge-ui/react components. Every value resolves to a CSS custom property — switch profiles and all colors update instantly.

Status card

Build status

Passing
Tests114 / 114

Last run 2 minutes ago

Form with validation

Used for login and notifications.
Must be at least 12 characters.

Metric grid

Scale ratio
φ
Fibonacci 10
89 +34
Tests
114 +9
{/* Every component uses only CSS custom properties */}
{/* Switch profiles and all colors update instantly — no re-render */}
import { createRengeTheme } from "@renge-ui/tokens";

const theme = createRengeTheme({ profile: "twilight" });
// Inject theme.css — all @renge-ui/react components adapt automatically.
All 44 components use only var(--renge-*) CSS custom properties. Switch color profiles by swapping the injected theme block — no component state changes required.
Scale6.0