14 minLesson 10 of 40
CSS3 & Responsive Design
CSS Variables & Custom Properties
CSS Variables & Custom Properties
CSS custom properties (commonly called CSS variables) let you define reusable values in one place and reference them throughout your stylesheet. Combined with JavaScript, they enable dynamic theming, dark mode, and runtime style changes.
Defining and Using Variables
/* Define on :root — globally available */
:root {
--color-primary: #4a90e2;
--color-secondary: #6c757d;
--color-background: #ffffff;
--color-text: #1a1a2e;
--color-error: #e74c3c;
--color-success: #2ecc71;
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 2rem;
--spacing-xl: 4rem;
--font-size-sm: 0.875rem;
--font-size-md: 1rem;
--font-size-lg: 1.25rem;
--font-size-xl: 1.5rem;
--font-size-2xl: 2rem;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--radius-full: 999px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.12);
--shadow-md: 0 4px 12px rgba(0,0,0,0.15);
--shadow-lg: 0 8px 30px rgba(0,0,0,0.20);
--transition: 200ms ease;
}
/* Use with var() */
.button {
background-color: var(--color-primary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
font-size: var(--font-size-md);
transition: background-color var(--transition);
}
.button:hover {
background-color: var(--color-secondary);
}
Fallback Values
/* var(--property, fallback) */
.element {
color: var(--text-color, #333); /* use #333 if --text-color isn't set */
font-size: var(--size, var(--font-size-md, 1rem)); /* chained fallbacks */
}
Dark Mode Theming
:root {
--bg: #ffffff;
--text: #1a1a2e;
--card-bg: #f8f9fa;
--border: #e0e0e0;
}
/* Option 1: Using prefers-color-scheme */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a2e;
--text: #e8e8f0;
--card-bg: #2d2d44;
--border: #404060;
}
}
/* Option 2: Using data attribute (user-toggled) */
[data-theme="dark"] {
--bg: #1a1a2e;
--text: #e8e8f0;
--card-bg: #2d2d44;
--border: #404060;
}
/* Use variables throughout */
body {
background-color: var(--bg);
color: var(--text);
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--border);
}
// Toggle dark mode via JavaScript
const toggle = document.getElementById("theme-toggle");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
let isDark = prefersDark.matches;
function applyTheme() {
document.documentElement.dataset.theme = isDark ? "dark" : "light";
toggle.textContent = isDark ? "☀️" : "🌙";
}
toggle.addEventListener("click", () => {
isDark = !isDark;
localStorage.setItem("theme", isDark ? "dark" : "light");
applyTheme();
});
// Apply saved preference
const saved = localStorage.getItem("theme");
if (saved) isDark = saved === "dark";
applyTheme();
Component-Scoped Variables
Override variables for specific components:
.button {
--btn-bg: var(--color-primary);
--btn-text: white;
--btn-padding: var(--spacing-sm) var(--spacing-md);
background: var(--btn-bg);
color: var(--btn-text);
padding: var(--btn-padding);
}
.button--danger {
--btn-bg: var(--color-error); /* just override the variable */
}
.button--large {
--btn-padding: var(--spacing-md) var(--spacing-lg);
}
/* No need to rewrite all button properties */
Dynamic Variables with JavaScript
:root {
--primary-hue: 210; /* just the hue, update this one value */
--color-primary: hsl(var(--primary-hue), 70%, 50%);
--color-primary-light: hsl(var(--primary-hue), 70%, 70%);
--color-primary-dark: hsl(var(--primary-hue), 70%, 30%);
}
// Read a CSS variable
const hue = getComputedStyle(document.documentElement)
.getPropertyValue("--primary-hue").trim();
// Set a CSS variable
document.documentElement.style.setProperty("--primary-hue", "280");
// Now ALL colors derived from --primary-hue update instantly!
// Color picker that updates the whole theme
colorPicker.addEventListener("input", (e) => {
document.documentElement.style.setProperty("--primary-hue", e.target.value);
});
Animations with Variables
:root {
--animation-speed: 200ms;
}
.card {
transform: translateY(0);
transition: transform var(--animation-speed) ease;
}
.card:hover {
transform: translateY(-4px);
}
/* User can prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
:root { --animation-speed: 0ms; }
}
Next lesson: Animations & Transitions — bringing your UI to life.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises