CSS Custom Properties (Variables)
CSS custom properties, commonly called "CSS variables," are one of the most powerful modern CSS features. They enable dynamic theming, maintainable design systems, and real-time JavaScript integration — things preprocessor variables simply cannot do.
1. Declaration and Syntax
CSS variables use the double-hyphen prefix -- and are declared inside a selector block:
:root {
/* Naming convention: --category-name-modifier */
--color-primary: #6c63ff;
--font-size-base: 1rem;
--space-md: 1.5rem;
}
:root is the recommended place for global variables — it targets the <html> element with the highest specificity of any element selector.
2. The var() Function
/* Syntax: var(--variable-name, fallback-value) */
.button {
background: var(--color-primary);
padding: var(--space-md);
font-size: var(--font-size-base, 1rem); /* Fallback if variable is undefined */
}
Nested Fallbacks
.text {
/* Try --text-color, then --color-text, then hardcoded #111 */
color: var(--text-color, var(--color-text, #111));
}
3. Scope: Local vs Global
Variables cascade like all CSS — you can scope them to a component:
/* Global */
:root { --card-bg: #fff; }
/* Scoped override — only affects .dark-section and its children */
.dark-section {
--card-bg: #1c1c1e;
--color-text: #f0f0f0;
}
.card { background: var(--card-bg); } /* Automatically adjusts in dark sections */
4. Complete Design Token System
A production-ready token set to paste into your :root:
:root {
/* ── Color Palette ── */
--hue: 245;
--color-primary: hsl(var(--hue), 80%, 62%);
--color-primary-dark: hsl(var(--hue), 80%, 48%);
--color-secondary: hsl(35, 90%, 58%);
--color-bg: #0f0f0f;
--color-surface: #1a1a1a;
--color-surface-2: #242424;
--color-border: rgba(255, 255, 255, 0.08);
--color-text: #f0f0f0;
--color-text-muted: #888888;
--color-danger: hsl(0, 75%, 55%);
--color-success: hsl(145, 65%, 45%);
--color-warning: hsl(40, 95%, 55%);
/* ── Typography ── */
--font-heading: 'Playfair Display', Georgia, serif;
--font-body: 'Inter', system-ui, -apple-system, sans-serif;
--font-mono: 'Fira Code', 'Courier New', monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.25rem;
--text-xl: 1.5rem;
--text-2xl: 2rem;
--text-3xl: clamp(2rem, 4vw, 3rem);
--text-hero: clamp(2.5rem, 6vw, 5rem);
/* ── Spacing ── */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-24: 6rem; /* 96px */
/* ── Border Radius ── */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--radius-xl: 24px;
--radius-full: 9999px;
/* ── Shadows ── */
--shadow-sm: 0 1px 3px rgba(0,0,0,0.15);
--shadow-md: 0 4px 12px rgba(0,0,0,0.3);
--shadow-lg: 0 8px 32px rgba(0,0,0,0.5);
/* ── Transitions ── */
--ease-fast: 0.15s ease;
--ease-normal: 0.3s ease;
--ease-slow: 0.6s ease;
--ease-bounce: 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
5. Dark Mode with Variables
:root { --bg: #fff; --text: #111; --card: #f5f5f5; }
@media (prefers-color-scheme: dark) {
:root { --bg: #0f0f0f; --text: #f0f0f0; --card: #1c1c1e; }
}
/* Or via attribute/class for a JS toggle */
[data-theme="dark"] {
--bg: #0f0f0f;
--text: #f0f0f0;
--card: #1c1c1e;
}
6. JavaScript Integration
// Read a variable
const primary = getComputedStyle(document.documentElement)
.getPropertyValue('--color-primary').trim();
// Write a variable
document.documentElement.style.setProperty('--color-primary', '#00ff87');
// Dark mode toggle
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme');
document.documentElement.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark');
localStorage.setItem('theme', current === 'dark' ? 'light' : 'dark');
}
// Restore on page load
document.documentElement.setAttribute('data-theme', localStorage.getItem('theme') || 'light');
7. Variables vs Sass Variables
| Feature | CSS Variables | Sass Variables |
|---|---|---|
| Computed at | Runtime (in browser) | Compile time |
| Can be changed by JS | ✅ Yes | ❌ No |
| Can be scoped | ✅ Yes (any selector) | ❌ Global only |
| Dark mode toggle | ✅ Easy | ❌ Requires duplication |
| Browser support | ✅ 97%+ | Needs build step |
| Fallback values | ✅ var(--x, fallback) | ❌ None |
[!TIP] In modern WordPress projects, use CSS Custom Properties for everything — theme colors, spacing, font sizes. They're natively supported by GeneratePress and Bricks Builder, and you can update them at runtime without rebuilding.