Custom Properties
CSS Custom Properties (CSS Variables) provide native variable support that works at runtime, unlike preprocessor variables that are compiled away.
The Old Way
Section titled “The Old Way”// Sass variables$primary-color: #3b82f6;$secondary-color: #10b981;$spacing-unit: 8px;$border-radius: 4px;
.button { background-color: $primary-color; padding: $spacing-unit * 2; border-radius: $border-radius;}
.button-secondary { background-color: $secondary-color;}
// Theme switching requires:// 1. Separate compiled stylesheets// 2. JavaScript to swap stylesheets// 3. Or duplicated rule setsProblems:
- Variables compiled away at build time
- Cannot change values at runtime
- Theme switching requires multiple stylesheets or duplicated code
- No JavaScript access to variable values
- No cascade or inheritance
The Modern Way
Section titled “The Modern Way”:root { --color-primary: #3b82f6; --color-secondary: #10b981; --spacing-unit: 8px; --border-radius: 4px;}
.button { background-color: var(--color-primary); padding: calc(var(--spacing-unit) * 2); border-radius: var(--border-radius);}
.button-secondary { background-color: var(--color-secondary);}
/* Theme switching - just update the variables */[data-theme='dark'] { --color-primary: #60a5fa; --color-secondary: #34d399;}Benefits:
- Runtime variable updates
- JavaScript can read and modify values
- Cascade and inheritance work naturally
- Single stylesheet for multiple themes
- Fallback values built-in
Advanced Patterns
Section titled “Advanced Patterns”Fallback Values
Section titled “Fallback Values”.element { /* Fallback if --color-accent is not defined */ color: var(--color-accent, #3b82f6);
/* Nested fallbacks */ background: var(--bg-custom, var(--bg-default, white));}Scoped Variables
Section titled “Scoped Variables”.card { --card-padding: 16px; --card-radius: 8px;
padding: var(--card-padding); border-radius: var(--card-radius);}
.card.compact { --card-padding: 8px; --card-radius: 4px; /* No need to redeclare the properties */}JavaScript Integration
Section titled “JavaScript Integration”// Read a CSS variableconst primaryColor = getComputedStyle(document.documentElement).getPropertyValue( '--color-primary');
// Set a CSS variabledocument.documentElement.style.setProperty('--color-primary', '#ff0000');
// Set on a specific elementelement.style.setProperty('--local-var', '20px');Dynamic Calculations
Section titled “Dynamic Calculations”:root { --base-size: 16px; --scale-ratio: 1.25;}
h1 { font-size: calc(var(--base-size) * var(--scale-ratio) * var(--scale-ratio) * var(--scale-ratio));}h2 { font-size: calc(var(--base-size) * var(--scale-ratio) * var(--scale-ratio));}h3 { font-size: calc(var(--base-size) * var(--scale-ratio));}Animation with Custom Properties
Section titled “Animation with Custom Properties”@property --gradient-angle { syntax: '<angle>'; initial-value: 0deg; inherits: false;}
.animated-gradient { background: linear-gradient(var(--gradient-angle), #3b82f6, #10b981); animation: rotate-gradient 3s linear infinite;}
@keyframes rotate-gradient { to { --gradient-angle: 360deg; }}Browser Support
Section titled “Browser Support”Supported in all modern browsers. See caniuse.com/css-variables.
@property for typed custom properties has good support. See caniuse.com/mdn-css_at-rules_property.
Performance Considerations
Section titled “Performance Considerations”- Custom properties are resolved at computed-value time
- Changes trigger style recalculation for affected elements
- Scope variables as narrowly as possible for best performance
- Avoid setting custom properties in tight loops
When to Use
Section titled “When to Use”Use Custom Properties when:
- Building theme systems (light/dark mode)
- Values need to change at runtime
- JavaScript needs to read or modify CSS values
- Creating component-scoped design tokens
- Building responsive systems with calculated values
Keep using preprocessor variables when:
- Values are truly static and never change
- You need preprocessing features (mixins, functions)
- Working with legacy browser requirements