---
title: "CSS Custom Properties Explained: How CSS Variables Actually Work"
url: https://theplusaddons.com/blog/css-custom-properties/
date: 2026-09-22
modified: 2026-09-22
lang: en
author: "Sagar Patel"
description: "[nexter-builder id=\"118291\"] If you have ever opened a stylesheet and seen something like --primary-color: #3556e9; sitting at the top, followed by color: var(--primary-color); further down, you have already met CSS..."
image: https://theplusaddons.com/wp-content/uploads/2026/09/css-custom-properties-featured-1024x538.jpg
word_count: 1793
---

# CSS Custom Properties Explained: How CSS Variables Actually Work

## 

- CSS custom properties use a two-dash name like --primary-color and are reused with var(), as shown by color: var(--primary-color); and padding: var(--spacing-md);.
- Sass and Less variables are compiled away before the browser sees them, while CSS custom properties stay live in the browser and can be changed with JavaScript at runtime.
- The :root selector makes a custom property available to every element on the page, so changing one value at the top updates every rule that references it.
- A fallback value in var(--card-radius, 8px) protects against typos or missing variables, which matters for load-bearing styles.
- Elementor's Global Colors and Global Fonts panels use the same shared-value idea, and The Plus Addons' Advanced Typography widget inherits those values automatically.

Table Of Contents

If you have ever opened a stylesheet and seen something like `--primary-color: #3556e9;` sitting at the top, followed by `color: var(--primary-color);` further down, you have already met CSS custom properties. Most people call them CSS variables, and once you understand what they actually do, changing an entire site's color scheme, spacing scale, or dark mode toggle stops being a find-and-replace job across a dozen files.

This guide covers what CSS custom properties are, how they differ from Sass or Less variables, how to declare and use them with real code, and where they show up inside a page builder like Elementor even if you never write a line of custom CSS yourself.

## What Are CSS Custom Properties?

A CSS custom property is a value you define once, give a name prefixed with two dashes, and then reuse anywhere in your stylesheet with the `var()` function. The official name is "custom property" because that is exactly what it is, a property you invent yourself rather than one built into the CSS specification like `color` or `margin`. "CSS variable" is the informal name most developers actually use.

