CSS Variables (Custom Properties)
Answer
CSS Custom Properties (CSS Variables) allow you to define reusable values in CSS. They cascade like regular CSS properties and can be dynamically changed with JavaScript.
Syntax
/* Define a variable */
:root {
--primary-color: #3498db;
--spacing-unit: 8px;
}
/* Use a variable */
.button {
background: var(--primary-color);
padding: var(--spacing-unit);
}
Variable Scope
/* Global scope */
:root {
--primary: blue;
--secondary: green;
}
/* Component scope */
.card {
--card-padding: 20px;
}
/* Override in specific context */
.dark-theme {
--primary: lightblue;
--secondary: lightgreen;
}
Fallback Values
.element {
/* If --accent not defined, use red */
color: var(--accent, red);
/* Fallback can be another variable */
background: var(--bg, var(--default-bg, white));
/* Fallback with complex value */
padding: var(--spacing, 16px 24px);
}
Theme Switching
/* Light theme (default) */
:root {
--bg-color: #ffffff;
--text-color: #333333;
--border-color: #e0e0e0;
--shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Dark theme */
[data-theme="dark"] {
--bg-color: #1a1a1a;
--text-color: #f0f0f0;
--border-color: #333333;
--shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
/* Components use variables - no duplication needed */
.card {
background: var(--bg-color);
color: var(--text-color);
border: 1px solid var(--border-color);
box-shadow: var(--shadow);
}
<!-- Toggle theme -->
<html data-theme="light">
<!-- or -->
<html data-theme="dark"></html>
</html>
Design System Example
:root {
/* Colors */
--color-primary: #3498db;
--color-primary-dark: #2980b9;
--color-secondary: #2ecc71;
--color-danger: #e74c3c;
--color-warning: #f1c40f;
/* Typography */
--font-family: "Inter", system-ui, sans-serif;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.25rem;
--font-size-xl: 1.5rem;
/* Spacing scale */
--space-1: 4px;
--space-2: 8px;
--space-3: 16px;
--space-4: 24px;
--space-5: 32px;
/* Border radius */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 20px rgba(0, 0, 0, 0.15);
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 300ms ease;
}
JavaScript Interaction
// Get variable value
const styles = getComputedStyle(document.documentElement);
const primary = styles.getPropertyValue("--primary-color");
// Set variable value
document.documentElement.style.setProperty("--primary-color", "#ff6b6b");
// Set on specific element
element.style.setProperty("--custom-width", "200px");
// Theme toggle
function toggleTheme() {
const html = document.documentElement;
const currentTheme = html.getAttribute("data-theme");
html.setAttribute("data-theme", currentTheme === "dark" ? "light" : "dark");
}
Calculations with CSS Variables
:root {
--base-size: 16px;
--scale: 1.25;
}
h1 {
/* Calculate using var in calc() */
font-size: calc(
var(--base-size) * var(--scale) * var(--scale) * var(--scale)
);
}
.grid {
--columns: 3;
--gap: 20px;
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: var(--gap);
}
/* Responsive adjustments */
@media (max-width: 768px) {
.grid {
--columns: 2;
--gap: 15px;
}
}
Component-Based Variables
/* Button component with internal variables */
.button {
--btn-padding-x: 16px;
--btn-padding-y: 8px;
--btn-bg: var(--color-primary);
--btn-color: white;
padding: var(--btn-padding-y) var(--btn-padding-x);
background: var(--btn-bg);
color: var(--btn-color);
}
/* Size variants */
.button--small {
--btn-padding-x: 12px;
--btn-padding-y: 4px;
}
.button--large {
--btn-padding-x: 24px;
--btn-padding-y: 12px;
}
/* Color variants */
.button--danger {
--btn-bg: var(--color-danger);
}
.button--outline {
--btn-bg: transparent;
--btn-color: var(--color-primary);
border: 2px solid currentColor;
}
Browser Support & Fallbacks
.element {
/* Fallback for old browsers */
color: blue;
/* Modern browsers */
color: var(--primary-color, blue);
}
/* Or use @supports */
@supports (--css: variables) {
.element {
color: var(--primary-color);
}
}
Key Points
- Define with
--name, use withvar(--name) - Scope to
:rootfor global, element for local - Variables cascade and can be overridden
- Provide fallbacks:
var(--name, fallback) - Can be modified with JavaScript
- Perfect for theming and design systems
- Work inside
calc()for dynamic calculations