In short
CSS variables (custom properties) let you store a value like a color once and reuse it everywhere with var(). Change it in one place and your whole site updates. What are CSS variables? A CSS variable is a named box that holds a value.
CSS variables (custom properties) let you store a value like a color once and reuse it everywhere with var(). Change it in one place and your whole site updates.
What are CSS variables?
A CSS variable is a named box that holds a value. You give the box a name, put a value inside, and then use that name wherever you need the value. The official name is custom property, but almost everyone says "CSS variable".
Think of it like a contact in your phone. You do not type your friend's number every time. You save it once as "Sam" and tap "Sam". If Sam gets a new number, you update one contact, not every message.
A variable name always starts with two dashes: --brand-color. You read the value with the var() function.
:root {
--brand-color: #4f46e5;
--radius: 8px;
}
.button {
background: var(--brand-color);
border-radius: var(--radius);
color: white;
padding: 0.75rem 1.25rem;
border: none;
}
.card {
border: 2px solid var(--brand-color);
border-radius: var(--radius);
padding: 1rem;
}
Here the button and the card both use the same purple and the same rounded corners. If you change --brand-color to green, both turn green at once.
The :root selector matches the <html> element, the top of the page. Variables defined there are available to every element, so :root is the usual place for global variables.
Note: Variable names are case-sensitive.
--Brandand--brandare two different variables.
How do you use var() with a fallback value?
The var() function can take a second value, called a fallback. The browser uses it when the variable is not defined.
.alert {
background: var(--alert-bg, #fef3c7);
color: var(--alert-text, #92400e);
padding: 1rem;
}
If nobody has set --alert-bg, the alert still gets a soft yellow background. This is useful for components that you share between projects, where you cannot be sure every variable exists.
Common mistake: Writing
var(brand-color)orvar(-brand-color). You always need both dashes insidevar():var(--brand-color).
Scoping and overriding variables in components
Variables follow the normal cascade and inheritance rules you already know. A variable set on an element is available to that element and all its children. A closer definition wins over one further up the tree.
This means you can give a component its own "settings" and change them for one version only. Here is a button with a small variant system:
<button class="btn">Save</button>
<button class="btn btn--danger">Delete</button>
<button class="btn btn--ghost">Cancel</button>
.btn {
--btn-bg: #4f46e5;
--btn-text: #ffffff;
background: var(--btn-bg);
color: var(--btn-text);
border: 2px solid var(--btn-bg);
padding: 0.6rem 1.2rem;
border-radius: 6px;
font: inherit;
}
.btn--danger {
--btn-bg: #dc2626;
}
.btn--ghost {
--btn-bg: transparent;
--btn-text: #4f46e5;
border-color: #4f46e5;
}
The .btn rule does all the real styling. The variant classes only change variables. You will see a purple button, a red button and an outlined button, but the main rule is written only once.
You can even change variables inside a media query. Set --gap: 1rem on :root, then set --gap: 2rem inside @media (min-width: 800px), and every element that uses var(--gap) grows on wide screens.
Design tokens: building a small design system
Design tokens are the basic design decisions of a project, saved as named values: colors, font sizes, spacing steps, shadows and corner radius. Using variables for tokens keeps a site consistent, because everyone picks from the same short list.
:root {
/* Colors */
--color-primary: #4f46e5;
--color-text: #1f2937;
--color-muted: #6b7280;
--color-surface: #ffffff;
/* Spacing scale */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 1rem;
--space-4: 2rem;
/* Type and shape */
--font-body: system-ui, sans-serif;
--radius-md: 8px;
--shadow-sm: 0 1px 3px rgb(0 0 0 / 0.12);
}
body {
font-family: var(--font-body);
color: var(--color-text);
background: var(--color-surface);
}
.profile-card {
padding: var(--space-4);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
}
Now nobody writes random values like 13px or #333. They choose a token. This makes big changes, like a rebrand or dark mode, much easier.
Tip: Give tokens names that describe their job, like
--color-text, not their look, like--dark-gray. The job stays the same even if the color changes.
You can read more on MDN: Using CSS custom properties.
Practice exercise
Create a .tag component (small rounded labels like "New" or "Sale"). Give it two variables, --tag-bg and --tag-text, with default values. Then add .tag--success and .tag--warning classes that only change the variables.
Hint: Put the defaults inside .tag itself, like the .btn example, and use var() for background and color.
Key takeaways
- A CSS variable starts with
--and is read withvar(--name). var(--name, fallback)uses the fallback if the variable is missing.- Define global variables on
:root; override them on any element or inside media queries. - Variants can change only variables, so the main component rule stays simple.
- Design tokens are your project's shared values, stored as variables.
Frequently asked questions
What is the difference between CSS variables and Sass variables?
Sass variables are replaced when the code is compiled, so the browser never sees them. CSS variables live in the browser. They follow the cascade and can change at runtime, for example inside a media query or for dark mode.
Can I use CSS variables inside media query conditions?
No. You cannot write @media (min-width: var(--bp)). You can change a variable's value inside a media query, but not use one in the condition itself.
Do CSS variables work in all browsers?
Yes. Every modern browser has supported custom properties for many years, so you can use them safely.