What Are CSS Custom Properties?

CSS custom properties — commonly called CSS variables — allow you to define reusable values directly in your stylesheet. Unlike preprocessor variables (like those in Sass or Less), CSS custom properties are live in the browser, meaning they respond to DOM changes, JavaScript updates, and even media queries.

They're declared with a double-dash prefix and consumed using the var() function:

:root {
  --color-primary: #7C3AED;
  --spacing-md: 1rem;
}

button {
  background-color: var(--color-primary);
  padding: var(--spacing-md);
}

Why Use Custom Properties Instead of Hardcoded Values?

  • Single source of truth: Change a value once, update it everywhere.
  • Runtime flexibility: JavaScript can read and write them on the fly.
  • Theming made easy: Toggle dark mode or brand themes with minimal code.
  • Cascade-aware: They inherit and cascade just like regular CSS properties.

Structuring Your Custom Properties

A clean way to organize variables is to group them by purpose. Think of them as design tokens — the foundational values that describe your design system:

:root {
  /* Colors */
  --color-bg: #F9F7FF;
  --color-text: #1E1B2E;
  --color-accent: #F59E0B;

  /* Typography */
  --font-size-base: 1rem;
  --font-size-lg: 1.25rem;
  --line-height-body: 1.6;

  /* Spacing */
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 2rem;

  /* Borders */
  --radius-sm: 4px;
  --radius-md: 8px;
}

Building a Dark Mode Theme with Custom Properties

One of the most popular applications of CSS variables is theme switching. Because custom properties cascade, you can redefine them under a class or media query:

:root {
  --color-bg: #ffffff;
  --color-text: #1E1B2E;
}

[data-theme="dark"] {
  --color-bg: #0F0A1E;
  --color-text: #F0ABFC;
}

body {
  background-color: var(--color-bg);
  color: var(--color-text);
}

Toggling the data-theme attribute via JavaScript instantly re-paints your entire UI without touching a single component.

Fallback Values

The var() function accepts a second argument as a fallback value, which is incredibly useful for resilient styling:

color: var(--color-brand, #7C3AED);

If --color-brand is not defined, the browser falls back to #7C3AED. You can even chain fallbacks.

Animating Custom Properties

While you can't directly animate custom properties with @keyframes in all browsers, you can pair them with @property (the Houdini Properties and Values API) to register typed custom properties that are animatable:

@property --hue {
  syntax: '<number>';
  initial-value: 0;
  inherits: false;
}

@keyframes hue-rotate {
  to { --hue: 360; }
}

Best Practices

  1. Always define global tokens on :root.
  2. Use component-scoped variables for local overrides.
  3. Name variables semantically (--color-action instead of --purple).
  4. Document your token names in a shared reference file or design system.
  5. Avoid deeply nested var(var(--x)) chains — they hurt readability.

Conclusion

CSS custom properties are more than a convenience — they're the backbone of scalable, themeable, and maintainable front-end code. Whether you're building a design system from scratch or refactoring an existing stylesheet, adopting a disciplined custom properties strategy will pay dividends across your entire project.