CSS Custom Properties (Variables): Reusable Values in Your Stylesheets
What Are CSS Custom Properties?
CSS Custom Properties, commonly called CSS Variables, let you define reusable values in your stylesheets. Instead of repeating the same color, spacing, or font value everywhere, you define it once and reference it throughout your CSS.
:root {
--primary-color: #3498db;
--font-size-base: 16px;
--spacing-md: 1.5rem;
}
.button {
background: var(--primary-color);
font-size: var(--font-size-base);
padding: var(--spacing-md);
}Declaring Custom Properties
Custom properties are declared with a double hyphen prefix (--) and can be placed on any element:
/* Global variables on :root */
:root {
--brand-blue: #3498db;
--brand-red: #e74c3c;
--text-color: #333;
--bg-color: #ffffff;
--border-radius: 8px;
}
/* Component-scoped variables */
.card {
--card-padding: 1.5rem;
--card-shadow: 0 2px 8px rgba(0,0,0,0.1);
}The :root selector targets the html element and is the conventional place for global variables.
Using Custom Properties
Use the var() function to reference a custom property:
.header {
background-color: var(--brand-blue);
color: var(--bg-color);
}
.card {
padding: var(--card-padding);
box-shadow: var(--card-shadow);
border-radius: var(--border-radius);
}
h1 {
color: var(--text-color);
}Fallback Values
The var() function accepts a second argument as a fallback value in case the variable is not defined:
.element {
/* If --accent-color is not defined, use #e74c3c */
color: var(--accent-color, #e74c3c);
/* Fallback can reference another variable */
background: var(--card-bg, var(--bg-color, white));
}Fallbacks are especially useful for component libraries where users may or may not define certain variables.
Scope and Inheritance
Custom properties follow CSS inheritance rules. Variables defined on a parent are available to all children:
:root {
--text-color: #333;
}
/* This inherits --text-color from :root */
p { color: var(--text-color); }
/* Override for a specific section */
.dark-section {
--text-color: #fff;
}
/* Paragraphs inside .dark-section use white */
.dark-section p { color: var(--text-color); } /* #fff */This scoping is powerful — you can redefine variables on any element to change values for its entire subtree.
Practical Use Cases
Color System
:root {
/* Primary palette */
--color-primary: #3498db;
--color-primary-light: #5dade2;
--color-primary-dark: #2471a3;
/* Neutral palette */
--color-gray-100: #f8f9fa;
--color-gray-200: #e9ecef;
--color-gray-300: #dee2e6;
--color-gray-600: #6c757d;
--color-gray-800: #343a40;
--color-gray-900: #212529;
/* Semantic colors */
--color-success: #27ae60;
--color-warning: #f39c12;
--color-error: #e74c3c;
}Spacing Scale
:root {
--space-xs: 0.25rem; /* 4px */
--space-sm: 0.5rem; /* 8px */
--space-md: 1rem; /* 16px */
--space-lg: 1.5rem; /* 24px */
--space-xl: 2rem; /* 32px */
--space-2xl: 3rem; /* 48px */
--space-3xl: 4rem; /* 64px */
}
.section { padding: var(--space-2xl) 0; }
.card { padding: var(--space-lg); margin-bottom: var(--space-md); }
.button { padding: var(--space-sm) var(--space-lg); }Typography Scale
:root {
--font-family-base: 'Inter', system-ui, sans-serif;
--font-family-heading: 'Poppins', sans-serif;
--font-family-mono: 'Fira Code', monospace;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 2rem;
--font-size-4xl: 2.5rem;
}
body { font-family: var(--font-family-base); font-size: var(--font-size-base); }
h1 { font-family: var(--font-family-heading); font-size: var(--font-size-4xl); }
h2 { font-family: var(--font-family-heading); font-size: var(--font-size-3xl); }
code { font-family: var(--font-family-mono); font-size: var(--font-size-sm); }Component Theming
.button {
--btn-bg: var(--color-primary);
--btn-color: white;
--btn-padding: var(--space-sm) var(--space-lg);
background: var(--btn-bg);
color: var(--btn-color);
padding: var(--btn-padding);
border: none;
border-radius: var(--border-radius);
}
.button:hover {
--btn-bg: var(--color-primary-dark);
}
.button-danger {
--btn-bg: var(--color-error);
}
.button-outline {
--btn-bg: transparent;
--btn-color: var(--color-primary);
border: 2px solid var(--color-primary);
}Dark Mode with CSS Variables
CSS variables make implementing dark mode straightforward:
:root {
--bg-color: #ffffff;
--text-color: #333333;
--card-bg: #f8f9fa;
--border-color: #dee2e6;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1a1a2e;
--text-color: #e0e0e0;
--card-bg: #16213e;
--border-color: #2a2a4a;
}
}
body {
background: var(--bg-color);
color: var(--text-color);
}
.card {
background: var(--card-bg);
border: 1px solid var(--border-color);
}Your entire site theme changes by simply redefining the variable values.
Custom Properties vs Preprocessor Variables
CSS custom properties are different from Sass/LESS variables:
- CSS Variables: Live in the browser, can be changed dynamically, respect scope and inheritance
- Sass Variables: Compiled at build time, static once compiled, no runtime changes
/* CSS Variable: can change at runtime */
:root { --color: blue; }
.dark { --color: lightblue; } /* Overrides in this scope */
/* Sass Variable: fixed at compile time */
/* $color: blue; -- Cannot be changed at runtime */Summary
CSS custom properties bring reusability, maintainability, and dynamic theming to your stylesheets. Define global variables on :root, use var() to reference them, and leverage scope and inheritance to create flexible component themes. CSS variables are the foundation of modern CSS architecture, enabling features like dark mode, design systems, and responsive typography with clean, maintainable code.