Skip to content
Laviyx Learn

Try

CSS course menu

CSS Custom Properties (CSS Variables): var(), Fallbacks, Scope and Theming

Learn CSS custom properties (variables): declaring --name values, using var() with fallbacks, global and local scope, inheritance, overriding variables in components and media queries, design tokens, changing them with JavaScript, and @property.

Advanced7 min readUpdated
On this page

Custom properties, usually called CSS variables, store a value once and reuse it everywhere. Change the variable and every place that uses it updates.

Declaring and using variables#

CSS
:root {
  --brand: #2f5fe0;
  --radius: 10px;
  --space: 12px;
}

.btn {
  padding: var(--space) calc(var(--space) * 2);
  background: var(--brand);
  color: white;
  border: none;
  border-radius: var(--radius);
}
.card {
  margin-top: var(--space);
  padding: var(--space);
  border: 2px solid var(--brand);
  border-radius: var(--radius);
}
HTML
<button class="btn">Brand button</button>
<div class="card">A card using the same colour, radius and spacing.</div>
Result
  • A custom property name starts with two hyphens: --brand. Names are case-sensitive.
  • var(--brand) reads its value.
  • :root is the <html> element, so variables set there are available everywhere.

Fallback values#

var() takes an optional fallback, used when the variable is not set:

CSS
color: var(--text-color, #222);
padding: var(--card-padding, var(--space, 16px));   /* fallbacks can nest */

Scope and inheritance#

Custom properties are inherited and can be set on any element. A component can override a variable for itself and everything inside it:

CSS
:root { --accent: #2f5fe0; }

.badge { display: inline-block; padding: 4px 10px; border-radius: 999px; background: var(--accent); color: white; }

.danger { --accent: #c92a2a; }
.success { --accent: #2f9e44; }
HTML
<p><span class="badge">Default</span></p>
<div class="danger"><span class="badge">Inside .danger</span></div>
<div class="success"><span class="badge">Inside .success</span></div>
Result

The .badge rule never changes; only the variable it reads does. This is the basis of theming and component variants.

Variables in media queries#

You cannot use a variable inside a media query condition (@media (min-width: var(--bp)) does not work), but you can change variables within media queries:

CSS
:root { --gutter: 16px; }

@media (min-width: 800px) {
  :root { --gutter: 32px; }
}

.container { padding-inline: var(--gutter); }

Design tokens#

Many teams keep all their design decisions in variables, often called design tokens:

CSS
:root {
  /* Colours */
  --color-bg: #ffffff;
  --color-text: #1f2328;
  --color-primary: #2f5fe0;

  /* Spacing scale */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 2rem;

  /* Typography */
  --font-sans: system-ui, sans-serif;
  --text-sm: 0.875rem;
  --text-lg: 1.25rem;
}

Using tokens everywhere keeps a design consistent, and switching the values (for a dark theme or a rebrand) re-themes the whole site. Dark Mode builds on exactly this.

Changing variables with JavaScript#

HTML
<label>Hue <input type="range" min="0" max="360" value="220" id="hue"></label>
<div id="swatch" style="height: 50px; margin-top: 8px; border-radius: 8px; background: hsl(var(--hue, 220) 70% 50%)"></div>
JavaScript
const slider = document.getElementById("hue");
slider.addEventListener("input", () => {
  document.getElementById("swatch").style.setProperty("--hue", slider.value);
});
Result

element.style.setProperty("--name", value) sets a variable, and getComputedStyle(element).getPropertyValue("--name") reads one.

Things to know#

  • A variable holds raw text until it is used. --size: 20 and then width: var(--size)px does not work; write --size: 20px, or calc(var(--size) * 1px).
  • If a variable contains a value that is invalid where it is used, the property falls back to its inherited or initial value, not to an earlier declaration.
  • @property can register a variable with a type, initial value and inheritance, which also lets browsers animate it:
CSS
@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

Practice#

Practice exercise

Create a .alert component that uses --alert-color for its left border and its heading colour, with a default of blue, and .alert-warning and .alert-error variants that only change the variable.

Show solution
CSS
.alert {
  --alert-color: #2f5fe0;
  border-left: 5px solid var(--alert-color);
  background: #f8f9fa;
  padding: 8px 12px;
  margin-bottom: 8px;
}
.alert strong { color: var(--alert-color); }
.alert-warning { --alert-color: #e67700; }
.alert-error { --alert-color: #c92a2a; }
HTML
<div class="alert"><strong>Info:</strong> Classes start at 10 am.</div>
<div class="alert alert-warning"><strong>Warning:</strong> Only 3 seats left.</div>
<div class="alert alert-error"><strong>Error:</strong> Payment failed.</div>
Result

Key takeaway#

Finished this lesson?