Table of Contents
- What Are CSS Variables?
- Basic Syntax: Defining and Using Variables
- Dynamic Behavior: Updating Variables
- Fallback Values
- Scoping: Global vs. Local Variables
- Practical Use Cases
- Best Practices
- Troubleshooting Common Issues
- Conclusion
- Reference
What Are CSS Variables?
CSS Variables (or Custom Properties) are user-defined values that can be reused throughout a stylesheet. They follow a specific syntax and are subject to the CSS cascade, meaning they can be inherited, overridden, and updated dynamically.
Key Benefits:
- Reduced Repetition: Define values once and reuse them across selectors.
- Easier Maintenance: Update a single variable to change styles globally.
- Dynamic Styling: Modify values in real-time with media queries, JavaScript, or user interactions.
- Theming: Simplify switching between themes (e.g., light/dark mode).
Basic Syntax: Defining and Using Variables
Declaring Variables
CSS variables are declared using a double hyphen (--) prefix, followed by a name (e.g., --primary-color). They must be declared inside a CSS selector, which determines their scope (more on scoping later).
To make a variable globally accessible (usable across the entire stylesheet), declare it in the :root pseudo-class (which targets the root element of the document, typically <html>):
:root {
/* Global variables */
--primary-color: #3498db; /* Blue */
--secondary-color: #2ecc71; /* Green */
--spacing-sm: 8px;
--spacing-md: 16px;
--font-size-base: 16px;
}
Using Variables with var()
To use a variable, reference it with the var() function:
/* Using global variables */
.button {
background-color: var(--primary-color);
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-base);
}
.card {
border: 1px solid var(--secondary-color);
margin-bottom: var(--spacing-md);
}
Output:
The .button will have a blue background, 8px/16px padding, and 16px font size. The .card will have a green border and 16px bottom margin.
Dynamic Behavior: Updating Variables
Unlike preprocessor variables (e.g., Sass), CSS variables are live and can be updated after the page loads. This makes them ideal for dynamic styling.
With Media Queries
Adjust variables based on viewport size to create responsive designs:
:root {
--font-size-base: 16px;
}
/* On mobile (screen width < 768px) */
@media (max-width: 768px) {
:root {
--font-size-base: 14px; /* Smaller font size on mobile */
}
}
body {
font-size: var(--font-size-base); /* Uses updated value on mobile */
}
With Pseudo-Classes/Pseudo-Elements
Modify variables for interactive states like :hover, :focus, or :active:
:root {
--button-bg: #3498db;
--button-hover-bg: #2980b9;
}
.button {
background-color: var(--button-bg);
transition: background-color 0.3s ease;
}
.button:hover {
background-color: var(--button-hover-bg); /* Changes on hover */
}
With JavaScript
Use JavaScript to update variables dynamically based on user input, events, or application state. This is where CSS variables truly shine for interactivity.
Example: Updating a variable on button click
<button id="increase-spacing">Increase Spacing</button>
<div class="content">Dynamic spacing content</div>
:root {
--content-spacing: 16px;
}
.content {
padding: var(--content-spacing);
border: 1px solid #ddd;
}
const increaseButton = document.getElementById('increase-spacing');
increaseButton.addEventListener('click', () => {
// Get the root element
const root = document.documentElement;
// Get the current value of --content-spacing
const currentSpacing = getComputedStyle(root).getPropertyValue('--content-spacing');
// Parse the value (remove "px") and add 8px
const newSpacing = `${parseInt(currentSpacing) + 8}px`;
// Update the variable
root.style.setProperty('--content-spacing', newSpacing);
});
Clicking the button will increase the .content padding by 8px each time.
Fallback Values
The var() function accepts an optional second parameter: a fallback value to use if the variable is undefined or invalid.
Syntax:
var(--variable-name, fallback-value)
Example:
/* If --accent-color is undefined, use #ff9800 (orange) */
.alert {
color: var(--accent-color, #ff9800);
}
Fallbacks are useful for:
- Handling missing variables.
- Ensuring compatibility with older browsers (though most modern browsers support CSS variables).
Scoping: Global vs. Local Variables
Variables are scoped to the selector in which they are declared.
- Global Variables: Declared in
:root(or<html>) and accessible to all elements. - Local Variables: Declared in a specific selector and only accessible to that selector and its descendants.
Example: Local Variable Overriding Global
:root {
--text-color: #333; /* Global text color */
}
.card {
--text-color: #666; /* Local text color (overrides global in .card) */
}
/* Uses global --text-color (#333) */
body {
color: var(--text-color);
}
/* Uses local --text-color (#666) */
.card p {
color: var(--text-color);
}
Practical Use Cases
Theming (Light/Dark Mode)
CSS variables simplify theme switching by centralizing theme-specific values. Use a data attribute (e.g., data-theme) to toggle themes:
/* Default (light) theme */
:root {
--bg-color: #ffffff;
--text-color: #333333;
--primary-color: #3498db;
}
/* Dark theme */
:root[data-theme="dark"] {
--bg-color: #1a1a1a;
--text-color: #f5f5f5;
--primary-color: #9b59b6;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s ease, color 0.3s ease;
}
.button {
background-color: var(--primary-color);
}
Toggle the theme with JavaScript:
// Switch to dark mode
document.documentElement.setAttribute('data-theme', 'dark');
// Switch back to light mode
document.documentElement.setAttribute('data-theme', 'light');
Responsive Design
Use variables to centralize responsive values (e.g., breakpoints, padding, font sizes) for cleaner, more maintainable code:
:root {
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--padding-mobile: 16px;
--padding-desktop: 24px;
}
.container {
padding: var(--padding-mobile);
}
@media (min-width: var(--breakpoint-md)) {
.container {
padding: var(--padding-desktop);
}
}
Component-Level Customization
Let components define their own variables for granular control. For example, a reusable Card component with customizable colors:
/* Base card styles */
.card {
padding: var(--card-padding, 16px); /* Default padding if not set */
background: var(--card-bg, #fff);
border: 1px solid var(--card-border-color, #ddd);
border-radius: 4px;
}
/* Specialized card variant */
.card.highlight {
--card-bg: #f8f9fa;
--card-border-color: #3498db;
--card-padding: 20px;
}
Now you can customize individual cards without rewriting base styles!
Best Practices
- Use Descriptive Names: Name variables by their purpose, not their value (e.g.,
--primary-colorinstead of--blue). - Group Related Variables: Organize variables by type (e.g.,
--color-*,--spacing-*,--font-*)::root { /* Colors */ --color-primary: #3498db; --color-secondary: #2ecc71; /* Spacing */ --spacing-xs: 4px; --spacing-sm: 8px; /* Fonts */ --font-sans: 'Helvetica', sans-serif; } - Avoid Overuse: Don’t create variables for single-use values (e.g., a one-off margin). Reserve them for repeated or dynamic values.
- Document Variables: Add comments or a separate
variables.cssfile to explain variable purposes (especially in large projects). - Test for Fallbacks: Ensure fallbacks work for browsers that might not support variables (though this is rare today).
Troubleshooting Common Issues
1. Variable Not Applying? Check for Typos!
Variables are case-sensitive, and typos (e.g., --primay-color instead of --primary-color) are the most common issue.
2. Browser Compatibility
CSS variables are supported in all modern browsers (Chrome, Firefox, Safari, Edge), but not in Internet Explorer. Use tools like PostCSS with postcss-custom-properties if you need IE support (though this sacrifices dynamic updates).
Check current support on caniuse.com.
3. Specificity Issues
Variables inherit from their parent selectors. If a variable isn’t updating, ensure the selector overriding it has sufficient specificity:
/* This may not work if .card has higher specificity */
:root { --card-color: blue; }
.card { --card-color: red; } /* Overrides global for .card elements */
Conclusion
CSS variables revolutionize how we write and maintain styles by combining the power of reusability with dynamic flexibility. Whether you’re building a simple website or a complex web app, they reduce repetition, simplify theming, and enable interactive designs that respond to user input.
By mastering CSS variables, you’ll write cleaner, more maintainable code and unlock new possibilities for dynamic styling.