Design System Problems

Token Override Patterns

January 15, 2026 • 5 min read

Token Override Patterns

Token override patterns provide structured approaches for customizing token values within defined boundaries. Overrides enable themes, brand variations, component customization, and product-specific adjustments while maintaining system coherence. Well-designed override patterns balance flexibility with control, preventing chaos while enabling legitimate customization.

What Are Token Override Patterns

Token override patterns are established mechanisms for replacing or modifying token values in specific contexts. Rather than editing source tokens directly, overrides layer on top, changing values where needed while preserving the base system.

Patterns define how overrides are structured, where they apply, and how conflicts are resolved. Different patterns suit different customization needs.

How Token Override Patterns Work

Layer override pattern stacks token sets:

Base tokens → Theme overrides → Brand overrides → Product overrides

Each layer can override tokens from previous layers:

// Build configuration
module.exports = {
  source: [
    'tokens/base/**/*.json',      // Base tokens
    'tokens/dark/**/*.json',       // Theme overrides
    'tokens/brand-b/**/*.json'     // Brand overrides
  ]
};

Later sources override earlier sources for matching token names.

Scope override pattern limits override applicability:

/* Global token */
:root {
  --color-primary: blue;
}

/* Scoped override */
.marketing-section {
  --color-primary: green;
}

Overrides apply only within the scoped element.

Variant override pattern creates named variations:

{
  "button": {
    "background": {
      "default": { "$value": "{color.primary}" },
      "destructive": { "$value": "{color.error}" }
    }
  }
}

Components select appropriate variants rather than overriding base values.

Configuration override pattern uses runtime configuration:

const config = {
  overrides: {
    'color.primary': '#custom-color'
  }
};

const tokens = applyOverrides(baseTokens, config.overrides);

Key Considerations

Common Questions

How should override boundaries be defined?

Override boundaries establish what can be customized and to what degree.

Token-level boundaries:

{
  "color": {
    "primary": {
      "$value": "#3B82F6",
      "$overridable": true
    },
    "error": {
      "$value": "#DC2626",
      "$overridable": false  // Critical for UX consistency
    }
  }
}

Category-level boundaries:

Documentation of boundaries:

## Override Policy

### Fully Customizable
- Brand colors (color.brand.*)
- Component borders (component.*.border)

### Customizable with Approval
- Semantic colors (color.semantic.*)
- Typography scale (font.size.*)

### Not Customizable
- Status colors (color.status.*)
- Spacing scale (spacing.*)

Clear boundaries enable customization while protecting system integrity.

How should conflicting overrides be handled?

Conflicts occur when multiple overrides affect the same token.

Explicit precedence rules:

Product overrides > Brand overrides > Theme overrides > Base

The most specific context wins.

Merge strategies for object tokens:

// Shallow merge: Later completely replaces
const result = { ...base, ...override };

// Deep merge: Later supplements
const result = deepMerge(base, override);

Conflict detection:

function detectConflicts(layers) {
  const conflicts = [];
  const seen = new Map();

  layers.forEach(layer => {
    Object.keys(layer.tokens).forEach(name => {
      if (seen.has(name)) {
        conflicts.push({
          token: name,
          sources: [seen.get(name), layer.name]
        });
      }
      seen.set(name, layer.name);
    });
  });

  return conflicts;
}

Conflict resolution:

What about runtime versus build-time overrides?

Build-time overrides compile into final outputs:

// Different builds for different contexts
npm run build:brand-a  // Produces brand-a tokens
npm run build:brand-b  // Produces brand-b tokens

Advantages: No runtime overhead, simple debugging Disadvantages: Requires separate builds, no dynamic switching

Runtime overrides apply during execution:

// Runtime theme switching
document.documentElement.dataset.theme = 'dark';
:root { --color-bg: white; }
[data-theme="dark"] { --color-bg: black; }

Advantages: Dynamic switching, single build Disadvantages: Runtime overhead, more complex debugging

Hybrid approach uses build-time for structural overrides and runtime for user preferences:

Build: Brand A vs Brand B (different builds)
Runtime: Light vs Dark theme (CSS switching)

Summary

Token override patterns provide structured customization within defined boundaries. Layer patterns stack token sets for brand and theme variations. Scope patterns limit overrides to specific contexts. Variant patterns offer named alternatives. Clear boundaries define what is overridable and to what degree. Conflict resolution follows explicit precedence rules. Build-time versus runtime override selection depends on dynamism requirements and performance considerations.

Buoy scans your codebase for design system inconsistencies before they ship

Detect Design Drift Free
← Back to Token Management