![MDN Web Docs reference page for CSS custom properties showing syntax and browser support](https://theplusaddons.com/wp-content/uploads/2026/09/BmMiJMQ-Ov2lMM52orcIVFvmHMbVLhpYtvzdSiySbVpTQbpY6gXVn17HBuyn4dm9g1lVEnewHSYP7-SPH2ZkjA.png)MDN's reference confirms custom properties are Baseline "Widely available," meaning every major browser supports them today.

A basic declaration looks like this:

`:root {
--primary-color: #3556e9;
--spacing-md: 16px;
}

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

Declaring the properties on `:root` makes them available to every element on the page, because `:root` sits at the very top of the document. Change the value once at the top and every rule that references `var(--primary-color)` updates automatically, with no find-and-replace across the stylesheet.

***Also Read:** [Elementor Flexbox vs Grid Container: What Actually Changed](https://theplusaddons.com/blog/elementor-flexbox-vs-grid-container-differences/)*

## CSS Custom Properties vs. Sass or Less Variables

If you have used Sass or Less, the syntax will feel familiar, but the two are not the same thing under the hood, and the difference matters more than it looks.

- **Sass and Less variables are compiled away.** A build step swaps every `$primary-color` or `@primary-color` for its literal value before the browser ever sees the file. Once compiled, the variable is gone.

- **CSS custom properties live in the browser at runtime.** The browser keeps `--primary-color` as an actual, inspectable value in the DOM. You can read it, change it with JavaScript, or override it per element, all without recompiling anything.

- **Custom properties cascade and inherit like normal CSS.** You can redefine `--primary-color` inside a single component or media query, and only that scope picks up the new value. A compiled Sass variable has no concept of scope once it becomes a static value.

- **Custom properties respond instantly to JavaScript.** A single line like `document.documentElement.style.setProperty('--primary-color', '#ff5a6e')` repaints every element using that variable immediately, which is exactly how most dark mode and theme switchers work today.

In short, a preprocessor variable is a shortcut for the person writing the code. A CSS custom property is a live value the browser itself understands, which is what makes it useful for anything that needs to change after the page has already loaded.

## How to Declare and Use CSS Custom Properties

Here is the basic process for setting up and using your own custom properties.

- Pick a scope. Use `:root` for values the whole site should share, or a class like `.card` for values only that component should use.

- Name the property with two leading dashes, for example `--card-radius`, and give it a value, for example `--card-radius: 12px;`.

- Reference it anywhere with `var(--card-radius)`, for example `border-radius: var(--card-radius);`.

- Add a fallback for safety with a second argument, for example `var(--card-radius, 8px)`, which applies 8px if the variable is ever missing or misspelled.

- Override it locally when needed, by redeclaring the same property inside a more specific selector or a media query, and only that scope picks up the new value.

A fallback value is worth using on anything load-bearing. If a typo slips into a variable name somewhere in a large stylesheet, the browser silently falls back to the default you provided instead of rendering with no value at all.

## Real-World Use Cases

A few patterns account for most of the reason CSS custom properties caught on so fast, and most production websites you use every day already lean on at least one of them, whether or not the team behind them calls it out explicitly.

- **Dark mode toggles:** define light and dark values for the same variable names under a `[data-theme="dark"]` selector, then flip one attribute on the page to swap the entire palette.

- **Design tokens:** centralize colors, spacing, radii, and font sizes in one place so a brand refresh means editing a handful of values instead of hunting through every component.

- **Component theming:** let a single button or card component accept different colors per instance by overriding its custom properties inline, without writing a new CSS class for every variation.

- **Responsive spacing:** redefine a spacing variable inside a media query so every component using it automatically tightens up on smaller screens.

***Also Read:** [How to Use Elementor Flexbox Containers](https://theplusaddons.com/blog/how-to-use-elementor-flexbox-containers/)*

## CSS Custom Properties in Elementor: Global Colors and Beyond

You do not have to write `var()` by hand to benefit from this. Elementor's own Global Colors and Global Fonts panel is built on the same underlying idea: define a color or font once, apply it across dozens of widgets, and update every instance by editing the single global value. Under the hood, Elementor's newer container and style system stores many of these shared values as real CSS custom properties, which is part of why editing a global color updates the live preview instantly instead of requiring a full page reload.

![The Plus Addons Advanced Typography widget editor panel in Elementor with stroke, blend mode, and knockout text controls](https://theplusaddons.com/wp-content/uploads/2026/09/SAhgY-y8xtCzDQUGR8jUEDIVvNbQ6MhVWHsAgXf4zoDv7-KnJKpdcMv5InHsHMP3JuyDaAelbSV0AYWDxVGCeA.png)The Plus Addons' Advanced Typography widget layers stroke, blend mode, and knockout text effects on top of the same shared style values Elementor already manages globally.

If you are building on Elementor with The Plus Addons, the practical takeaway is this: set your palette and type scale once in Elementor's Global Colors and Global Fonts settings rather than picking a fresh color on every individual widget. Widgets like [Advanced Typography](https://theplusaddons.com/elementor-widget/advanced-typography/) then inherit those shared values automatically, so a brand color update in one place ripples through the whole site the same way a hand-written `var(--primary-color)` would in custom code.

## A Worked Example: Building a Dark Mode Toggle

Seeing the full pattern in one place makes it easier to apply than reading the pieces separately. Here is a minimal dark mode toggle built entirely on custom properties, no framework required.

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

[data-theme="dark"] {
--bg-color: #121212;
--text-color: #f2f2f2;
}

body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.2s ease, color 0.2s ease;
}`

With that CSS in place, a single line of JavaScript on a button click is enough to flip the entire site's palette:

`document.documentElement.dataset.theme =
document.documentElement.dataset.theme === "dark" ? "light" : "dark";`

Every element that reads `var(--bg-color)` or `var(--text-color)` repaints immediately, because the browser recalculates the variable's value the moment the `data-theme` attribute changes on the root element. Nothing needs to be recompiled and no page reload is involved. This is the exact mechanism most WordPress theme dark mode switchers, including third-party dark mode plugins, rely on today.

## Common Mistakes to Avoid

Most of the confusion around custom properties comes down to a handful of repeat mistakes.

- **Forgetting the two dashes.** `primary-color: blue;` is not a custom property, it is simply invalid CSS the browser silently ignores. It has to start with `--`, as in `--primary-color: blue;`.

- **Using a custom property where a value is expected without `var()`.** `color: --primary-color;` does nothing. You always need to wrap the reference in `var()`, as in `color: var(--primary-color);`.

- **Declaring the variable in a scope that never contains the element using it.** A custom property defined inside `.sidebar` is not visible to an element outside the sidebar in the markup. Move the declaration up to a shared ancestor, often `:root`, if multiple unrelated sections need it.

- **Assuming old Internet Explorer support.** Custom properties never worked in Internet Explorer 11 and are not going to. If a project still has to support IE11 specifically, this feature is not an option there regardless of fallbacks.

- **Reaching for JavaScript before checking if pure CSS solves it.** A hover state, a focus ring, or a simple light and dark palette swap can usually be handled with `:hover`, `:focus`, and a data attribute selector alone. Save the JavaScript for genuinely interactive controls, like a visible toggle switch a visitor clicks.

## Browser Support and Fallbacks

CSS custom properties are Baseline "Widely available," meaning every major browser, Chrome, Edge, Firefox, and Safari, has supported them for years at this point. There is no meaningful compatibility risk left for a typical WordPress site in 2026. The one habit worth keeping regardless is the fallback argument covered earlier, `var(--name, fallback-value)`, since it protects against a genuine typo rather than an old browser, and typos are far more common than an outdated browser at this point. If your analytics still show meaningful traffic on Internet Explorer 11, that is the one real exception where custom properties will not work at all, and a preprocessor or a build step remains the safer choice for that specific audience segment.

### Suggested Reading

- [Elementor Flexbox vs Grid Container: What Actually Changed](https://theplusaddons.com/blog/elementor-flexbox-vs-grid-container-differences/)

- [How to Use Elementor Flexbox Containers](https://theplusaddons.com/blog/how-to-use-elementor-flexbox-containers/)

- [Advanced Typography for Elementor](https://theplusaddons.com/elementor-widget/advanced-typography/)

## Frequently Asked Questions

### Are CSS custom properties the same as CSS variables?

Yes. "CSS custom property" is the formal name in the specification, and "CSS variable" is the informal name most developers use in conversation. They refer to the same feature.

### Do I need Sass if I already use CSS custom properties?

Not for variables specifically. CSS custom properties cover the variable use case natively in the browser. Sass still offers extras custom properties do not, like nesting shortcuts, mixins, and loops, so many teams use both together rather than choosing one over the other.

### Can I change a CSS custom property with JavaScript?

Yes, and this is one of their biggest advantages over preprocessor variables. A single call to `element.style.setProperty('--name', 'value')` updates the value live, which is the mechanism behind most dark mode toggles and interactive theme switchers.

### What happens if I reference a custom property that was never defined?

Without a fallback, the property using `var()` is treated as if its value were invalid, which for most properties means it falls back to its initial or inherited value. With a fallback, for example `var(--name, 16px)`, the browser uses the fallback instead, which is why adding one is good practice for anything visually important.

### Do I need to write custom CSS to use this in Elementor?

No. Elementor's Global Colors and Global Fonts panels apply the same underlying idea through the visual editor, so you get the "define once, reuse everywhere" benefit without writing a single line of CSS by hand.