Using CSS Variables

CSS Custom Properties — commonly known as CSS variables — let you define reusable values directly in your stylesheets and reference them anywhere with var(). They cut down on repetition, make theming and dark-mode toggles trivial, and can even be updated at runtime with JavaScript.

The Syntax

A custom property name always starts with two dashes (--), and its value can be almost anything: a color, a length, a font stack, even a list of values. You declare it inside a selector, and it becomes available to that selector and all of its descendants.

:root {
  --primary-color: #0090f0;
}

body {
  background-color: var(--primary-color);
}

Here, --primary-color is declared on :root (a pseudo-class that matches the <html> element), which makes it available globally across the entire page. body then reads that value with var(--primary-color).

Global vs. Scoped Custom Properties

The real power of custom properties comes from CSS’s normal cascade and inheritance rules. A property declared on :root is global, but you can override it on any selector to scope a different value to that element and its children.

:root {
  --primary-color: #0090f0;
}

.dark-section {
  --primary-color: #1a1a2e;
}

.card {
  background-color: var(--primary-color);
  color: white;
  padding: 1rem;
}

Any .card on the page uses the global blue, but a .card nested inside .dark-section automatically picks up the darker override — no extra selectors or specificity battles required.

Fallback Values

var() accepts an optional second argument: a fallback value used if the custom property hasn’t been defined (or was set to an invalid value).

.button {
  background-color: var(--primary-color, #0090f0);
}

If --primary-color is missing entirely, the button falls back to #0090f0 instead of rendering with no background color at all. This is especially useful for component styles you intend to reuse across projects where the variable may or may not be set by the consuming page.

Practical Use Cases

Theming and Dark Mode

Because custom properties cascade, switching themes is often just a matter of swapping which values are in scope — for example, by toggling a class on <body>.

:root {
  --bg-color: #ffffff;
  --text-color: #111111;
}

body.dark-mode {
  --bg-color: #111111;
  --text-color: #f5f5f5;
}

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

Reducing Repetition

Instead of copy-pasting the same spacing, color, or font value across dozens of rules, define it once and reference it everywhere. Update the single declaration and every usage updates with it.

Runtime Updates with JavaScript

Unlike Sass or Less variables, CSS custom properties are live in the browser — you can read and update them with JavaScript, and the page re-renders immediately.

// Set a custom property at runtime
document.documentElement.style.setProperty('--primary-color', '#ff4081');

// Read the current value
const current = getComputedStyle(document.documentElement)
  .getPropertyValue('--primary-color');

This is exactly how many dark-mode toggles and theme pickers work under the hood: a button click runs a small script that updates one or two custom properties, and the rest of the page’s styling follows automatically.

FAQs

Are CSS variables the same as Sass/Less variables?

No. Preprocessor variables are resolved at build time and produce static CSS. CSS custom properties are resolved by the browser at runtime, respect the cascade, and can be read or changed with JavaScript after the page has loaded.

Do I need a prefix like var-* to declare them?

No. The shipped specification uses a double-dash prefix for declarations, e.g. --primary-color: #0090f0;, and the var() function for usage, e.g. var(--primary-color). An earlier, abandoned draft proposal used a var-* prefix and a bare var(name) syntax, but no browser ever shipped that version.

Where should I declare global custom properties?

Declare them on :root so they’re available everywhere in the document. Override them on more specific selectors (a class, a component wrapper, a media query) when you need a different value for a particular section of the page.

Can I use CSS variables in media queries?

Custom properties can’t be used directly inside a media query’s condition (e.g. @media (min-width: var(--breakpoint)) doesn’t work), but you can still redefine custom property values inside a media query block and have the rest of your CSS pick up the new values automatically.

Conclusion

CSS custom properties bring real, cascade-aware variables to plain CSS — no build step required. Declare them on :root for global values, override them on specific selectors for scoped theming, supply fallbacks with var(--name, fallback) for safety, and reach for JavaScript’s setProperty() when you need to update styles at runtime.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.