CSS Variables (Custom Properties)

Store colours and sizes once, reuse everywhere, and switch themes.

Lesson 33 of 49Intermediate11 min read

Imagine your brand colour appears in 40 places in your CSS, and you're asked to make it slightly greener. With CSS variables, officially called custom properties, you change the value in one place and the whole site updates.

Variables also make CSS easier to read. var(--color-danger) tells you what a colour is for, while #d33f49 doesn't. And because variables can change depending on where they're used, they're the technique behind dark mode and colour themes on most modern websites, including this one.

How to define and use a CSS variable

CSS
:root {
  --brand: #17a865;
  --radius: 12px;
}

.button {
  background: var(--brand);
  border-radius: var(--radius);
}
  • Variable names start with two hyphens, as in --brand.
  • :root refers to the <html> element, so variables defined there are available everywhere on the page.
  • Read a variable's value with var(--name).

Variable names are case-sensitive: --Brand and --brand are two different variables.

Fallback values

var(--accent, orange) uses orange if --accent hasn't been defined. Fallbacks are useful in reusable components, where you can't be sure the page using them has set every variable.

How variables cascade and inherit

Variables follow the normal CSS rules. You can redefine a variable on any element, and its children see the new value. That makes themes and component variations straightforward:

CSS
.card { --bg: white; background: var(--bg); }
.card.dark { --bg: #15161b; }

The same idea powers dark mode: define light colours on :root, then redefine the same variables inside a prefers-color-scheme: dark media query. Every rule that uses the variables switches automatically.

Changing variables with JavaScript

JavaScript
document.documentElement.style.setProperty('--brand', 'hotpink');

One line of JavaScript restyles everything that uses --brand. This is how colour pickers, theme switchers and "large text" buttons are often built.

Variables in calculations

Variables work inside other functions, which makes them even more powerful. You can store a base unit and multiply it, or store numbers and add units later:

CSS
:root { --space: 8px; --cols: 3; }
.card { padding: calc(var(--space) * 2); }
.grid { grid-template-columns: repeat(var(--cols), 1fr); }

This is how spacing scales and grids in design systems are built: change --space once and every gap, margin and padding that uses it changes in proportion. You can also combine variables with clamp() and color-mix() to build fluid sizes and colour tints from a few base values.

CSS Variables Syntax

Custom properties

:root { --main-color: #4467ff; }
.title { color: var(--main-color); }
.title { color: var(--accent, tomato); } /* with fallback */

CSS Variables Summary

  • CSS variables store a value once so you can reuse it everywhere.
  • Define them with --name: value and read them with var(--name).
  • Variables on :root are available on the whole page.
  • Redefining a variable on an element changes it for that element's children, which makes themes easy.
  • JavaScript can update a variable and restyle the page instantly.

CSS Variables Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

A themed button set

Change --brand on line 2 and run. Every button updates.

:root {
  --brand: #17a865;
  --ink: #15161b;
  --radius: 12px;
  --pad: 10px 18px;
}

.btn {
  padding: var(--pad);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  background: var(--brand);
  color: white;
  font: 700 15px sans-serif;
}

.btn.outline { background: transparent; color: var(--ink); }
.btn.big { --pad: 16px 28px; --radius: 999px; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 1 to 6Define the design decisions once, on :root.
  2. Lines 9 to 12Use them with var().
  3. Line 18Override variables just for large buttons. Padding and radius change, and nothing else needs rewriting.
Example 2

Light and dark themes

Press the button. JavaScript only switches a class; the variables do the rest.

.app {
  --bg: #fbf7f0;
  --text: #15161b;
  --card: white;
  background: var(--bg);
  color: var(--text);
  padding: 20px;
  border-radius: 16px;
  font-family: sans-serif;
  transition: background 0.3s, color 0.3s;
}

.app.dark {
  --bg: #0e1014;
  --text: #f2efe8;
}

button {
  background: var(--text);
  color: var(--bg);
  border: 0;
  padding: 8px 14px;
  border-radius: 8px;
}
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 2 to 4The light theme values.
  2. Lines 5 to 6Everything reads colours from variables.
  3. Lines 13 to 16The dark class only redefines the variables.
  4. Lines 19 to 20Even the button inverts automatically.

Common CSS Variables Mistakes

Forgetting the dashes or var()

Don't
:root { brand: blue; }
h1 { color: --brand; }
Do
:root { --brand: blue; }
h1 { color: var(--brand); }

Using variables in media query conditions

Don't
:root { --tablet: 768px; }
@media (min-width: var(--tablet)) { … }
Do
@media (min-width: 768px) { … }

Variables work in property values, not in media query conditions or selectors.

CSS Variables in Real Projects

Design tokens

Large companies call these "design tokens": a single list of colours, spacing, radii and shadows shared across websites and mobile apps. This website runs on CSS variables. Look for --brand, --ink and --surface in DevTools, and watch them change when you switch to dark mode.

CSS Variables Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Define a variable and use it.

:root {
  : #f0592b;
}
.link {
  color: ;
}
Multiple choiceeasy

Where should you put global variables?

Multiple choicemedium

What does var(--size, 16px) do if --size doesn't exist?

Fix the bugmedium

The card variables aren't working. Fix the definition and usage so the card has a #e1e6ff background and 20px padding.

:root {
  card-bg: #e1e6ff;
  --card-pad = 20px;
}

.card {
  background: var(card-bg);
  padding: --card-pad;
}
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Background comes through
  • Padding is 20px
  • Uses variables

CSS Variables Quiz

3 questions to check what you learned.

  1. CSS custom property names must start with…

    • $
    • @
    • #
    • --
  2. Can you change a CSS variable's value inside a specific class?

    • No, variables are global constants
    • Only with JavaScript
    • Only in :root
    • Yes, and its children see the new value
  3. Which JavaScript changes a root variable?

    • document.documentElement.style.setProperty('--brand', 'red')
    • document.css('--brand', 'red')
    • root.brand = 'red'
    • var('--brand') = 'red'

CSS Variables Challenge

Build a tiny design system

  1. On :root, define --primary: #7c3aed, --space: 12px and --round: 10px
  2. .tag uses var(--primary) for its background, var(--space) for horizontal padding and var(--round) for its radius
  3. .tag.warn overrides only --primary to #f0592b
.tag { display: inline-block; color: white; padding-top: 4px; padding-bottom: 4px; font: 600 14px sans-serif; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Root variables defined
  • Tag is purple
  • Tag padding uses --space
  • Tag radius is 10px
  • Warn tag is orange through the variable