Skip to content
Now accepting Q2 projects — limited slots available. Get started →
Design · Updated Apr 30, 2026

What is CSS Custom Properties (Variables)?

CSS Custom Properties are author-defined values declared with `--` syntax that enable reusable, dynamic styling in stylesheets.

What is CSS Custom Properties (Variables)?

CSS Custom Properties — commonly called CSS Variables — are entities defined by CSS authors using the -- prefix that store reusable values within the cascade. Introduced in the CSS Custom Properties for Cascading Variables Module Level 1 spec (W3C Candidate Recommendation, first published 2015, broadly supported in browsers since 2017), they let you define a value once and reference it anywhere via var(). Unlike preprocessor variables from Sass or Less, custom properties are live in the browser: they participate in the cascade, can be inherited by child elements, and can be updated at runtime with JavaScript. As of April 2026, global browser support sits above 97% per caniuse.com. A common use case is theming — toggling between light and dark modes by swapping a handful of property values on :root or a parent selector.

How it works

You declare a custom property on any selector. The :root pseudo-class is the conventional place for global values:

:root {
  --color-primary: #2563eb;
  --spacing-md: 1rem;
  --font-body: 'Inter', system-ui, sans-serif;
}

.card {
  background: var(--color-primary);
  padding: var(--spacing-md);
  font-family: var(--font-body);
}

Key mechanics:

  • Cascade and inheritance: Custom properties follow normal CSS cascade rules. A property set on .theme-dark overrides the same property on :root for that subtree. This is the core trick behind scoped theming.
  • Fallback values: var(--color-accent, #f59e0b) returns the fallback if --color-accent is undefined.
  • Runtime updates via JS: document.documentElement.style.setProperty('--color-primary', '#dc2626') instantly repaints every element referencing that variable. No class toggling, no stylesheet swaps.
  • Invalid at computed-value time: If a custom property resolves to an invalid value for the property it's used in (e.g., color: var(--spacing-md) where the value is 1rem), the property falls back to its inherited or initial value — not to the var() fallback. This trips people up.

Custom properties can reference other custom properties: --color-surface: hsl(var(--hue), 90%, 95%). This composability is what makes them powerful for design token systems.

When to use it

Use custom properties when:

  • You're building a theming system (light/dark mode, white-label products). We've shipped this on 50+ projects and it's the single most effective pattern for multi-theme sites.
  • You need runtime style changes driven by user interaction or JS state — sliders controlling layout gaps, dynamic accent colors, etc.
  • You want a single source of truth for design tokens that CSS, JS, and frameworks can all read.
  • You're working with component-scoped styles in frameworks like Astro or web components where Sass variables can't cross shadow DOM boundaries but inherited custom properties can.

Skip them when:

  • You need compile-time logic (loops, conditionals, mixins). Preprocessors still own that space.
  • You're targeting legacy IE11 environments (support is zero there).
  • The value never changes and is used once — a plain value is simpler.

CSS Custom Properties vs alternatives

Feature CSS Custom Properties Sass/Less Variables Design Tokens (e.g., Style Dictionary)
Runtime update ✅ Yes ❌ Compiled away Depends on output format
Cascade/inheritance ✅ Yes ❌ No N/A — generates variables
Browser support 97%+ N/A (preprocessor) N/A
Shadow DOM penetration ✅ Via inheritance ❌ No Depends on output
Conditional logic ❌ Limited ✅ Mixins, loops ✅ Build-time transforms
Tooling required None Sass compiler Build pipeline (Style Dictionary, Tokens Studio)

Our preferred stack: define design tokens in a tool like Tokens Studio, export them as CSS custom properties via Style Dictionary, and consume them in Tailwind CSS v4's @theme directive or plain CSS. This gives you the best of all three columns.

Real-world example

On a recent multi-brand e-commerce project in Astro 5, we defined 38 design tokens as custom properties on :root. Each brand override was a single CSS file swapping those 38 values — colors, spacing scale, border radii, and font stacks. Theme switching happened via a data-brand attribute on <html>, with selectors like [data-brand='acme'] redefining the properties. Total added CSS: ~1.2 KB uncompressed per brand. Dark mode was an additional layer using prefers-color-scheme and a manual toggle that called setProperty() in 4 lines of JS. Lighthouse performance scores stayed above 95 because there's zero layout shift — the browser repaints in a single frame when properties change.

Frequently asked questions about CSS Custom Properties (Variables)

Are CSS Custom Properties the same as design tokens?
Not exactly. Design tokens are a platform-agnostic concept — named decisions like 'primary color' or 'spacing-md' that can be expressed in JSON, YAML, or any format. CSS Custom Properties are one *output format* for design tokens. Tools like Style Dictionary or Tokens Studio take your token definitions and generate CSS custom properties (among other formats like iOS Swift constants or Android XML resources). So custom properties are often how design tokens land in the browser, but the token layer is broader.
When did CSS Custom Properties become standard?
The CSS Custom Properties for Cascading Variables Module Level 1 reached W3C Candidate Recommendation status in 2015. Firefox shipped support in version 31 (2014, behind a flag; unflagged in 49, 2016). Chrome followed in version 49 (March 2016), Safari in 9.1 (March 2016), and Edge in 15 (April 2017). By mid-2017 all major browsers supported them. The spec has been stable since, with no breaking changes. As of April 2026, caniuse reports over 97% global coverage.
Can I use CSS Custom Properties with Tailwind CSS?
Yes, and in Tailwind CSS v4 (released early 2025) it's the default approach. Tailwind v4 uses a `@theme` directive that maps your design tokens directly to CSS custom properties. You write `@theme { --color-primary: #2563eb; }` and Tailwind auto-generates utility classes like `bg-primary` and `text-primary`. In Tailwind v3, you could reference custom properties in your config via `var(--color-primary)` inside `theme.extend`, but v4 makes custom properties first-class citizens. Our preferred setup is Tailwind v4 with tokens exported as custom properties.
Do CSS Custom Properties hurt performance?
In practice, no. Modern browser engines (Blink, WebKit, Gecko) resolve custom properties during the cascade with negligible overhead. The one thing to watch: deeply nested custom property references (a variable that references a variable that references a variable, several layers deep) can theoretically slow style recalculation, but we've never measured a meaningful impact in production. Changing a custom property on `:root` via JS triggers a repaint of all elements using it, which is fast — typically under 1ms on modern hardware for a few hundred elements. It's far cheaper than swapping a stylesheet or toggling dozens of classes.
Get in touch

Let's build
something together.

Whether it's a migration, a new build, or an SEO challenge — the Social Animal team would love to hear from you.

Get in touch →