CSS · Architecture

CSS Variables

CSS variables let you name a value once — like your brand colour — and reuse it everywhere. Change it in one place and the whole site updates.

Intermediate 5 min Lesson 20 of 26 Runnable examples
Before you start:Colors and Backgrounds
0/26 in CSS
On this page · 5 min · Intermediate

Learning objectives#

  • Declare and use custom properties
  • Provide fallback values
  • Scope variables to components
  • Build light and dark themes
  • Read and change variables with JavaScript

Explanation#

Declare with two dashes: --brand: #2965f1;. Use with var(): color: var(--brand);.

Variables declared on :root are global. Declared on a selector, they apply only to that element and its children — this is called scoping.

var(--gap, 1rem) uses 1rem if --gap is not defined.

Theming: define colour variables on :root, then redefine them inside @media (prefers-color-scheme: dark) or a [data-theme="dark"] selector. Every rule using the variables switches automatically.

Unlike Sass variables, CSS variables are live in the browser: JavaScript can change them with element.style.setProperty("--brand", "red").

Syntax#

CSS Illustrative
:root { --brand: #2965f1; --radius: 8px; }
.btn  { background: var(--brand); border-radius: var(--radius); }
.card { padding: var(--space, 1rem); } /* fallback */

Code example: A themeable component#

HTMLA themeable component Runnable
<style>
  :root {
    --bg: #ffffff;
    --text: #0f172a;
    --brand: #2965f1;
    --radius: 10px;
    --space: 16px;
  }
  [data-theme="dark"] {
    --bg: #0f172a;
    --text: #e2e8f0;
    --brand: #60a5fa;
  }
  body { background: var(--bg); color: var(--text); font-family: sans-serif; padding: var(--space); transition: background 200ms; }
  .btn { background: var(--brand); color: var(--bg); border: 0; padding: 8px 16px; border-radius: var(--radius); }
  .btn.danger { --brand: #dc2626; }   /* scoped override */
</style>


<h2>Theme demo</h2>
<button class="btn" id="toggle">Toggle dark mode</button>
<button class="btn danger">Delete</button>
<p><label>Brand colour <input type="color" id="pick" value="#2965f1"></label></p>


<script>
  toggle.onclick = () => {
    const html = document.documentElement;
    html.dataset.theme = html.dataset.theme === 'dark' ? 'light' : 'dark';
  };
  pick.oninput = e => document.documentElement.style.setProperty('--brand', e.target.value);
</script>
Expected output
Clicking the toggle switches the whole page to dark colours; the Delete button stays red; the colour picker changes the brand colour live.
Opens this example in the full playground

Step-by-step explanation#

  1. All colours and sizes are declared once on :root.
  2. [data-theme="dark"] redefines only the colour variables.
  3. Every rule uses var(), so switching the theme updates everything at once.
  4. .btn.danger overrides --brand only for that button.
  5. The script toggles data-theme on <html>.
  6. setProperty changes --brand live as you pick a colour.

Real-world use case#

Design systems store every colour, spacing, radius and font size as variables (“design tokens”), making rebrands and dark mode a matter of changing a few lines.

Common mistakes#

Missing the double dash

brand: red is an invalid property. Custom properties must start with --.

Using var() in media query conditions

@media (min-width: var(--bp)) does not work. Variables only work in property values.

Variable names are case-sensitive

--Brand and --brand are two different variables.

Best practices#

  • Put design tokens on :root
  • Name by purpose (--color-danger) not by value (--red)
  • Use scoped variables to create component variants
  • Use variables for dark mode instead of duplicating rules

Remember this#

--name to declare, var(--name) to use.

Define once on :root, use everywhere.

Dark mode = redefine the colour variables, nothing else.

Practice exercise#

Your task: Create --primary and --spacing variables on :root and use them for a .card’s border colour and padding. Add a fallback for --spacing.

Need a hint?
var(--spacing, 1rem).
Show solution
CSS Illustrative
:root {
  --primary: #7c3aed;
  --spacing: 1.25rem;
}
.card {
  border: 2px solid var(--primary);
  padding: var(--spacing, 1rem);
}

Mini quiz#

CSS Variables quizMultiple choice Question 1 of 3

How do you use the variable --main-color?

Score so far: 0/0

Want more? Practise all CSS MCQs · CSS interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.