Skip to content
Foundation · Design Tokens v2

White-Labeling Guide

Enterprise customers can override semantic tokens to match brand identity without forking the design system. One API, 24 overrideable fields, full dark-mode support.

How It Works

1
Call createTheme()
Pass your brand colors, radius, and typography overrides. All other tokens default to the Nodus base theme.
2
Serialize to CSS
Use serializeThemeToCss() to get a CSS string. All values are developer-authored — safe for injection into a style tag.
3
Scope or global
Inject to :root for global brand, or to a CSS selector for per-tenant/per-page scope in multi-org apps.

Example Themes

Acme Corp — Live Preview
Financial services — conservative blue authority
Primary Button
Temporal
Agency
Temporal
Validation
Card component
Adapts to brand radius and colors
Theme Config
{
  "id": "acme-corp",
  "colors": {
    "agency": "#0055CC",
    "agencySubtle": "#E6EFFF",
    "agencyFg": "#FFFFFF",
    "temporal": "#5C6BC0",
    "temporalSubtle": "#EEF0FF"
  },
  "radius": {
    "sm": "2px",
    "md": "4px",
    "lg": "6px"
  }
}

Integration Code

import { createTheme, serializeThemeToCss } from "@nodus-ds/tokens";

const acmeTheme = createTheme({
  id: "acme-corp",
  colors: {
    // Override intent colors to match brand
    agency: "#0055CC",       // Acme blue instead of Nodus red
    agencySubtle: "#E6EFFF",
    agencyFg: "#FFFFFF",
    // Leave temporal/validation/surface as Nodus defaults
  },
  radius: {
    sm: "2px",   // Conservative radius for financial context
    md: "4px",
    lg: "6px",
  },
});

// Serialize to CSS string for injection
const cssText = serializeThemeToCss(acmeTheme, ":root");

Overrideable Fields

All fields are optional — defaults are the Nodus base theme. Override only what your brand requires.

ThemeConfig field
CSS variable
Description
colors.agency--ds-color-agencyPrimary authority color — replaces Nodus red
colors.agencySubtle--ds-color-agency-subtleSubtle tint for agency surfaces
colors.agencyFg--ds-color-agency-fgForeground on agency backgrounds
colors.temporal--ds-color-temporalProcessing/informational color
colors.temporalSubtle--ds-color-temporal-subtleSubtle tint for temporal surfaces
colors.temporalFg--ds-color-temporal-fgForeground on temporal backgrounds
colors.validation--ds-color-validationQuality/enrichment color
colors.validationSubtle--ds-color-validation-subtleSubtle tint for validation surfaces
colors.validationFg--ds-color-validation-fgForeground on validation backgrounds
colors.surfaceGround--ds-surface-groundPage background
colors.surfaceRaised--ds-surface-raisedCard / panel surface
colors.surfaceInverse--ds-surface-inverseDark surface, primary button bg
colors.textPrimary--ds-text-primaryPrimary text
colors.textSecondary--ds-text-secondaryBody text
colors.textMuted--ds-text-mutedCaptions, metadata
colors.textInverse--ds-text-inverseText on inverse surfaces
colors.borderStructure--ds-border-structureHeavy border
colors.borderSeparator--ds-border-separatorLight separator
typography.displayFont--ds-type-display-fontHeading typeface
typography.bodyFont--ds-type-body-fontBody typeface
typography.monoFont--ds-type-mono-fontMonospace typeface
radius.sm--ds-radius-smSmall element radius
radius.md--ds-radius-mdMedium element radius
radius.lg--ds-radius-lgLarge element radius

Do / Don't

DOOverride semantic tokens, not primitives
createTheme({
  colors: {
    agency: "#0055CC", // semantic token
  },
})

Semantic tokens are stable API surface. Primitive token names may change; semantic tokens are versioned.

DON'TDon't reference --bh-* tokens in your brand config
/* Avoid overriding primitives directly */
:root {
  --bh-red: #0055CC; /* wrong layer */
}

Primitives are internal implementation. Override at the semantic layer to avoid breakage when primitive names change.

DOPreserve WCAG contrast in foreground pairs
createTheme({
  colors: {
    agency: "#0055CC",    // 4.8:1 on white
    agencyFg: "#FFFFFF",  // pair verified
  },
})

The design system enforces minimum 4.5:1 for text. If you change a background color, verify the fg pair still passes.

DON'TDon't invert the three semantic meanings
// Red temporal is confusing —
// temporal should feel calm/informational
createTheme({
  colors: {
    temporal: "#D42020", // wrong: red = agency
  },
})

Agency (red), Temporal (blue), Validation (gold) are semantic meanings in this system. Inverting them confuses users who rely on consistent color language.

DOScope to a CSS selector for multi-tenant apps
const css = serializeThemeToCss(theme, '[data-org="acme"]');
// Styles apply only inside elements with data-org="acme"

Scoping prevents brand leakage between tenants in multi-org applications.

DON'TDon't use font sizes below the 10px floor
/* Below the accessibility minimum */
:root {
  --ds-type-size-caption: 8px; /* fail */
}

10px is the minimum font size. WCAG requires text to be perceivable. Tokens below 10px create accessibility violations.