Color tokens

The system generates semantic tokens from palettes. Each token has a tone optimized for the configured light/dark mode and contrast level.

Usage in Tailwind CSS

Tokens are exposed as CSS variables and Tailwind utility classes:

html
<!-- Surface background -->
<div class="bg-surface text-on-surface">...</div>

<!-- Primary button -->
<button class="bg-primary text-on-primary">Action</button>

<!-- Secondary container -->
<div class="bg-secondary-container text-on-secondary-container">...</div>

Token reference

Surfaces

Surfaces form the layers of the interface. They range from darkest (surfaceContainerLowest) to lightest (surfaceContainerHighest) in light mode, and inversely in dark mode.

TokenTailwind classUsage
surfacebg-surfaceMain background
surfaceDimbg-surface-dimSlightly dimmed surface
surfaceBrightbg-surface-brightSlightly brightened surface
surfaceContainerLowestbg-surface-container-lowestLayer 0 (lowest)
surfaceContainerLowbg-surface-container-lowLayer 1
surfaceContainerbg-surface-containerLayer 2 (cards, dialogs)
surfaceContainerHighbg-surface-container-highLayer 3
surfaceContainerHighestbg-surface-container-highestLayer 4 (highest)
onSurfacetext-on-surfacePrimary text on surface
onSurfaceVarianttext-on-surface-variantSecondary text on surface
outlineborder-outlineVisible borders
outlineVariantborder-outline-variantSubtle dividers
inverseSurfacebg-inverse-surfaceInverted surface (snackbar, tooltip)
inverseOnSurfacetext-inverse-on-surfaceText on inverted surface

Primary

TokenTailwind classUsage
primarybg-primaryMain actions, CTAs
onPrimarytext-on-primaryText on primary
primaryContainerbg-primary-containerSoft colored areas
onPrimaryContainertext-on-primary-containerText on primaryContainer
inversePrimarytext-inverse-primaryLink on inverted surface

Secondary

TokenTailwind classUsage
secondarybg-secondarySecondary accents
onSecondarytext-on-secondaryText on secondary
secondaryContainerbg-secondary-containerChips, badges
onSecondaryContainertext-on-secondary-containerText on secondaryContainer

Tertiary

TokenTailwind classUsage
tertiarybg-tertiaryAdditional accents
onTertiarytext-on-tertiaryText on tertiary
tertiaryContainerbg-tertiary-containerSoft tertiary areas
onTertiaryContainertext-on-tertiary-containerText on tertiaryContainer

Error

TokenTailwind classUsage
errorbg-errorError state
onErrortext-on-errorText on error
errorContainerbg-error-containerSoft error area
onErrorContainertext-on-error-containerText on errorContainer

Aliases

These tokens are aliases for Material Design compatibility:

AliasPoints to
backgroundsurface
onBackgroundonSurface
surfaceVariantsurfaceContainerHighest
surfaceTintprimary

Raw CSS variables

Variables are generated as --color-{token} with camelCase converted to kebab-case. The build output registers them in Tailwind’s @theme block; the runtime layer scopes live values to the configured dynamic selector:

css
@theme {
  --color-primary: #6750a4;
  --color-on-primary: #ffffff;
  --color-primary-container: #eaddff;
  --color-surface: #fef7ff;
  /* … */
}

@layer theme {
  .dynamic {
    --color-primary: #6750a4;
    --color-on-primary: #ffffff;
    /* … */
  }

  .dark .dynamic {
    --color-primary: #d0bcff;
    --color-on-primary: #381e72;
    /* … */
  }
}

Custom color tokens (Udixio variant)

With variant: Variants.Udixio and custom palettes, these tokens are generated for each {name} palette:

css
--color-{name}
--color-on-{name}
--color-{name}-container
--color-on-{name}-container
html
<!-- Example with "brand" palette -->
<div class="bg-brand text-on-brand">...</div>
<div class="bg-brand-container text-on-brand-container">...</div>

Adding custom tokens

Add custom semantic tokens directly to theme.config.ts. A token can be a static hex value, a dynamic Color, or a transformation of the token already provided by the active variant:

ts
// theme.config.ts
import { defineConfig } from '@udixio/tailwind';
import {
  Color,
  contrastAgainst,
  onColor,
  avoidBackgroundGap,
} from '@udixio/theme';

export default defineConfig({
  sourceColor: '#6750A4',
  colors: {
    highlight: Color.fromPalette('tertiary', {
      tone: () => 40,
      adjustTone: [contrastAgainst('surface', 3), avoidBackgroundGap()],
    }),
    onHighlight: Color.fromPalette('tertiary', {
      adjustTone: onColor('highlight', 4.5),
    }),
    surface: (color) => color.withTone(Math.min(100, color.tone + 2)),
  },
});

See Colors API for every adjuster and how to write your own.

Reading tokens from the API

A token is a Color, read as a value — each access resolves against the current dark mode and contrast level:

ts
const api = await loader(config);
await api.load();

// With sourceColor '#6750A4' and the default variant:
const primary = api.colors.get('primary');

primary.hex; // "#655789"
primary.tone; // 40.3 in light mode, 80 in dark
primary.hue; // 299.4
primary.chroma; // 32.2
primary.rgb; // { r: 101, g: 87, b: 137 }
primary.argb; // 4284831625

Derivations remain dynamic. They are evaluated again when the source token is resolved for another context or variant:

ts
primary.withTone(90); // the same hue and chroma, at tone 90
primary.rotate(55); // hue shifted by 55°, normalised over 360
primary.scaleChroma(0.5);

primary.contrastWith(api.colors.get('surface')); // 6.08

See Color for the constructors.