CSS Advanced
CSS Variables
CSS variables, formally called custom properties, let you store values once and reuse them everywhere. They cascade, inherit and can be changed at runtime, making them the foundation of modern theming. This guide covers declaring, reading, scoping and theming with live examples.
What Are CSS Variables?
A CSS custom property is a name that starts with two dashes and holds a value you can reuse. Unlike a preprocessor variable, it lives in the browser at runtime, respects the cascade, inherits to descendants, and can be updated with JavaScript or media queries.
:root {
--brand: #4f46e5;
--space: 16px;
}
.button {
background: var(--brand);
padding: var(--space);
}Custom property names are case-sensitive: --Brand and --brand are two different variables.
Declaring and Reading
You declare a variable like any property, and read it with the var() function. Declaring on the :root pseudo-class makes it available to the whole document because :root is the highest element and variables inherit.
<!DOCTYPE html>
<html>
<head>
<style>
:root { --accent: #0ea5e9; }
.card {
border-top: 4px solid var(--accent);
padding: 16px;
font: 15px sans-serif;
box-shadow: 0 2px 8px rgba(0,0,0,.1);
}
.card h3 { color: var(--accent); margin: 0 0 6px; }
.card button { background: var(--accent); color: #fff; border: none; padding: 8px 14px; border-radius: 6px; }
</style>
</head>
<body>
<div class="card">
<h3>Reusable colour</h3>
<p>Change --accent once and every part updates.</p>
<button>Action</button>
</div>
</body>
</html>Fallback Values
var() accepts a second argument used when the variable is not defined. This guards against missing variables.
.box {
/* If --gap is undefined, use 12px */
gap: var(--gap, 12px);
}Scoping and Overriding
Variables cascade like any property, so a variable set on an ancestor applies to its descendants, and any element can override it locally. This makes component theming trivial.
<!DOCTYPE html>
<html>
<head>
<style>
.badge {
--bg: #64748b;
background: var(--bg);
color: #fff;
padding: 6px 12px;
border-radius: 999px;
font: 13px sans-serif;
}
.badge.success { --bg: #16a34a; }
.badge.danger { --bg: #dc2626; }
</style>
</head>
<body>
<span class="badge">Default</span>
<span class="badge success">Success</span>
<span class="badge danger">Danger</span>
</body>
</html>| Feature | CSS variables | Preprocessor (Sass) variables |
|---|---|---|
| Available at runtime | Yes | No (compiled away) |
| Inherit and cascade | Yes | No |
| Changeable with JS | Yes | No |
| Respond to media queries | Yes | No |
Theming With a Single Override
Because variables inherit, switching a theme is as simple as redefining the variables on a wrapper element or under a media query.
<!DOCTYPE html>
<html>
<head>
<style>
.panel {
--bg: #ffffff; --fg: #0f172a;
background: var(--bg); color: var(--fg);
padding: 20px; font: 15px sans-serif; border-radius: 10px;
}
.panel.dark { --bg: #0f172a; --fg: #e2e8f0; }
</style>
</head>
<body>
<div class="panel">Light panel using tokens.</div>
<br>
<div class="panel dark">Dark panel — same CSS, new variable values.</div>
</body>
</html>Read and set variables from JavaScript with element.style.setProperty('--brand', '#f00') and getComputedStyle(el).getPropertyValue('--brand') for dynamic themes.
A custom property holds a plain token of text. The browser only validates it where it is used, so var(--x) is invalid if --x contains a value the target property cannot accept.
Key points
- Custom properties start with -- and are read with var(--name).
- They inherit and cascade, unlike preprocessor variables.
- Declare global tokens on :root; override them locally on any element.
- var(--name, fallback) provides a default when the variable is missing.
- Redefining variables on a wrapper or media query switches themes instantly.
