Defining and Using CSS Variables
- CSS Basics
- Selectors & Specificity
- Box Model & Units
- Typography & Text Styling
- Colors & Color Spaces
- Positioning & Stacking Context
- Flexbox
- Grid
- Grid Advanced
- Responsive Design
- Container Queries
- Pseudo-classes & Pseudo-elements
- Modern Selectors
- CSS Custom Properties
- CSS Functions
- Transitions & Animations
- Transforms
- Forms & Input Styling
- CSS Architecture & Methodologies
- Accessibility in CSS
- Performance & Optimization
- View Transitions & Modern APIs
- Cascade Layers & Scope
What CSS Custom Properties Are
Custom properties, informally called CSS variables, let a value be defined once and reused across a stylesheet. Unlike variables in a preprocessor like Sass, they are a real part of the CSS language and can even be read and changed with JavaScript at runtime.
Defining a Custom Property
:root {
--brand-color: #1d4ed8;
--spacing-unit: 8px;
}Custom property names always start with two dashes. Defining them on :root (which matches the <html> element) makes them available globally throughout the whole document.
Using a Variable with var()
.btn {
background: var(--brand-color);
padding: calc(var(--spacing-unit) * 2);
}The var() function reads a custom property's current value wherever it is referenced.
Why Use Custom Properties Instead of Repeating Values
- Changing one brand color in one place updates every element that references it, instead of hunting through the whole file for every hex code.
- They can be changed dynamically - by a media query, a class toggle, or JavaScript - something a Sass variable cannot do, since Sass variables are compiled away before the browser ever sees them.
- They naturally support theming, such as switching a whole color palette for dark mode by redefining a small set of variables.
Overriding a Variable Locally
.dark-section {
--brand-color: #93c5fd; /* only affects elements inside .dark-section */
}Because custom properties follow the normal cascade and inheritance rules, redefining one inside a specific selector only affects that selector and its descendants, leaving the global value untouched elsewhere.