Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
CSSHigh

Use CSS custom properties for design tokens

Define design system values (colors, spacing, typography) as CSS custom properties on :root to enable consistent theming, dynamic updates, and dark mode support.

Utilities
Quick take
Typical fix time 20 min
  • Define reusable values as --variable-name on :root
  • Custom properties cascade and inherit — they can be overridden at any scope
  • Use var(--name, fallback) to provide fallback values
  • Custom properties are runtime values — JavaScript can read and write them
Why it matters: Hard-coded color values and magic numbers scattered across CSS files make global changes (a rebrand, a dark mode, a spacing adjustment) require finding and updating dozens of individual lines. Custom properties centralize these values so one change propagates everywhere — and enable runtime theming that preprocessor variables cannot.

Rule Details

CSS custom properties (often called CSS variables) are properties you define yourself, prefixed with --, that cascade and inherit just like standard CSS properties.

Code Example

:root {
  /* Colors */
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-surface: #ffffff;
  --color-text: #111827;
  --color-text-muted: #6b7280;
  --color-border: #e5e7eb;
 
  /* Spacing scale */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
 
  /* Typography */
  --font-sans: 'Inter', system-ui, sans-serif;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
 
  /* Borders */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-full: 9999px;
 
  /* Shadows */
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
}

Why It Matters

Hard-coded color values and magic numbers scattered across CSS files make global changes (a rebrand, a dark mode, a spacing adjustment) require finding and updating dozens of individual lines. Custom properties centralize these values so one change propagates everywhere — and enable runtime theming that preprocessor variables cannot.

Using Custom Properties

.button {
  background-color: var(--color-primary);
  color: var(--color-surface);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
 
  /* Fallback value if variable is undefined */
  box-shadow: var(--shadow-button, var(--shadow-sm));
}
 
.button:hover {
  background-color: var(--color-primary-hover);
}

Component-Scoped Overrides

/* Override a variable at the component level */
.card {
  --color-surface: #f9fafb;
  --shadow-md: 0 8px 12px -2px rgb(0 0 0 / 0.15);
 
  background: var(--color-surface);
  box-shadow: var(--shadow-md);
}

Dark Mode with Custom Properties

:root {
  --color-surface: #ffffff;
  --color-text: #111827;
  --color-border: #e5e7eb;
}
 
@media (prefers-color-scheme: dark) {
  :root {
    --color-surface: #1f2937;
    --color-text: #f9fafb;
    --color-border: #374151;
  }
}
 
/* All components using these variables update automatically */

JavaScript Interop

// Read a custom property
const primaryColor = getComputedStyle(document.documentElement)
  .getPropertyValue('--color-primary').trim()
 
// Write a custom property (dynamic theming)
document.documentElement.style.setProperty('--color-primary', '#10b981')
 
// Theme switching
function setTheme(theme) {
  document.documentElement.setAttribute('data-theme', theme)
}

Verification

  1. Inspect the rendered UI at the breakpoints and interaction states affected by the rule.
  2. Confirm the computed styles match the intended fix in DevTools.
  3. Test at least one mobile and one desktop viewport before shipping.
  4. If the rule affects motion, contrast, or layout stability, verify those user-facing outcomes directly.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Find hard-coded color values, spacing values, and font sizes in this CSS file that should be replaced with CSS custom properties.

Fix

Auto-fix issues

Extract repeated and meaningful values into CSS custom properties on :root and replace all usages with var() references.

Explain

Learn more

Explain CSS custom properties: how they differ from preprocessor variables, how cascade and inheritance work, and how to use them for theming.

Review

Code review

Review stylesheets, component styles, and responsive states related to Use CSS custom properties for design tokens. Flag exact selectors, declarations, or breakpoints that violate the rule in the rendered UI.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

MDN CSS Custom Propertiesdeveloper.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Support dark mode with prefers-color-scheme

Implement dark mode using the prefers-color-scheme media query and CSS custom properties so the site automatically adapts to the user's system preference.

CSS
Use consistent CSS naming conventions

Adopt a consistent class naming methodology (BEM, CUBE CSS, or a team-agreed pattern) to make class names self-documenting and prevent style conflicts.

CSS
Use oklch() and oklab() for perceptually uniform colour palettes

Colour values in the design system use oklch() or oklab() colour functions to produce perceptually uniform palettes where equal numeric steps produce equal perceived lightness changes.

CSS
Avoid embedded and inline CSS

Embedded and inline CSS are avoided except for critical CSS and performance optimization.

CSS

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385