Custom Properties Basics

A CSS custom property is declared with a double-dash prefix and accessed with the var() function. They're most commonly defined on :root for global access, but can be scoped to any selector:

CSS
:root {
  --color-primary: #7c6fff;
  --color-secondary: #ff6fb0;
  --radius-md: 12px;
  --spacing-lg: 32px;
}

.button {
  background: var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--spacing-lg);
}

/* With fallback value */
.card {
  color: var(--color-text, #333);
}

The var() function accepts an optional second argument โ€” a fallback value used if the custom property is not defined. This is useful for component-level defaults that can be overridden by parent context.

Cascading & Scoping

Unlike preprocessor variables (Sass, Less), CSS custom properties cascade and inherit just like regular CSS properties. This means you can redefine them at any level of the DOM tree:

CSS
:root {
  --accent: #7c6fff;
}

.sidebar {
  --accent: #ff6fb0;  /* overrides for sidebar and descendants */
}

.button {
  background: var(--accent);
  /* Purple in main content, pink in sidebar */
}

This cascading behavior is incredibly powerful for component theming. A single component can adapt its appearance based on the context it's placed in, without any JavaScript or CSS modifications.

Dynamic Theming

The most common use of custom properties is building a theme system. By defining all your colors, sizes, and other design decisions as custom properties, switching themes becomes trivial:

CSS
:root {
  --bg: #0a0a0f;
  --surface: #13131a;
  --text: #f0f0ff;
  --muted: #7070a0;
  --accent: #7c6fff;
}

:root.light {
  --bg: #f5f5f8;
  --surface: #ffffff;
  --text: #1a1a2e;
  --muted: #6a6a8a;
  --accent: #6c5ce7;
}

/* Every component using these variables
   automatically adapts to the theme */
body { background: var(--bg); color: var(--text); }
.card { background: var(--surface); }

Toggle the theme by adding or removing a class on the root element โ€” that's all it takes. No stylesheet swapping, no complex JavaScript logic, and the transition is instant.

Design Tokens with CSS Variables

Design tokens are the atomic building blocks of a design system โ€” the named values for color, typography, spacing, and more. CSS custom properties are a perfect fit for implementing tokens:

CSS
:root {
  /* Primitive tokens */
  --blue-500: #7c6fff;
  --pink-500: #ff6fb0;
  --gray-100: #f0f0ff;
  --gray-900: #0a0a0f;

  /* Semantic tokens (reference primitives) */
  --color-primary: var(--blue-500);
  --color-accent: var(--pink-500);
  --color-bg: var(--gray-900);
  --color-text: var(--gray-100);

  /* Component tokens (reference semantic) */
  --button-bg: var(--color-primary);
  --card-bg: var(--color-bg);
}

This three-tier structure (our Gradient Generator uses this pattern) (primitive โ†’ semantic โ†’ component) gives you maximum flexibility. Rebranding? Change the primitive tokens. Dark mode? Override the semantic tokens. Custom button variant? Override the component tokens.

Interacting with JavaScript

Custom properties can be read and written from JavaScript, making them a bridge between CSS and JS for dynamic styling:

JavaScript
// Read a custom property value
const root = document.documentElement;
const accent = getComputedStyle(root)
  .getPropertyValue('--accent').trim();

// Set a custom property
root.style.setProperty('--accent', '#ff6fb0');

// Respond to user input
slider.addEventListener('input', (e) => {
  root.style.setProperty('--blur', e.target.value + 'px');
});

Practical Tips

  • Name with purpose: Use semantic names like --color-primary instead of --blue. When your brand color changes, you won't need to rename everything.
  • Use fallbacks: Always provide a fallback value in var() for properties that might not be defined: var(--accent, #7c6fff).
  • Don't over-abstract: Not every value needs to be a variable. If a value is used once and is unlikely to change, a hard-coded value is clearer.
  • Group logically: Organize your custom properties into sections โ€” colors, typography, spacing, shadows, borders โ€” with comments separating each group.
  • Performance: Custom properties are resolved at computed-value time and are very fast. Thousands of variables on a page won't cause performance issues.

See CSS Variables in Action

Our tools use CSS custom properties for theming. Try them out and explore the generated code.

Try Gradient Generator โ†’