CSS Color Variables & Custom Properties
Master CSS custom properties for flexible, maintainable color systems. Perfect for theming and design systems.
🎨
Easy Theming
Switch between light/dark modes with a single class change
âš¡
Real-time Updates
Change colors dynamically without recompiling
🔧
Maintainable
Update once, apply everywhere across your design
Basic Syntax
CSS custom properties use the -- prefix and are defined in the :root selector for global access.
:root {
--primary: #2D5BE3;
--secondary: #E8453C;
--background: #FFFFFF;
--text: #1A1A1A;
--border: #E5E4E0;
}
.button {
background: var(--primary);
color: var(--background);
border: 2px solid var(--primary);
}
.card {
background: var(--background);
color: var(--text);
border: 1px solid var(--border);
}
HEX vs RGB vs HSL in Variables
Each format has its strengths. Choose based on your use case.
HEX (Simple & Clean)
:root {
--color-primary: #2D5BE3;
}
✓ Most common, easy to read
✗ Can't adjust opacity easily
RGB (Best for Opacity)
:root {
--primary-rgb: 45, 91, 227;
}
.overlay {
background: rgba(var(--primary-rgb), 0.5);
}
✓ Perfect for transparency
✓ Works with rgba()
:root {
--primary-h: 225;
--primary-s: 77%;
--primary-l: 53%;
--primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
}
.button-light {
background: hsl(var(--primary-h), var(--primary-s), 80%);
}
.button-dark {
background: hsl(var(--primary-h), var(--primary-s), 30%);
}
Dark Mode Implementation
Use CSS custom properties to build elegant dark mode switching without duplicating CSS.
:root {
--bg: #FFFFFF;
--bg-secondary: #F5F5F5;
--text: #1A1A1A;
--text-secondary: #6B6B6B;
--border: #E5E4E0;
--accent: #2D5BE3;
}
:root.dark-mode {
--bg: #0F0F0F;
--bg-secondary: #1E1E1E;
--text: #FAFAFA;
--text-secondary: #A0A0A0;
--border: #2A2A2A;
--accent: #4169E1;
}
body {
background: var(--bg);
color: var(--text);
}
.card {
background: var(--bg-secondary);
border: 1px solid var(--border);
}
const toggleDarkMode = () => {
document.documentElement.classList.toggle('dark-mode');
localStorage.setItem('theme',
document.documentElement.classList.contains('dark-mode') ? 'dark' : 'light'
);
};
if (localStorage.getItem('theme') === 'dark') {
document.documentElement.classList.add('dark-mode');
}
Live Demo
Component Preview
This component uses CSS variables for all colors. Toggle between themes to see how easily colors update.
Complete Design System Example
A production-ready color system with semantic naming and scale.
:root {
--brand-primary: #2D5BE3;
--brand-secondary: #E8453C;
--neutral-50: #FAFAFA;
--neutral-100: #F5F5F5;
--neutral-200: #E5E5E5;
--neutral-300: #D4D4D4;
--neutral-400: #A3A3A3;
--neutral-500: #737373;
--neutral-600: #525252;
--neutral-700: #404040;
--neutral-800: #262626;
--neutral-900: #171717;
--color-success: #18A558;
--color-warning: #F4A940;
--color-error: #E8453C;
--color-info: #2D5BE3;
--bg-primary: var(--neutral-50);
--bg-secondary: var(--neutral-100);
--bg-tertiary: var(--neutral-200);
--text-primary: var(--neutral-900);
--text-secondary: var(--neutral-600);
--text-tertiary: var(--neutral-400);
--border-light: var(--neutral-200);
--border-medium: var(--neutral-300);
--border-dark: var(--neutral-400);
}
Quick Copy Snippets
Ready-to-use patterns for common scenarios.
Opacity Variants with RGB
:root {
--primary-rgb: 45, 91, 227;
}
.overlay-light { background: rgba(var(--primary-rgb), 0.1); }
.overlay-medium { background: rgba(var(--primary-rgb), 0.5); }
.overlay-heavy { background: rgba(var(--primary-rgb), 0.9); }
Hover State Variations
:root {
--primary: #2D5BE3;
--primary-hover: #2349BE;
--primary-active: #1A3799;
}
.button {
background: var(--primary);
transition: background 0.2s;
}
.button:hover { background: var(--primary-hover); }
.button:active { background: var(--primary-active); }
System Preference Detection
@media (prefers-color-scheme: dark) {
:root {
--bg: #0F0F0F;
--text: #FAFAFA;
}
}
Pro Tip
Use semantic names like --color-primary instead of --blue. This makes it easier to change your entire color scheme without touching your CSS.