CSS Variables
Definition
CSS Variables (officially called CSS Custom Properties) are entities defined by developers that contain specific values to be reused throughout a stylesheet. They follow the syntax --variable-name: value;
when defined and are accessed using the var(--variable-name)
function. Unlike preprocessor variables (like those in Sass or Less), CSS Variables are part of the actual CSS specification, are resolved at runtime rather than compile time, cascade through the DOM, and can be manipulated with JavaScript.
Basic Syntax and Usage
CSS Variables follow specific syntactic rules:
Defining Variables
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--base-font-size: 16px;
--spacing-unit: 8px;
}
Using Variables
.button {
background-color: var(--primary-color);
padding: var(--spacing-unit) calc(var(--spacing-unit) * 2);
font-size: var(--base-font-size);
}
Fallback Values
.element {
color: var(--undefined-color, #333);
}
Local Scope Variables
.component {
--component-padding: 20px;
padding: var(--component-padding);
}
Key Characteristics of CSS Variables
CSS Variables have several important properties that distinguish them from preprocessor variables:
- Runtime Evaluation: Computed at runtime, allowing dynamic changes
- DOM Cascading: Follow the normal cascading rules of CSS
- Inheritance: Values inherit to descendant elements
- Scope: Can be scoped to specific elements or globally using
:root
- JavaScript Access: Can be read and modified using JavaScript
- Responsive Capabilities: Can be changed within media queries
- Real DOM Properties: They exist as actual properties on DOM elements
Use Cases and Applications
CSS Variables are particularly valuable in several scenarios:
- Theme Systems: Creating light/dark modes or brand variations
- Responsive Design: Adjusting values at different breakpoints
- Component Libraries: Providing customization options for components
- Animation: Animating between variable values
- Dynamic Styling: Changing styles based on user interaction
- Maintaining Design Systems: Centralizing design decisions
- Simplifying Complex Calculations: Using variables in
calc()
expressions - Reducing Repetition: Defining common values in one place
Theme Implementation with CSS Variables
A common implementation pattern for theming:
/* Define theme variables */
:root {
/* Light theme (default) */
--background-color: #ffffff;
--text-color: #333333;
--accent-color: #0066cc;
}
/* Dark theme */
[data-theme="dark"] {
--background-color: #1a1a1a;
--text-color: #f0f0f0;
--accent-color: #3399ff;
}
/* Apply theme variables */
body {
background-color: var(--background-color);
color: var(--text-color);
}
.button {
background-color: var(--accent-color);
}
Manipulating CSS Variables with JavaScript
CSS Variables can be dynamically changed using JavaScript:
// Get variable value
const rootStyles = getComputedStyle(document.documentElement);
const currentColor = rootStyles.getPropertyValue('--primary-color').trim();
// Set variable value
document.documentElement.style.setProperty('--primary-color', '#ff0000');
// Change variables based on user interaction
document.getElementById('slider').addEventListener('input', function(e) {
document.documentElement.style.setProperty('--spacing-unit', e.target.value + 'px');
});
CSS Variables vs. Preprocessor Variables
Key differences between native CSS Variables and preprocessor variables (Sass, Less):
Aspect | CSS Variables | Preprocessor Variables |
---|---|---|
Runtime Changes | Dynamic (can change at runtime) | Static (resolved at compile time) |
DOM Access | Available to JavaScript | Not available after compilation |
Cascade | Follow CSS inheritance rules | No concept of inheritance |
Scope | Scoped to selectors | Scoped to files or blocks |
Browser Support | Modern browsers (IE11 requires polyfill) | All browsers (compiles to standard CSS) |
Computation | Computed in browser | Computed during build process |
Advanced Techniques with CSS Variables
CSS Variables enable sophisticated styling techniques:
Calculated Properties
:root {
--base-size: 16px;
}
h1 { font-size: calc(var(--base-size) * 2.5); }
h2 { font-size: calc(var(--base-size) * 2); }
h3 { font-size: calc(var(--base-size) * 1.5); }
Variable Composition
:root {
--hue: 220;
--saturation: 50%;
--lightness: 50%;
--primary-color: hsl(var(--hue), var(--saturation), var(--lightness));
}
CSS Variables in Media Queries
:root {
--content-width: 100%;
}
@media (min-width: 768px) {
:root {
--content-width: 750px;
}
}
.container {
width: var(--content-width);
}
Performance Considerations
When using CSS Variables, consider these performance aspects:
- Recalculation Cost: Changing variables triggers style recalculations
- Specificity Management: Higher specificity overrides can increase complexity
- Fallback Processing: Browsers must evaluate fallback values when variables are invalid
- Deep Nesting: Deeply nested variables referencing other variables can impact performance
- Size Efficiency: Can reduce overall CSS size by eliminating repetition
Browser Support and Polyfills
- Modern Browsers: Full support in all modern browsers
- Legacy Support: IE11 requires polyfills with limitations
- Graceful Degradation: Using fallback values for unsupported browsers
- Feature Detection: Can test for support with
@supports (--custom-property: value) { ... }
Best Practices for CSS Variables
Effective use of CSS Variables involves:
- Naming Conventions: Using clear, consistent naming patterns
- Organization: Grouping related variables together
- Documentation: Commenting variable purpose and usage
- Fallbacks: Providing fallback values for critical properties
- Scoping: Using appropriate scope for variables rather than making everything global
- Moderation: Not overusing variables for single-use values
- Semantic Naming: Using names that reflect purpose, not specific values
- Layering: Creating systems of variables that reference other variables
By providing a native way to create reusable, dynamic values in CSS, Custom Properties have fundamentally changed how developers structure and maintain stylesheets, enabling more sophisticated, maintainable, and adaptable front-end code.