@cdx-ui/styles
v0.0.1-beta.112
Published
Design tokens and theme infrastructure for the Forge Design System. Tokens flow from Figma Variables through a Style Dictionary pipeline into CSS custom properties, Tailwind v4 theme variables, and runtime theming artifacts — supporting three presets (Poi
Downloads
10,064
Readme
@cdx-ui/styles
Design tokens and theme infrastructure for the Forge Design System. Tokens flow from Figma Variables through a Style Dictionary pipeline into CSS custom properties, Tailwind v4 theme variables, and runtime theming artifacts — supporting three presets (Poise, Prestige, Pulse), light/dark modes, cross-platform fonts, and FI white-label overrides.
Installation
pnpm add @cdx-ui/stylesUsage
CSS imports (Tailwind v4 + Uniwind)
Import theme.css and utilities.css in your global stylesheet, after Tailwind and Uniwind:
@import 'tailwindcss';
@import 'uniwind';
@import '@cdx-ui/styles/theme.css';
@import '@cdx-ui/styles/utilities.css';theme.css declares all token variables — primitives in @theme static, mode-dependent semantics in @variant light/dark, and platform fonts in @variant ios/android/web. utilities.css provides composite typography utilities (heading-xl through body-xs, plus label-sm and label-xs).
Components then use standard Tailwind utilities backed by theme variables:
<View className="bg-surface-background p-4 rounded-lg">
<Text className="text-content-primary heading-md">Welcome</Text>
<Text className="text-content-secondary body-md">Get started below.</Text>
</View>CSS imports (non-Tailwind)
For consumers that do not use Tailwind, vanilla.css provides all variables as plain :root declarations with @media (prefers-color-scheme: dark) and class-based typography:
@import '@cdx-ui/styles/vanilla.css';Preset JSON
All three preset theme objects are importable as JSON for runtime theming:
import poise from '@cdx-ui/styles/presets/poise.json';
import prestige from '@cdx-ui/styles/presets/prestige.json';
import pulse from '@cdx-ui/styles/presets/pulse.json';TypeScript exports
The package exports DTCG-compatible type definitions, runtime constants, palette generation utilities, theming functions, and the useForgeFonts hook:
import type {
ThemeObject,
ThemeOverride,
ThemeMetadata,
ThemeOverrideMetadata,
TokenGroup,
TokenValue,
Preset,
Mode,
Platform,
CssVariableMaps,
RuntimeMap,
PaletteCategory,
PaletteScale,
PaletteStep,
PaletteTokenMap,
ApplyThemeOverrideOptions,
ApplyThemeOverrideResult,
} from '@cdx-ui/styles';
import {
useForgeFonts,
OVERRIDE_SCHEMA_VERSION,
SUPPORTED_OVERRIDE_SCHEMA_VERSIONS,
INPUT_TOKEN_MAP,
presetFonts,
DEFAULT_PRESET,
generateColorScale,
generatePalettesFromInputs,
deriveBaseColorKey,
applyThemeOverride,
isSchemaVersionSupported,
presetPatchToUniwindMaps,
themeOverrideToUniwindMaps,
} from '@cdx-ui/styles';Types:
ThemeObject— Complete Forge UI theme object (DTCG-compatible), includingmodes,platform, and$extensions.ThemeOverride— Hybrid FI override structure withinputs(brand values for palette generation) and optional per-modeoverrides.ThemeMetadata/ThemeOverrideMetadata— Metadata stored under$extensions.com.forge.ui.themeand$extensions.com.forge.ui.themeOverride.Preset— Union type:'poise' | 'prestige' | 'pulse'.TokenValue/TokenGroup— DTCG leaf node and recursive group types.Mode/Platform—'light' | 'dark'and'web' | 'ios' | 'android'.CssVariableMaps— Per-mode CSS variable maps ready forUniwind.updateCSSVariables.RuntimeMap— Token dot-paths → CSS custom property names.PaletteCategory/PaletteScale/PaletteStep/PaletteTokenMap— Palette generation types.ApplyThemeOverrideOptions/ApplyThemeOverrideResult— Options and result types forapplyThemeOverride.
Constants:
OVERRIDE_SCHEMA_VERSION— Current override contract version ('1.0.0'). Consuming apps gate FI override compatibility on this value.SUPPORTED_OVERRIDE_SCHEMA_VERSIONS— Array of schema versions accepted by this package version (includes current and optionally one prior version during transition windows).INPUT_TOKEN_MAP— Maps known input keys (brandPrimary,accentPrimary,basePrimary,displayFont) to the token path pattern each affects. Used by override application to route inputs to the correct palette namespace.presetFonts— Allowed display font families per preset, used by the Theme Editor for font selection and by consuming apps for validation.DEFAULT_PRESET— The default preset ('poise').
Palette generation:
generateColorScale(hex, category)— Generates an 11-step Leonardo contrast scale (50–950) plus aninputstep from a single hex color. Categories:'brand' | 'accent' | 'base'. Returns aPaletteTokenMapkeyed by token dot-paths (e.g.color.brand.50throughcolor.brand.input).generatePalettesFromInputs(inputs)— IteratesbrandPrimary,accentPrimary,basePrimaryfrom an override'sinputsobject; callsgenerateColorScalefor each present value. Returns a mergedPaletteTokenMap.deriveBaseColorKey(brandHex)— Derives a neutral base color key from a brand hex (brand hue/lightness at 5% saturation). Used by the Theme Editor to automatically computebasePrimaryfrombrandPrimary.
Theming utilities:
applyThemeOverride(override, options?)— High-level orchestrator: validates schema version → applies preset patch (if non-default base) → generates palettes from inputs → merges FI overrides → callsUniwind.updateCSSVariablesper mode. Returns a discriminated union:{ applied: true, light, dark }or{ applied: false, reason }. Options:runtimeMap,runtimePlatform. Bundles the default runtime map internally. Requiresuniwindas a peer dependency.isSchemaVersionSupported(override)— Schema version gate; returnstrueif the override'sschemaVersionis inSUPPORTED_OVERRIDE_SCHEMA_VERSIONS.presetPatchToUniwindMaps(patch, runtimeMap, runtimePlatform)— Walks a nested DTCG preset patch; buckets leaves by mode/platform into per-mode CSS variable maps.themeOverrideToUniwindMaps(override, runtimeMap)— Processes a hybrid FI override: palette generation frominputs, font mapping, per-modeoverridesmerge, and token-reference alias resolution.
A tree-shakeable subpath is also available:
import {
applyThemeOverride,
presetPatchToUniwindMaps,
themeOverrideToUniwindMaps,
} from '@cdx-ui/styles/theming';Hooks:
useForgeFonts— Loads all preset display fonts (nine families, three per preset) plus platform web fonts (Inter, IBM Plex Mono) viaexpo-font. Returns{ loaded: boolean; error: Error | null }. Call in your root layout and gate rendering on the returnedloadedboolean. See Getting Started for usage.useCdxFonts— Deprecated alias foruseForgeFonts. Retained for backward compatibility during the CDX → Forge rename transition. See Getting Started for the migration path.
Runtime artifacts
The build produces runtime JSON artifacts importable from the package:
import runtimeMap from '@cdx-ui/styles/runtime/token-to-css-var.json';
import prestigePatch from '@cdx-ui/styles/runtime/prestige-vs-default.json';
import pulsePatch from '@cdx-ui/styles/runtime/pulse-vs-default.json';runtime/token-to-css-var.json— Flat map of ~664 token dot-paths to CSS custom property names (e.g."color.brand.500": "--color-brand-500"). Used internally byapplyThemeOverride; importable for custom apply logic.runtime/prestige-vs-default.json/runtime/pulse-vs-default.json— Sparse preset patches (diff from build default to target preset). Used internally byapplyThemeOverridewhenbasePresetdiffers from the build default.
Token pipeline
The pipeline has two stages — fetch and build — both run from package scripts.
Fetching tokens from Figma
Tokens are pulled from Figma Variables via the REST API (GET /v1/files/:file_key/variables/local). This requires an Enterprise plan and a personal access token with file_variables:read scope.
FIGMA_VARIABLES_TOKEN=your_token pnpm tokens:fetchThe script reads figma.config.json for the file key, preset list, and default FI mode, then assembles one DTCG-compatible theme object JSON per preset:
tokens/presets/
poise.json # Default build preset
prestige.json
pulse.json
.manifest.json # SHA-256 checksums (written by fetch).manifest.json records the SHA-256 hash of each preset JSON after fetch. It is used by CI and downstream tooling to detect whether preset files changed between fetches — if the checksums match, the build step can be skipped.
Each file is assembled from four Figma collections: Primitives (shared), FI Primitives (Candescent mode — brand/accent/base colors), Semantics ({Preset}) (Light + Dark modes), and Platform (Web/iOS/Android fonts). Alias references are preserved as DTCG "{path.to.token}" syntax.
Figma MCP alternative
If the Figma MCP server is configured in Cursor, you can inspect variables interactively. Select a frame/layer and ask the agent for variable names and values. This is useful for ad-hoc inspection but does not replace the fetch script for full pipeline runs.
Building tokens
Style Dictionary v4 reads the default preset JSON and produces all output artifacts:
pnpm tokens:buildCSS outputs (written to css/):
| File | Contents |
| --------------- | --------------------------------------------------------------------------------------------------- |
| theme.css | @theme static (primitives + semantic defaults), @variant light/dark, @variant ios/android/web |
| utilities.css | @utility blocks for composite typography (headings, body-xl through body-xs, label-sm, label-xs) |
| vanilla.css | Non-Tailwind fallback — :root variables + @media (prefers-color-scheme: dark) + classes |
Runtime artifacts (generated by tokens:build):
| Artifact | File | Purpose |
| ------------------ | ---------------------------------- | ------------------------------------------------------------------ |
| Runtime map | runtime/token-to-css-var.json | Theme object path → CSS custom property name for sparse flattening |
| Preset patches | runtime/{preset}-vs-default.json | Sparse diff from build default → each non-default preset |
Validating font tokens
Font registration names in useForgeFonts must exactly match the --font-* values emitted into theme.css — a mismatch causes silent fallback to platform default fonts with no error. The validation script catches this drift as a build/CI failure:
pnpm fonts:validateIt parses every --font-* declaration from css/theme.css (across @theme static, @variant light/dark, and @variant ios/android/web), reads the font map keys from src/useForgeFonts.ts source, and verifies a matching key exists for every non-system value. System fonts (SF Pro, SF Mono, Roboto, Roboto Mono) are read from figma.config.json and excluded — they are platform built-ins resolved natively by React Native. var() aliases are skipped because they resolve to other variables that are themselves validated.
On success the script prints Font validation: N CSS values checked, N matched, 0 mismatches. and exits 0. On failure it lists each mismatch with its variable name, value, and CSS section, then exits non-zero. The script is wired into .github/workflows/ci.yml as a pre-build step so font-token drift fast-fails before the heavier build/lint/test stages.
Theme architecture
Presets
Three presets ship with the package — Poise (default), Prestige, and Pulse — each a complete theme object with all primitives, semantic tokens (light + dark), platform fonts, and typography composites. Poise is compiled into CSS at build time. Apps using a different preset apply a build-generated sparse patch via Uniwind.updateCSSVariables.
Modes and platforms
Light/dark mode switching is handled by Uniwind @variant light/dark blocks in theme.css. Platform-specific fonts (SF Pro on iOS, Roboto on Android, Inter on web) use @variant ios/android/web blocks. Both are activated automatically by Uniwind at runtime.
FI overrides
Financial institution white-labelling uses the hybrid override format — brand inputs (a single hex color expanded into a full palette at runtime) plus optional per-mode semantic token overrides. The Theme API is a pass-through store; validation is performed by the Theme Editor before save. Consuming apps apply overrides at runtime via applyThemeOverride (which handles palette generation, runtime map lookup, and Uniwind.updateCSSVariables calls internally). See Override Structure for the payload format and responsibility contract, and Theme Definition for the full theme object schema.
Package structure
styles/
├── css/
│ ├── theme.css # Auto-generated — all token variable declarations
│ ├── utilities.css # Auto-generated — composite typography @utility blocks
│ └── vanilla.css # Auto-generated — non-Tailwind fallback
├── runtime/
│ ├── token-to-css-var.json # Build-generated — token path → CSS variable name map
│ ├── prestige-vs-default.json # Build-generated — sparse preset patch (Poise → Prestige)
│ └── pulse-vs-default.json # Build-generated — sparse preset patch (Poise → Pulse)
├── scripts/
│ ├── figma-fetch-variables.mjs # Figma Variables REST API fetch
│ ├── build-tokens.mjs # Style Dictionary build + artifact generation
│ └── validate-font-tokens.mjs # CI check: --font-* values match useForgeFonts keys
├── src/
│ ├── index.ts # Public barrel: types, constants, palette, theming, hooks
│ ├── palette.ts # Color scale generation (Leonardo contrast algorithm)
│ ├── theming.ts # Sparse flatten helpers (presetPatchToUniwindMaps, themeOverrideToUniwindMaps)
│ ├── applyThemeOverride.ts # High-level orchestrator (schema gate → patch → palette → apply)
│ ├── types.ts # DTCG types, override contract, constants
│ ├── useForgeFonts.ts # Font loader hook (expo-font) — loads all preset display fonts
│ └── useCdxFonts.ts # Deprecated alias for useForgeFonts
├── tokens/
│ └── presets/
│ ├── .manifest.json # SHA-256 checksums (written by fetch)
│ ├── poise.json # Poise theme object (build default)
│ ├── prestige.json # Prestige theme object
│ └── pulse.json # Pulse theme object
├── figma.config.json # File key, preset list, default preset/FI mode
├── sd.config.ts # Style Dictionary v4 configuration
├── package.json
├── tsconfig.json
└── tsconfig.build.jsoncss/ and runtime/ files are auto-generated — do not edit directly.
Building
pnpm --filter @cdx-ui/styles buildTypeScript sources are compiled by react-native-builder-bob to lib/ with CommonJS, ESM, and declaration targets.
Further reading
- Token Architecture — full pipeline documentation (Figma analysis, theme definition, output architecture, Style Dictionary config, expected CSS output, runtime theming guide)
