Complete Guide to CSS Variables with Examples
Read on to explore complete guide to css variables with examples — a beginner-friendly walkthrough by Codekilla.
CSS Variables (officially called CSS Custom Properties) let you store values in one place and reuse them throughout your stylesheet. Think of them as containers that hold colors, sizes, spacing, or any CSS value you want to reference multiple times. Instead of typing #3498db fifty times across your CSS, you define it once as --primary-color: #3498db; and call it with var(--primary-color) wherever you need it. They're scoped to elements, which means you can create global variables on :root or local ones that only apply to specific components.
Unlike preprocessor variables from Sass or Less that compile away, CSS Variables are live in the browser. You can change them with JavaScript, respond to media queries, and even inherit them through the DOM tree. This makes them incredibly powerful for theming, responsive design, and dynamic UI updates.
- Maintainability: Change your primary brand color in one place, and it updates everywhere instantly — no find-and-replace gymnastics.
- JavaScript Integration: Toggle dark mode, adjust font sizes for accessibility, or create interactive color pickers by modifying variables in real-time.
- Scoping Power: Define global defaults on
:root, then override them for specific components without touching the original values. - Responsive Design: Swap values in media queries without duplicating entire rule blocks — perfect for fluid typography and adaptive spacing.
- Performance: Browsers handle them natively, no preprocessor compilation required, and they cascade naturally through the CSS inheritance model.
You declare CSS Variables using the -- prefix inside any selector. For global variables, use the :root pseudo-class (which targets the document root). To use a variable, wrap it in the var() function.
css:root { --brand-color: #e74c3c; --spacing-unit: 1rem; --font-main: 'Inter', sans-serif; } .button { background-color: var(--brand-color); padding: var(--spacing-unit); font-family: var(--font-main); } .card { margin-bottom: calc(var(--spacing-unit) * 2); }
The var() function accepts a second argument as a fallback: var(--unknown-color, #333) returns #333 if --unknown-color isn't defined. You can also nest variables and use them in calc() for mathematical operations.
CSS Variables respect the cascade and inheritance. Variables defined on :root are globally accessible. Variables defined on specific selectors only exist within that element and its children.
| Scope | Selector | Accessibility |
|---|---|---|
| Global | :root or html | Available everywhere in the document |
| Local | Any element (.card, #header) | Only within that element and descendants |
| Override | Child element | Inherits parent value unless redefined |
css:root { --text-color: #2c3e50; } .dark-section { --text-color: #ecf0f1; /* Override for this section */ } p { color: var(--text-color); /* Uses global or overridden value */ }
In this example, paragraphs inside .dark-section get light text, while paragraphs elsewhere use the global dark color. This scoping behavior is what makes CSS Variables superior to preprocessor variables for component-based design.
This is where CSS Variables shine. You can manipulate them with JavaScript to create themes, sliders, or user preferences without touching inline styles.
javascript// Get the root element const root = document.documentElement; // Read a variable const primaryColor = getComputedStyle(root).getPropertyValue('--brand-color'); // Set a variable root.style.setProperty('--brand-color', '#9b59b6'); // Dark mode toggle example const toggleDarkMode = () => { const isDark = root.style.getPropertyValue('--bg-color') === '#1a1a1a'; root.style.setProperty('--bg-color', isDark ? '#ffffff' : '#1a1a1a'); root.style.setProperty('--text-color', isDark ? '#333333' : '#f0f0f0'); };
This approach keeps your styles in CSS where they belong, while JavaScript handles only the logic. No more cluttered inline style="" attributes scattered across your markup.
You can redefine variables inside media queries to create adaptive layouts without duplicating property declarations.
css:root { --container-width: 90%; --font-size-base: 16px; --grid-gap: 1rem; } @media (min-width: 768px) { :root { --container-width: 720px; --font-size-base: 18px; --grid-gap: 1.5rem; } } @media (min-width: 1200px) { :root { --container-width: 1140px; --font-size-base: 20px; --grid-gap: 2rem; } } .container { width: var(--container-width); font-size: var(--font-size-base); } .grid { gap: var(--grid-gap); }
Notice how .container and .grid rules stay clean. You only adjust the variable values at breakpoints, not entire rule sets. This dramatically reduces code duplication.
CSS Variables make multi-theme systems trivial. Define theme palettes, then swap them by changing a single class on the <body> or :root.
css:root { /* Default light theme */ --bg-primary: #ffffff; --bg-secondary: #f8f9fa; --text-primary: #212529; --text-secondary: #6c757d; --accent: #007bff; } [data-theme="dark"] { --bg-primary: #1a1a1a; --bg-secondary: #2d2d2d; --text-primary: #e0e0e0; --text-secondary: #a0a0a0; --accent: #4dabf7; } body { background-color: var(--bg-primary); color: var(--text-primary); } .sidebar { background-color: var(--bg-secondary); } .link { color: var(--accent); }
Toggle themes with JavaScript: document.documentElement.setAttribute('data-theme', 'dark'). Every element using those variables updates instantly.
| Need | Reach For |
|---|---|
| Global values used everywhere | :root { --var-name: value; } |
| Component-specific overrides | .component { --var-name: value; } |
| Fallback if variable undefined | var(--var-name, fallback-value) |
| Math with variables | calc(var(--spacing) * 2) |
| Read variable in JS | getComputedStyle(el).getPropertyValue('--var') |
| Set variable in JS | element.style.setProperty('--var', 'value') |
| Responsive variable changes | Redefine inside @media queries |
| Theme switching | Change data-theme attribute, define theme palettes |
- Using variables without the
var()function: Writingcolor: --brand-color;doesn't work. You must usecolor: var(--brand-color);. - Forgetting the
--prefix: Variable names must start with two dashes.--primary-colorworks,primary-colordoesn't. - Expecting them to work like preprocessor variables: CSS Variables are case-sensitive and don't support operations outside
calc(). You can't do--spacing * 2directly. - Not providing fallbacks for critical values: If a variable is undefined and you don't provide a fallback, the property becomes invalid and won't render.
- Over-nesting variable definitions: Defining too many local scopes can make debugging difficult. Keep global defaults on
:rootand override sparingly. - Assuming full browser support for ancient versions: CSS Variables work in all modern browsers but fail in IE11. Use feature detection or fallbacks if supporting legacy environments.
💡 Think Like a Programmer: Treat CSS Variables like you'd treat constants in JavaScript — define them once, use them everywhere, and let the cascade do the heavy lifting. Your future self will thank you when refactoring themes or adjusting spacing systems.
Keep Reading
CSS Selectors Explained — Complete Guide
Read on to explore css selectors explained — complete guide — a beginner-friendly walkthrough by Codekilla.
CSS :has() Pseudo-Class Guide
Read on to explore css :has() pseudo-class guide — a beginner-friendly walkthrough by Codekilla.
All CSS Properties Cheat Sheet with Examples
Read on to explore all css properties cheat sheet with examples — a beginner-friendly walkthrough by Codekilla.
