Table of Contents
- Introduction to CSS Custom Properties
- Basic Syntax and Usage
- Variable Scope and Inheritance
- The Cascade with Custom Properties
- Dynamic Updates with JavaScript
- Practical Applications
- Theming and Dark Mode
- Responsive Design with Variables
- Performance Considerations
- Browser Support and Fallbacks
- Advanced Techniques
- Best Practices
- Real-World Examples
Introduction to CSS Custom Properties
CSS Custom Properties, commonly known as CSS Variables, are entities defined by CSS authors that contain specific values to be reused throughout a document. They follow the same cascade and inheritance rules as regular CSS properties.
Why CSS Variables?
/* Without CSS Variables - Repetitive and hard to maintain */
.button-primary {
background-color: #3498db;
color: #ffffff;
border: 2px solid #2980b9;
}
.button-secondary {
background-color: #2ecc71;
color: #ffffff;
border: 2px solid #27ae60;
}
.alert-success {
background-color: #2ecc71;
color: #ffffff;
border: 1px solid #27ae60;
}
/* With CSS Variables - Clean, maintainable, and dynamic */
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--text-light: #ffffff;
--border-dark: #2980b9;
--border-darker: #27ae60;
}
.button-primary {
background-color: var(--primary-color);
color: var(--text-light);
border: 2px solid var(--border-dark);
}
.button-secondary,
.alert-success {
background-color: var(--secondary-color);
color: var(--text-light);
border: 1px solid var(--border-darker);
}
Benefits of CSS Variables
/* 1. Maintainability - Change one value, update everywhere */
:root {
--brand-color: #3498db; /* Change this once */
}
.header { background-color: var(--brand-color); }
.footer { background-color: var(--brand-color); }
.button { background-color: var(--brand-color); }
/* 2. Readability - Self-documenting code */
.element {
box-shadow: 0 2px 4px var(--shadow-color);
transition: all var(--transition-speed) var(--transition-timing);
}
/* 3. Dynamic updates - Can be changed at runtime */
.element {
--custom-padding: 1rem;
padding: var(--custom-padding);
}
/* 4. Cascade-friendly - Respect CSS cascade rules */
Basic Syntax and Usage
Defining Variables
/* Basic variable definition */
element {
--variable-name: value;
}
/* Examples */
:root {
--main-color: #3498db;
--secondary-color: #2ecc71;
--font-size-large: 24px;
--spacing-unit: 8px;
--border-radius: 4px;
--transition-speed: 0.3s;
--max-width: 1200px;
--font-stack: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
Using Variables
/* Basic variable usage */
.element {
property: var(--variable-name);
property: var(--variable-name, fallback-value);
}
/* Examples */
.button {
background-color: var(--primary-color, #3498db); /* With fallback */
color: var(--text-color, white);
padding: calc(var(--spacing-unit, 8px) * 2);
font-size: var(--font-size-base, 16px);
border-radius: var(--border-radius, 4px);
transition: all var(--transition-speed, 0.3s) ease;
}
/* Multiple variables in one declaration */
.card {
box-shadow:
0 var(--shadow-offset, 2px) var(--shadow-blur, 4px)
var(--shadow-color, rgba(0,0,0,0.1));
}
Variable Naming Conventions
/* Common naming patterns */
/* 1. kebab-case (recommended) */
:root {
--primary-color: #3498db;
--font-size-base: 16px;
--spacing-unit: 8px;
}
/* 2. Contextual naming */
:root {
--color-primary: #3498db;
--color-secondary: #2ecc71;
--color-danger: #e74c3c;
--color-warning: #f39c12;
}
/* 3. Component-specific naming */
.alert {
--alert-bg: #f8d7da;
--alert-border: #f5c6cb;
--alert-text: #721c24;
}
/* 4. Theming variables */
:root {
--theme-light-bg: #ffffff;
--theme-light-text: #333333;
--theme-dark-bg: #1a1a1a;
--theme-dark-text: #ffffff;
}
/* 5. Functional naming */
:root {
--text-primary: #333333;
--text-secondary: #666666;
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--border-light: #e0e0e0;
--border-dark: #999999;
}
Variable Scope and Inheritance
Global Scope with :root
/* Global variables - available everywhere */
:root {
--global-color: #3498db;
--global-font: sans-serif;
--global-spacing: 8px;
}
/* These variables are accessible anywhere in the document */
.header {
background-color: var(--global-color);
}
.footer {
font-family: var(--global-font);
}
.card {
margin: var(--global-spacing);
}
Local Scope
/* Component-scoped variables */
.card {
--card-padding: 20px;
--card-bg: #ffffff;
--card-border: 1px solid #e0e0e0;
padding: var(--card-padding);
background: var(--card-bg);
border: var(--card-border);
}
/* These variables are only available within .card and its children */
.card__title {
color: var(--card-title-color, #333); /* Falls back to #333 */
margin-bottom: calc(var(--card-padding) / 2);
}
/* Nested scope */
.card:hover {
--card-bg: #f5f5f5; /* Changes only within hover state */
}
Inheritance Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Variable Inheritance</title>
<style>
:root {
--text-color: #333;
--accent-color: #3498db;
--spacing: 8px;
}
.container {
--container-bg: #f5f5f5;
--text-color: #666; /* Overrides :root for descendants */
background: var(--container-bg);
padding: calc(var(--spacing) * 3);
}
.card {
--card-bg: white;
--card-shadow: 0 2px 4px rgba(0,0,0,0.1);
background: var(--card-bg);
box-shadow: var(--card-shadow);
margin-bottom: var(--spacing);
padding: calc(var(--spacing) * 2);
color: var(--text-color); /* Uses .container's value */
}
.card__title {
color: var(--accent-color); /* Inherits from :root */
margin-bottom: var(--spacing);
}
.card__button {
--button-bg: var(--accent-color);
--button-text: white;
background: var(--button-bg);
color: var(--button-text);
padding: var(--spacing) calc(var(--spacing) * 2);
border: none;
cursor: pointer;
}
.card__button:hover {
--button-bg: #2980b9; /* Only affects this element */
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<h3 class="card__title">Card Title</h3>
<p>This text uses the container's text color (#666)</p>
<button class="card__button">Click Me</button>
</div>
<div class="card">
<h3 class="card__title">Another Card</h3>
<p>Same inheritance applies here</p>
<button class="card__button">Click Me</button>
</div>
</div>
</body>
</html>
Scope and Cascade Rules
/* CSS variables follow standard cascade rules */
/* 1. Later declarations override earlier ones */
.element {
--color: red;
--color: blue; /* Wins */
color: var(--color); /* blue */
}
/* 2. Specificity matters */
:root {
--color: red;
}
.element {
--color: blue; /* More specific than :root */
color: var(--color); /* blue */
}
/* 3. Media queries affect scope */
:root {
--columns: 1;
}
@media (min-width: 768px) {
:root {
--columns: 2; /* Changes globally in media query */
}
}
@media (min-width: 1024px) {
:root {
--columns: 3;
}
}
/* 4. Pseudo-classes create new scope */
.element {
--bg: white;
}
.element:hover {
--bg: #f0f0f0; /* Only applies on hover */
background: var(--bg);
}
The Cascade with Custom Properties
Understanding the Cascade
/* CSS variables participate in the cascade */
:root {
--brand-color: #3498db; /* Level 1 */
}
body {
--brand-color: #2ecc71; /* Level 2 - more specific than :root? No, same specificity */
}
.container {
--brand-color: #e74c3c; /* Level 3 - most specific? No, depends on element */
}
.element {
color: var(--brand-color); /* Which one wins? */
}
/* The actual winner depends on where .element is in the DOM */
Cascade Demonstration
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Variable Cascade</title>
<style>
/* Global variables */
:root {
--color: blue;
--padding: 10px;
}
/* Element selector */
body {
--color: green; /* Overrides :root for body and descendants */
}
/* Class selector */
.container {
--color: orange; /* Overrides body for .container and descendants */
--padding: 20px;
}
/* More specific class */
.container.highlight {
--color: red; /* Overrides .container when both classes present */
}
/* ID selector (even more specific) */
#special {
--color: purple; /* Overrides everything for #special */
}
/* !important doesn't work on custom properties */
.container {
--color: orange !important; /* !important is ignored for custom properties */
}
/* Style blocks */
.demo-box {
background-color: var(--color);
padding: var(--padding);
margin: 10px;
color: white;
}
</style>
</head>
<body>
<div class="demo-box">This should be green (body level)</div>
<div class="container">
<div class="demo-box">This should be orange (container level)</div>
</div>
<div class="container highlight">
<div class="demo-box">This should be red (container.highlight)</div>
</div>
<div id="special" class="container">
<div class="demo-box">This should be purple (ID selector)</div>
</div>
</body>
</html>
Custom Properties and the Cascade
/* Important cascade rules for custom properties */
/* Rule 1: Custom properties are not inherited like normal properties */
.parent {
--parent-var: blue;
color: black; /* Inherited by children */
}
.child {
/* --parent-var is available (inherited) */
/* color: black is also inherited */
background: var(--parent-var); /* Works */
}
/* Rule 2: Custom properties can be conditionally set */
.element {
--theme: light;
background: var(--light-bg, white);
}
.element.dark {
--theme: dark;
--light-bg: #333; /* Doesn't affect light-bg usage */
}
/* Rule 3: Custom properties are resolved at computed value time */
.element {
--size: 10px;
font-size: var(--size);
}
.element.large {
--size: 20px; /* Affects font-size */
}
/* Rule 4: Invalid at computed value time */
.element {
--color: 10px; /* Invalid for color property */
color: var(--color); /* Becomes invalid, falls back to inherited or initial */
}
Dynamic Updates with JavaScript
Getting and Setting Variables
// Getting CSS variable values
const root = document.documentElement;
// Get computed style
const primaryColor = getComputedStyle(root).getPropertyValue('--primary-color').trim();
console.log(primaryColor); // "#3498db"
// Getting from specific element
const element = document.querySelector('.card');
const cardBg = getComputedStyle(element).getPropertyValue('--card-bg').trim();
// Setting CSS variables
root.style.setProperty('--primary-color', '#e74c3c');
root.style.setProperty('--spacing-unit', '12px');
root.style.setProperty('--font-size', '18px', 'important'); // With priority
// Removing a variable
root.style.removeProperty('--temp-variable');
// Checking if variable exists
if (getComputedStyle(root).getPropertyValue('--my-var')) {
console.log('Variable exists');
}
Interactive Variable Updates
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Variables</title>
<style>
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--spacing: 8px;
--border-radius: 4px;
--transition-speed: 0.3s;
--font-size: 16px;
}
body {
font-family: sans-serif;
transition: background-color var(--transition-speed);
background-color: var(--primary-color);
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.demo-card {
background: white;
padding: calc(var(--spacing) * 4);
border-radius: var(--border-radius);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
width: 400px;
}
.demo-card h2 {
color: var(--primary-color);
margin: 0 0 calc(var(--spacing) * 2);
font-size: calc(var(--font-size) * 1.5);
}
.demo-card p {
color: #666;
line-height: 1.6;
margin-bottom: calc(var(--spacing) * 3);
font-size: var(--font-size);
}
.control-panel {
display: grid;
gap: calc(var(--spacing) * 2);
}
.control-group {
display: flex;
flex-direction: column;
gap: var(--spacing);
}
.control-group label {
font-weight: bold;
color: #333;
font-size: calc(var(--font-size) * 0.9);
}
.control-group input[type="range"],
.control-group input[type="color"] {
width: 100%;
cursor: pointer;
}
.value-display {
font-family: monospace;
background: #f0f0f0;
padding: calc(var(--spacing) / 2);
border-radius: var(--border-radius);
font-size: calc(var(--font-size) * 0.9);
}
</style>
</head>
<body>
<div class="demo-card">
<h2>CSS Variable Playground</h2>
<p>Adjust the controls below to see CSS variables in action. All changes update in real-time using JavaScript!</p>
<div class="control-panel">
<div class="control-group">
<label>Primary Color</label>
<input type="color" id="primary-color" value="#3498db">
<span class="value-display" id="primary-value">#3498db</span>
</div>
<div class="control-group">
<label>Spacing (<span id="spacing-value">8</span>px)</label>
<input type="range" id="spacing" min="4" max="24" value="8" step="1">
</div>
<div class="control-group">
<label>Border Radius (<span id="radius-value">4</span>px)</label>
<input type="range" id="border-radius" min="0" max="20" value="4" step="1">
</div>
<div class="control-group">
<label>Font Size (<span id="font-value">16</span>px)</label>
<input type="range" id="font-size" min="12" max="24" value="16" step="1">
</div>
<div class="control-group">
<label>Animation Speed (<span id="speed-value">0.3</span>s)</label>
<input type="range" id="speed" min="0.1" max="1" value="0.3" step="0.1">
</div>
</div>
</div>
<script>
const root = document.documentElement;
// Update primary color
document.getElementById('primary-color').addEventListener('input', (e) => {
root.style.setProperty('--primary-color', e.target.value);
document.getElementById('primary-value').textContent = e.target.value;
});
// Update spacing
document.getElementById('spacing').addEventListener('input', (e) => {
root.style.setProperty('--spacing', e.target.value + 'px');
document.getElementById('spacing-value').textContent = e.target.value;
});
// Update border radius
document.getElementById('border-radius').addEventListener('input', (e) => {
root.style.setProperty('--border-radius', e.target.value + 'px');
document.getElementById('radius-value').textContent = e.target.value;
});
// Update font size
document.getElementById('font-size').addEventListener('input', (e) => {
root.style.setProperty('--font-size', e.target.value + 'px');
document.getElementById('font-value').textContent = e.target.value;
});
// Update transition speed
document.getElementById('speed').addEventListener('input', (e) => {
root.style.setProperty('--transition-speed', e.target.value + 's');
document.getElementById('speed-value').textContent = e.target.value;
});
// Initial values
document.getElementById('primary-value').textContent =
getComputedStyle(root).getPropertyValue('--primary-color').trim();
</script>
</body>
</html>
Theme Switcher with JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Theme Switcher</title>
<style>
:root {
/* Light theme (default) */
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--text-primary: #333333;
--text-secondary: #666666;
--accent-color: #3498db;
--border-color: #e0e0e0;
--shadow-color: rgba(0,0,0,0.1);
--card-bg: white;
--header-bg: #3498db;
--header-text: white;
}
[data-theme="dark"] {
--bg-primary: #1a1a1a;
--bg-secondary: #2d2d2d;
--text-primary: #ffffff;
--text-secondary: #b0b0b0;
--accent-color: #64b5f6;
--border-color: #404040;
--shadow-color: rgba(0,0,0,0.3);
--card-bg: #2d2d2d;
--header-bg: #000000;
--header-text: #ffffff;
}
[data-theme="sepia"] {
--bg-primary: #fbf7e9;
--bg-secondary: #f0e9d8;
--text-primary: #5b4636;
--text-secondary: #7d6b5a;
--accent-color: #b85e3a;
--border-color: #d9c9b4;
--shadow-color: rgba(91,70,54,0.1);
--card-bg: #fbf7e9;
--header-bg: #8b5a2b;
--header-text: #fbf7e9;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
font-family: sans-serif;
margin: 0;
transition: background-color 0.3s, color 0.3s;
}
header {
background-color: var(--header-bg);
color: var(--header-text);
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.theme-selector {
display: flex;
gap: 1rem;
}
.theme-btn {
padding: 0.5rem 1rem;
border: 1px solid var(--border-color);
background: var(--bg-secondary);
color: var(--text-primary);
cursor: pointer;
border-radius: 4px;
transition: all 0.3s;
}
.theme-btn:hover {
background: var(--accent-color);
color: white;
}
.container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.card {
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 2px 4px var(--shadow-color);
transition: all 0.3s;
}
.card h3 {
color: var(--text-primary);
margin-bottom: 1rem;
}
.card p {
color: var(--text-secondary);
line-height: 1.6;
}
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin: 2rem 0;
}
.stat {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
text-align: center;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
color: var(--accent-color);
}
.stat-label {
color: var(--text-secondary);
margin-top: 0.5rem;
}
button {
background: var(--accent-color);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: opacity 0.3s;
}
button:hover {
opacity: 0.9;
}
</style>
</head>
<body>
<header>
<h1>Theme Switcher Demo</h1>
<div class="theme-selector">
<button class="theme-btn" data-theme="light">Light</button>
<button class="theme-btn" data-theme="dark">Dark</button>
<button class="theme-btn" data-theme="sepia">Sepia</button>
</div>
</header>
<div class="container">
<div class="stats">
<div class="stat">
<div class="stat-value">150+</div>
<div class="stat-label">Components</div>
</div>
<div class="stat">
<div class="stat-value">3</div>
<div class="stat-label">Themes</div>
</div>
<div class="stat">
<div class="stat-value">24/7</div>
<div class="stat-label">Support</div>
</div>
</div>
<div class="card-grid">
<div class="card">
<h3>Card Title 1</h3>
<p>This card automatically updates its colors based on the selected theme. All using CSS variables!</p>
<button>Learn More</button>
</div>
<div class="card">
<h3>Card Title 2</h3>
<p>Notice how borders, shadows, and text colors all change consistently across components.</p>
<button>Learn More</button>
</div>
<div class="card">
<h3>Card Title 3</h3>
<p>The theme switcher uses JavaScript to update the data-theme attribute on the HTML element.</p>
<button>Learn More</button>
</div>
</div>
</div>
<script>
// Theme switching
const themeButtons = document.querySelectorAll('.theme-btn');
const html = document.documentElement;
themeButtons.forEach(button => {
button.addEventListener('click', () => {
const theme = button.getAttribute('data-theme');
html.setAttribute('data-theme', theme);
// Optional: Save preference
localStorage.setItem('theme', theme);
});
});
// Load saved theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
html.setAttribute('data-theme', savedTheme);
}
// Live variable inspection
function logCurrentVariables() {
const styles = getComputedStyle(html);
const variables = [
'--bg-primary',
'--text-primary',
'--accent-color',
'--border-color'
];
console.log('Current theme values:');
variables.forEach(varName => {
console.log(`${varName}: ${styles.getPropertyValue(varName).trim()}`);
});
}
// Log when theme changes
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'data-theme') {
logCurrentVariables();
}
});
});
observer.observe(html, { attributes: true });
</script>
</body>
</html>
Practical Applications
Design System with Variables
/* Complete design system using CSS variables */
:root {
/* Color palette */
--color-primary: #3498db;
--color-primary-light: #5faee3;
--color-primary-dark: #2980b9;
--color-secondary: #2ecc71;
--color-secondary-light: #58d68d;
--color-secondary-dark: #27ae60;
--color-danger: #e74c3c;
--color-warning: #f39c12;
--color-info: #3498db;
--color-success: #2ecc71;
--color-gray-100: #f8f9fa;
--color-gray-200: #e9ecef;
--color-gray-300: #dee2e6;
--color-gray-400: #ced4da;
--color-gray-500: #adb5bd;
--color-gray-600: #6c757d;
--color-gray-700: #495057;
--color-gray-800: #343a40;
--color-gray-900: #212529;
/* Typography */
--font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-family-heading: var(--font-family-base);
--font-family-monospace: 'SF Mono', Monaco, Consolas, monospace;
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-md: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.75;
/* Spacing */
--spacing-1: 0.25rem; /* 4px */
--spacing-2: 0.5rem; /* 8px */
--spacing-3: 0.75rem; /* 12px */
--spacing-4: 1rem; /* 16px */
--spacing-5: 1.25rem; /* 20px */
--spacing-6: 1.5rem; /* 24px */
--spacing-8: 2rem; /* 32px */
--spacing-10: 2.5rem; /* 40px */
--spacing-12: 3rem; /* 48px */
--spacing-16: 4rem; /* 64px */
/* Borders */
--border-radius-sm: 0.125rem; /* 2px */
--border-radius-md: 0.25rem; /* 4px */
--border-radius-lg: 0.5rem; /* 8px */
--border-radius-xl: 1rem; /* 16px */
--border-radius-full: 9999px;
--border-width-thin: 1px;
--border-width-medium: 2px;
--border-width-thick: 4px;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
/* Transitions */
--transition-fast: 150ms;
--transition-base: 300ms;
--transition-slow: 500ms;
--transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
/* Z-index */
--z-dropdown: 1000;
--z-sticky: 1020;
--z-fixed: 1030;
--z-modal: 1040;
--z-popover: 1050;
--z-tooltip: 1060;
/* Layout */
--container-max-width: 1200px;
--header-height: 60px;
--sidebar-width: 250px;
}
/* Component styles using design tokens */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--spacing-2) var(--spacing-4);
font-size: var(--font-size-md);
font-weight: var(--font-weight-medium);
line-height: var(--line-height-normal);
border-radius: var(--border-radius-md);
border: var(--border-width-thin) solid transparent;
transition: all var(--transition-fast) var(--transition-timing);
cursor: pointer;
}
.button--primary {
background-color: var(--color-primary);
color: white;
}
.button--primary:hover {
background-color: var(--color-primary-dark);
}
.button--secondary {
background-color: var(--color-secondary);
color: white;
}
.button--secondary:hover {
background-color: var(--color-secondary-dark);
}
.button--outline {
background-color: transparent;
border-color: var(--color-gray-300);
color: var(--color-gray-700);
}
.button--outline:hover {
background-color: var(--color-gray-100);
}
.button--large {
padding: var(--spacing-3) var(--spacing-6);
font-size: var(--font-size-lg);
}
.button--small {
padding: var(--spacing-1) var(--spacing-2);
font-size: var(--font-size-sm);
}
Responsive Design with Variables
/* Responsive design using CSS variables */
:root {
/* Base values (mobile) */
--container-padding: 1rem;
--grid-gap: 1rem;
--font-size-base: 16px;
--heading-scale: 1.2;
--columns: 1;
--sidebar-width: 100%;
--header-height: 50px;
}
/* Tablet */
@media (min-width: 768px) {
:root {
--container-padding: 2rem;
--grid-gap: 1.5rem;
--font-size-base: 17px;
--heading-scale: 1.3;
--columns: 2;
--sidebar-width: 250px;
--header-height: 60px;
}
}
/* Desktop */
@media (min-width: 1024px) {
:root {
--container-padding: 3rem;
--grid-gap: 2rem;
--font-size-base: 18px;
--heading-scale: 1.4;
--columns: 3;
--sidebar-width: 300px;
--header-height: 70px;
}
}
/* Large desktop */
@media (min-width: 1440px) {
:root {
--container-padding: 4rem;
--grid-gap: 2.5rem;
--font-size-base: 18px;
--heading-scale: 1.5;
--columns: 4;
--header-height: 80px;
}
}
/* Component styles using responsive variables */
.container {
max-width: 1400px;
margin: 0 auto;
padding: 0 var(--container-padding);
}
.grid {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: var(--grid-gap);
}
h1 {
font-size: calc(var(--font-size-base) * var(--heading-scale) * 2);
}
h2 {
font-size: calc(var(--font-size-base) * var(--heading-scale) * 1.5);
}
h3 {
font-size: calc(var(--font-size-base) * var(--heading-scale) * 1.17);
}
.layout {
display: grid;
grid-template-columns: var(--sidebar-width) 1fr;
gap: var(--grid-gap);
}
.header {
height: var(--header-height);
}
Component Theming
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Component Theming</title>
<style>
/* Base card component */
.card {
--card-bg: white;
--card-border: 1px solid #e0e0e0;
--card-shadow: 0 2px 4px rgba(0,0,0,0.1);
--card-title-color: #333;
--card-text-color: #666;
--card-accent: #3498db;
background: var(--card-bg);
border: var(--card-border);
border-radius: 8px;
padding: 20px;
box-shadow: var(--card-shadow);
transition: all 0.3s;
}
.card__title {
color: var(--card-title-color);
margin-bottom: 10px;
}
.card__text {
color: var(--card-text-color);
line-height: 1.6;
}
.card__button {
background: var(--card-accent);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
margin-top: 15px;
cursor: pointer;
transition: opacity 0.3s;
}
.card__button:hover {
opacity: 0.9;
}
/* Theme variations */
.card.dark {
--card-bg: #2d2d2d;
--card-border: 1px solid #404040;
--card-shadow: 0 4px 6px rgba(0,0,0,0.3);
--card-title-color: #fff;
--card-text-color: #b0b0b0;
--card-accent: #64b5f6;
}
.card.success {
--card-bg: #f0fff4;
--card-border: 1px solid #2ecc71;
--card-accent: #27ae60;
--card-title-color: #27ae60;
}
.card.warning {
--card-bg: #fff9f0;
--card-border: 1px solid #f39c12;
--card-accent: #e67e22;
--card-title-color: #e67e22;
}
.card.danger {
--card-bg: #fff5f5;
--card-border: 1px solid #e74c3c;
--card-accent: #c0392b;
--card-title-color: #c0392b;
}
/* Demo container */
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
max-width: 1200px;
margin: 40px auto;
padding: 20px;
}
</style>
</head>
<body>
<div class="demo-grid">
<div class="card">
<h3 class="card__title">Default Card</h3>
<p class="card__text">This card uses the default theme variables. All styles are controlled through CSS custom properties.</p>
<button class="card__button">Learn More</button>
</div>
<div class="card dark">
<h3 class="card__title">Dark Theme</h3>
<p class="card__text">The dark theme only changes the custom properties, keeping the same HTML structure.</p>
<button class="card__button">Learn More</button>
</div>
<div class="card success">
<h3 class="card__title">Success Theme</h3>
<p class="card__text">Perfect for success messages, confirmations, and positive feedback.</p>
<button class="card__button">Learn More</button>
</div>
<div class="card warning">
<h3 class="card__title">Warning Theme</h3>
<p class="card__text">Use for warnings, alerts, and messages that need attention.</p>
<button class="card__button">Learn More</button>
</div>
<div class="card danger">
<h3 class="card__title">Danger Theme</h3>
<p class="card__text">Ideal for errors, deletions, and destructive actions.</p>
<button class="card__button">Learn More</button>
</div>
<div class="card" style="--card-accent: #9b59b6; --card-title-color: #9b59b6;">
<h3 class="card__title">Custom Theme</h3>
<p class="card__text">You can even override variables inline for one-off customizations.</p>
<button class="card__button">Learn More</button>
</div>
</div>
</body>
</html>
Theming and Dark Mode
System Preference Detection
/* Automatic dark mode based on system preference */
:root {
/* Light theme (default) */
--bg-color: #ffffff;
--text-color: #333333;
--link-color: #3498db;
--border-color: #e0e0e0;
--card-bg: #f5f5f5;
--shadow-color: rgba(0,0,0,0.1);
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1a1a1a;
--text-color: #ffffff;
--link-color: #64b5f6;
--border-color: #404040;
--card-bg: #2d2d2d;
--shadow-color: rgba(0,0,0,0.3);
}
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s, color 0.3s;
}
a {
color: var(--link-color);
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
box-shadow: 0 2px 4px var(--shadow-color);
}
Complete Dark Mode Implementation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode with CSS Variables</title>
<style>
/* Theme variables */
:root {
--primary: #3498db;
--primary-light: #5faee3;
--primary-dark: #2980b9;
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--text-primary: #333333;
--text-secondary: #666666;
--text-muted: #999999;
--border-color: #e0e0e0;
--shadow-color: rgba(0,0,0,0.1);
--card-bg: #ffffff;
--header-bg: #ffffff;
--footer-bg: #f5f5f5;
--code-bg: #f5f5f5;
--table-stripe: #f9f9f9;
}
/* Dark theme */
[data-theme="dark"] {
--primary: #64b5f6;
--primary-light: #90caf9;
--primary-dark: #42a5f5;
--bg-primary: #1a1a1a;
--bg-secondary: #2d2d2d;
--text-primary: #ffffff;
--text-secondary: #b0b0b0;
--text-muted: #808080;
--border-color: #404040;
--shadow-color: rgba(0,0,0,0.3);
--card-bg: #2d2d2d;
--header-bg: #000000;
--footer-bg: #000000;
--code-bg: #2d2d2d;
--table-stripe: #252525;
}
/* High contrast dark theme */
[data-theme="dark-high-contrast"] {
--primary: #ffb74d;
--primary-light: #ffa726;
--primary-dark: #ff9800;
--bg-primary: #000000;
--bg-secondary: #1a1a1a;
--text-primary: #ffffff;
--text-secondary: #ffff00;
--text-muted: #ffa500;
--border-color: #ffffff;
--shadow-color: rgba(255,255,255,0.3);
--card-bg: #1a1a1a;
--header-bg: #000000;
--footer-bg: #000000;
--code-bg: #1a1a1a;
--table-stripe: #111111;
}
/* Base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
transition: background-color 0.3s, color 0.3s;
min-height: 100vh;
}
/* Header */
header {
background-color: var(--header-bg);
border-bottom: 1px solid var(--border-color);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 100;
transition: background-color 0.3s, border-color 0.3s;
}
.logo {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary);
}
.theme-controls {
display: flex;
gap: 0.5rem;
}
.theme-btn {
padding: 0.5rem 1rem;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
color: var(--text-primary);
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.theme-btn:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
/* Main content */
.container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 2rem;
}
/* Hero section */
.hero {
background: linear-gradient(135deg, var(--primary-light), var(--primary));
color: white;
padding: 4rem;
border-radius: 8px;
margin-bottom: 3rem;
text-align: center;
}
.hero h1 {
font-size: 3rem;
margin-bottom: 1rem;
}
.hero p {
font-size: 1.2rem;
opacity: 0.9;
}
/* Cards */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin: 3rem 0;
}
.card {
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 4px 6px var(--shadow-color);
transition: transform 0.3s, box-shadow 0.3s, background-color 0.3s, border-color 0.3s;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px var(--shadow-color);
}
.card h3 {
color: var(--primary);
margin-bottom: 1rem;
}
.card p {
color: var(--text-secondary);
}
/* Code block */
pre {
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
overflow-x: auto;
margin: 2rem 0;
}
code {
color: var(--text-primary);
font-family: 'Courier New', monospace;
}
/* Table */
table {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
}
th {
background: var(--primary);
color: white;
padding: 1rem;
text-align: left;
}
td {
padding: 1rem;
border-bottom: 1px solid var(--border-color);
}
tr:nth-child(even) {
background: var(--table-stripe);
}
/* Footer */
footer {
background: var(--footer-bg);
border-top: 1px solid var(--border-color);
padding: 2rem;
text-align: center;
color: var(--text-secondary);
transition: background-color 0.3s, border-color 0.3s;
}
/* Responsive */
@media (max-width: 768px) {
header {
flex-direction: column;
gap: 1rem;
}
.hero {
padding: 2rem;
}
.hero h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<header>
<div class="logo">🌓 ThemeSwitcher</div>
<div class="theme-controls">
<button class="theme-btn" data-theme="light">Light</button>
<button class="theme-btn" data-theme="dark">Dark</button>
<button class="theme-btn" data-theme="dark-high-contrast">High Contrast</button>
</div>
</header>
<div class="container">
<section class="hero">
<h1>CSS Variables Theming</h1>
<p>Experience seamless theme switching with CSS Custom Properties</p>
</section>
<div class="card-grid">
<div class="card">
<h3>Dynamic Updates</h3>
<p>All colors update in real-time without page reload. The transition is smooth and performant.</p>
</div>
<div class="card">
<h3>System Friendly</h3>
<p>Respects user's system preferences and saves your choice for future visits.</p>
</div>
<div class="card">
<h3>Accessible</h3>
<p>High contrast mode ensures readability for all users, including those with visual impairments.</p>
</div>
</div>
<h2>Code Example</h2>
<pre><code>:root {
--primary: #3498db;
--bg-primary: #ffffff;
--text-primary: #333333;
}
[data-theme="dark"] {
--primary: #64b5f6;
--bg-primary: #1a1a1a;
--text-primary: #ffffff;
}</code></pre>
<h2>Feature Comparison</h2>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Light</th>
<th>Dark</th>
<th>High Contrast</th>
</tr>
</thead>
<tbody>
<tr>
<td>Background</td>
<td>Light</td>
<td>Dark</td>
<td>Black</td>
</tr>
<tr>
<td>Text Contrast</td>
<td>High</td>
<td>High</td>
<td>Maximum</td>
</tr>
<tr>
<td>Eye Strain</td>
<td>Low</td>
<td>Lower</td>
<td>Customizable</td>
</tr>
<tr>
<td>Battery Saving</td>
<td>No</td>
<td>Yes (OLED)</td>
<td>Yes (OLED)</td>
</tr>
</tbody>
</table>
</div>
<footer>
<p>Built with CSS Custom Properties • All themes use the same HTML structure</p>
</footer>
<script>
// Theme switching with system preference detection and persistence
const html = document.documentElement;
const themeButtons = document.querySelectorAll('.theme-btn');
// Check for saved theme or system preference
function getPreferredTheme() {
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
return savedTheme;
}
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
return prefersDark ? 'dark' : 'light';
}
// Apply theme
function setTheme(theme) {
html.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}
// Set initial theme
setTheme(getPreferredTheme());
// Theme button click handlers
themeButtons.forEach(btn => {
btn.addEventListener('click', () => {
const theme = btn.getAttribute('data-theme');
setTheme(theme);
});
});
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light');
}
});
</script>
</body>
</html>
Responsive Design with Variables
Fluid Typography
/* Fluid typography with CSS variables */
:root {
--min-font-size: 16px;
--max-font-size: 24px;
--min-viewport: 320px;
--max-viewport: 1200px;
}
body {
font-size: var(--min-font-size);
}
@media screen and (min-width: 320px) {
body {
font-size: calc(
var(--min-font-size) + (var(--max-font-size) - var(--min-font-size)) *
((100vw - var(--min-viewport)) / (var(--max-viewport) - var(--min-viewport)))
);
}
}
@media screen and (min-width: 1200px) {
body {
font-size: var(--max-font-size);
}
}
/* Using clamp() for simpler fluid typography */
:root {
--fluid-typography: clamp(16px, 4vw, 24px);
}
body {
font-size: var(--fluid-typography);
}
Responsive Spacing System
/* Responsive spacing with variables */
:root {
/* Base spacing (mobile) */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
--space-2xl: 3rem;
/* Layout variables */
--container-padding: var(--space-md);
--grid-gap: var(--space-md);
--section-spacing: var(--space-xl);
}
/* Tablet */
@media (min-width: 768px) {
:root {
--space-xs: 0.5rem;
--space-sm: 0.75rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
--space-2xl: 4rem;
--container-padding: var(--space-lg);
--grid-gap: var(--space-lg);
--section-spacing: var(--space-2xl);
}
}
/* Desktop */
@media (min-width: 1024px) {
:root {
--space-xs: 0.75rem;
--space-sm: 1rem;
--space-md: 2rem;
--space-lg: 3rem;
--space-xl: 4rem;
--space-2xl: 5rem;
--container-padding: var(--space-xl);
--grid-gap: var(--space-xl);
--section-spacing: calc(var(--space-2xl) * 2);
}
}
/* Component styles using responsive spacing */
.container {
padding: 0 var(--container-padding);
}
.grid {
display: grid;
gap: var(--grid-gap);
}
.section {
margin-bottom: var(--section-spacing);
}
.card {
padding: var(--space-lg);
margin-bottom: var(--space-md);
}
.button {
padding: var(--space-sm) var(--space-lg);
}
Responsive Component Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Card Grid</title>
<style>
:root {
/* Base values (mobile) */
--container-padding: 1rem;
--grid-gap: 1rem;
--card-padding: 1rem;
--card-border-radius: 4px;
--font-size-h2: 1.5rem;
--font-size-h3: 1.2rem;
--font-size-body: 1rem;
--columns: 1;
}
/* Tablet */
@media (min-width: 768px) {
:root {
--container-padding: 2rem;
--grid-gap: 1.5rem;
--card-padding: 1.5rem;
--card-border-radius: 8px;
--font-size-h2: 2rem;
--font-size-h3: 1.5rem;
--font-size-body: 1rem;
--columns: 2;
}
}
/* Desktop */
@media (min-width: 1024px) {
:root {
--container-padding: 3rem;
--grid-gap: 2rem;
--card-padding: 2rem;
--card-border-radius: 12px;
--font-size-h2: 2.5rem;
--font-size-h3: 1.8rem;
--font-size-body: 1.1rem;
--columns: 3;
}
}
/* Large desktop */
@media (min-width: 1440px) {
:root {
--container-padding: 4rem;
--grid-gap: 2.5rem;
--card-padding: 2.5rem;
--card-border-radius: 16px;
--font-size-h2: 3rem;
--font-size-h3: 2rem;
--font-size-body: 1.2rem;
--columns: 4;
}
}
body {
font-family: -apple-system, sans-serif;
margin: 0;
background: #f5f5f5;
}
.container {
max-width: 1600px;
margin: 0 auto;
padding: var(--container-padding);
}
h2 {
font-size: var(--font-size-h2);
margin-bottom: calc(var(--grid-gap) * 2);
color: #333;
}
.grid {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: var(--grid-gap);
}
.card {
background: white;
padding: var(--card-padding);
border-radius: var(--card-border-radius);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.card h3 {
font-size: var(--font-size-h3);
margin-bottom: var(--grid-gap);
color: #2c3e50;
}
.card p {
font-size: var(--font-size-body);
color: #666;
line-height: 1.6;
margin-bottom: var(--grid-gap);
}
.card-meta {
display: flex;
justify-content: space-between;
color: #999;
font-size: calc(var(--font-size-body) * 0.9);
border-top: 1px solid #eee;
padding-top: var(--grid-gap);
}
</style>
</head>
<body>
<div class="container">
<h2>Responsive Card Grid</h2>
<div class="grid">
<div class="card">
<h3>Card Title 1</h3>
<p>This card automatically adjusts its padding, font sizes, and layout based on screen size using CSS variables.</p>
<div class="card-meta">
<span>👁️ 2.5k views</span>
<span>❤️ 1.2k likes</span>
</div>
</div>
<div class="card">
<h3>Card Title 2</h3>
<p>The number of columns changes from 1 to 4 based on viewport width, all controlled by variables.</p>
<div class="card-meta">
<span>👁️ 1.8k views</span>
<span>❤️ 890 likes</span>
</div>
</div>
<div class="card">
<h3>Card Title 3</h3>
<p>Gaps between cards scale proportionally, creating a harmonious layout at any screen size.</p>
<div class="card-meta">
<span>👁️ 3.1k views</span>
<span>❤️ 1.5k likes</span>
</div>
</div>
<div class="card">
<h3>Card Title 4</h3>
<p>Border radius increases on larger screens, matching the overall design scale.</p>
<div class="card-meta">
<span>👁️ 980 views</span>
<span>❤️ 420 likes</span>
</div>
</div>
<div class="card">
<h3>Card Title 5</h3>
<p>All these values are defined in :root and updated via media queries, making maintenance easy.</p>
<div class="card-meta">
<span>👁️ 2.2k views</span>
<span>❤️ 1.1k likes</span>
</div>
</div>
<div class="card">
<h3>Card Title 6</h3>
<p>This approach ensures consistent scaling across all components, not just cards.</p>
<div class="card-meta">
<span>👁️ 1.5k views</span>
<span>❤️ 670 likes</span>
</div>
</div>
</div>
</div>
</body>
</html>
Performance Considerations
Variable Resolution Performance
/* CSS variable resolution happens at computed-value time */
:root {
--primary: #3498db;
--spacing: 8px;
}
.element {
/* Variables are resolved when the element is styled */
color: var(--primary);
margin: calc(var(--spacing) * 2);
}
/* Performance best practices */
/* 1. Use variables for values that change frequently */
.theme-aware {
background-color: var(--bg-color); /* Good for theming */
transition: background-color 0.3s; /* Smooth transitions */
}
/* 2. Avoid variables in animations if possible */
@keyframes bad-practice {
to {
left: var(--target-position); /* May cause layout recalculations */
}
}
@keyframes good-practice {
to {
transform: translateX(var(--target-position)); /* Better performance */
}
}
/* 3. Group variable usage for better readability */
.card {
--card-padding: 20px;
--card-bg: white;
--card-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: var(--card-padding);
background: var(--card-bg);
box-shadow: var(--card-shadow);
}
Optimizing Variable Updates
// Efficient variable updates
const root = document.documentElement;
// Bad - multiple style recalculations
root.style.setProperty('--color-1', '#ff0000');
root.style.setProperty('--color-2', '#00ff00');
root.style.setProperty('--color-3', '#0000ff');
// Good - batch updates
requestAnimationFrame(() => {
root.style.setProperty('--color-1', '#ff0000');
root.style.setProperty('--color-2', '#00ff00');
root.style.setProperty('--color-3', '#0000ff');
});
// Better - use CSS class for theme changes
html.setAttribute('data-theme', 'dark'); // Single change triggers all variable updates
// Use will-change for animated variables
.animated-element {
will-change: background-color, color;
transition: background-color 0.3s, color 0.3s;
}
Browser Support and Fallbacks
Feature Detection
/* Using @supports for fallback */
.element {
background-color: #3498db; /* Fallback */
background-color: var(--primary-color, #3498db); /* Modern with fallback */
}
@supports (--css: variables) {
.element {
background-color: var(--primary-color);
}
}
/* For older browsers */
.no-cssvars .element {
background-color: #3498db; /* Traditional approach */
}
Fallback Strategies
/* Multiple fallback levels */
.element {
/* Level 1: Very old browsers */
color: #333;
/* Level 2: Browsers with partial support */
color: var(--text-color, #333);
/* Level 3: Modern browsers with full support */
@supports (--css: variables) {
color: var(--text-color);
}
}
/* Complex fallback example */
.button {
/* Fallback for all browsers */
background: #3498db;
border-radius: 4px;
padding: 8px 16px;
/* Modern browsers with variables */
background: var(--button-bg, #3498db);
border-radius: var(--border-radius, 4px);
padding: var(--button-padding, 8px 16px);
}
/* Using JavaScript to check support */
<script>
if (window.CSS && CSS.supports('--css', 'variables')) {
document.documentElement.classList.add('cssvars');
} else {
document.documentElement.classList.add('no-cssvars');
}
</script>
Complete Fallback Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Variables Fallback</title>
<style>
/* Base styles for all browsers */
.card {
border: 1px solid #ddd;
padding: 20px;
border-radius: 4px;
}
/* CSS variables for modern browsers */
@supports (--css: variables) {
:root {
--card-bg: white;
--card-border: 1px solid #3498db;
--card-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card {
background: var(--card-bg);
border: var(--card-border);
box-shadow: var(--card-shadow);
transition: all 0.3s;
}
.card:hover {
--card-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
}
/* JavaScript detection */
.cssvars .card {
/* Variables already defined above */
}
.no-cssvars .card {
background: white;
border: 1px solid #3498db;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div class="card">
<h3>Card Title</h3>
<p>This card works in all browsers, with enhanced features in modern ones.</p>
</div>
<script>
// Feature detection and class toggling
if (window.CSS && CSS.supports('--css', 'variables')) {
document.documentElement.classList.add('cssvars');
} else {
document.documentElement.classList.add('no-cssvars');
}
</script>
</body>
</html>
Advanced Techniques
Computed Variables
/* Variables that compute other variables */
:root {
--base-size: 16px;
--scale-ratio: 1.25;
--text-xs: calc(var(--base-size) / var(--scale-ratio) / var(--scale-ratio));
--text-sm: calc(var(--base-size) / var(--scale-ratio));
--text-base: var(--base-size);
--text-lg: calc(var(--base-size) * var(--scale-ratio));
--text-xl: calc(var(--base-size) * var(--scale-ratio) * var(--scale-ratio));
--text-2xl: calc(var(--base-size) * var(--scale-ratio) * var(--scale-ratio) * var(--scale-ratio));
}
/* Conditional variables */
.button {
--button-bg: var(--primary, #3498db);
--button-text: white;
}
.button--secondary {
--button-bg: var(--secondary, #2ecc71);
}
/* Variables in calc() */
:root {
--columns: 3;
--gap: 20px;
}
.grid {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: var(--gap);
/* Calculated total width */
width: calc((100% - (var(--columns) - 1) * var(--gap)) / var(--columns) * var(--columns));
}
CSS Variables with @property
/* Registered custom properties with types */
@property --primary-color {
syntax: '<color>';
inherits: true;
initial-value: #3498db;
}
@property --spacing {
syntax: '<length>';
inherits: true;
initial-value: 8px;
}
@property --rotation {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
/* Using registered properties */
:root {
--primary-color: #3498db; /* Must be a valid color */
--spacing: 16px; /* Must be a valid length */
--rotation: 45deg; /* Must be a valid angle */
}
.element {
background-color: var(--primary-color);
margin: var(--spacing);
transform: rotate(var(--rotation));
}
Dynamic Color Manipulation
/* Color manipulation with variables */
:root {
--hue: 200;
--saturation: 70%;
--lightness: 50%;
--primary: hsl(var(--hue), var(--saturation), var(--lightness));
--primary-light: hsl(var(--hue), var(--saturation), calc(var(--lightness) + 15%));
--primary-dark: hsl(var(--hue), var(--saturation), calc(var(--lightness) - 15%));
--primary-transparent: hsla(var(--hue), var(--saturation), var(--lightness), 0.5);
}
/* Complementary colors */
:root {
--base-hue: 200;
--complement-hue: calc(var(--base-hue) + 180);
--base-color: hsl(var(--base-hue), 70%, 50%);
--complement-color: hsl(var(--complement-hue), 70%, 50%);
}
/* Adjustable theme */
.theme {
--hue: 200;
--primary: hsl(var(--hue), 70%, 50%);
--secondary: hsl(calc(var(--hue) + 60), 70%, 50%);
--accent: hsl(calc(var(--hue) - 60), 70%, 50%);
}
.theme.blue {
--hue: 200;
}
.theme.green {
--hue: 120;
}
.theme.purple {
--hue: 280;
}
CSS Variables with Keyframe Animations
/* Animated variables */
:root {
--animation-speed: 2s;
--animation-delay: 0s;
--move-distance: 100px;
}
@keyframes slide {
0% {
transform: translateX(0);
}
100% {
transform: translateX(var(--move-distance));
}
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(var(--pulse-scale, 1.2));
opacity: var(--pulse-opacity, 0.8);
}
100% {
transform: scale(1);
opacity: 1;
}
}
.animated-element {
animation: slide var(--animation-speed) ease infinite;
}
.pulse-element {
--pulse-scale: 1.1;
--pulse-opacity: 0.9;
animation: pulse var(--animation-speed) ease infinite;
}
.pulse-element.intense {
--pulse-scale: 1.5;
--pulse-opacity: 0.5;
}
Best Practices
Organization and Structure
/* 1. Organize variables logically */
:root {
/* Colors */
--color-primary: #3498db;
--color-secondary: #2ecc71;
--color-danger: #e74c3c;
--color-warning: #f39c12;
/* Typography */
--font-family-base: -apple-system, sans-serif;
--font-size-base: 16px;
--font-size-lg: 18px;
--font-size-sm: 14px;
/* Spacing */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
/* Borders */
--border-radius: 4px;
--border-width: 1px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
/* Transitions */
--transition-fast: 150ms;
--transition-base: 300ms;
--transition-slow: 500ms;
}
/* 2. Use meaningful names */
/* Good */
--button-primary-bg: #3498db;
--button-primary-text: white;
/* Avoid */
--blue: #3498db;
--white: white;
/* 3. Group by component */
.button {
--button-padding: 8px 16px;
--button-radius: 4px;
--button-font: inherit;
}
/* 4. Provide fallbacks */
.element {
color: var(--text-color, #333);
background: var(--bg-color, white);
}
Naming Conventions
/* 1. Use kebab-case (recommended) */
:root {
--primary-color: #3498db;
--font-size-large: 24px;
--spacing-unit: 8px;
}
/* 2. Be specific about purpose */
:root {
/* ❌ Vague */
--blue: #3498db;
--red: #e74c3c;
/* ✅ Descriptive */
--color-primary: #3498db;
--color-danger: #e74c3c;
}
/* 3. Use contextual prefixes */
:root {
/* Typography */
--text-primary: #333;
--text-secondary: #666;
--text-muted: #999;
/* Backgrounds */
--bg-primary: white;
--bg-secondary: #f5f5f5;
/* Borders */
--border-light: #e0e0e0;
--border-dark: #ccc;
}
/* 4. Component-specific naming */
.alert {
--alert-bg: #f8d7da;
--alert-border: #f5c6cb;
--alert-text: #721c24;
}
.alert.success {
--alert-bg: #d4edda;
--alert-border: #c3e6cb;
--alert-text: #155724;
}
Common Pitfalls to Avoid
/* Pitfall 1: Not providing fallbacks */
.element {
/* Bad - may break if variable not defined */
color: var(--undefined-var);
/* Good - has fallback */
color: var(--undefined-var, #333);
}
/* Pitfall 2: Overusing global scope */
:root {
/* Good - truly global values */
--brand-color: #3498db;
/* Avoid - component-specific variables */
--card-padding: 20px; /* Should be in .card scope */
}
/* Pitfall 3: Complex calculations without fallbacks */
.element {
/* Bad - will fail if --base not defined */
width: calc(100% - var(--base));
/* Good - has fallback */
width: calc(100% - var(--base, 20px));
}
/* Pitfall 4: Not considering inheritance */
.parent {
--color: blue;
}
.child {
/* Works, but consider if this is intended */
color: var(--color);
}
/* Pitfall 5: Using variables where they're not needed */
:root {
--one-pixel: 1px; /* Overkill */
--zero: 0; /* Unnecessary */
}
.element {
border-width: var(--one-pixel); /* Just use 1px */
}
/* Pitfall 6: Not documenting complex variables */
/* Bad - what's this for? */
:root {
--c1: #3498db;
--c2: #2ecc71;
--c3: #e74c3c;
}
/* Good - clear purpose */
:root {
--color-primary: #3498db;
--color-success: #2ecc71;
--color-danger: #e74c3c;
}
Performance Best Practices
/* 1. Limit variable scope when possible */
.card {
/* Good - only affects this component */
--card-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* 2. Avoid unnecessary variable updates */
@media (prefers-reduced-motion: reduce) {
* {
/* Better to disable animations completely */
animation-duration: 0.01ms !important;
}
}
/* 3. Use will-change for animated variables */
.animated {
will-change: transform, opacity;
transform: translateX(var(--offset, 0));
opacity: var(--opacity, 1);
}
/* 4. Batch related variables */
:root {
/* Group theme variables */
--theme-primary: #3498db;
--theme-secondary: #2ecc71;
--theme-bg: white;
--theme-text: #333;
}
/* 5. Avoid recalculations in animations */
@keyframes move {
/* Bad - recalculates on each frame */
to {
left: calc(100% - var(--offset));
}
/* Better - use transforms */
to {
transform: translateX(calc(100% - var(--offset)));
}
}
Real-World Examples
Example 1: Design System with Variables
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Design System with CSS Variables</title>
<style>
/* Design tokens */
:root {
/* Colors */
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
--color-primary-200: #bfdbfe;
--color-primary-300: #93c5fd;
--color-primary-400: #60a5fa;
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
--color-primary-800: #1e40af;
--color-primary-900: #1e3a8a;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
--color-info: #3b82f6;
/* Typography */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-serif: Georgia, 'Times New Roman', serif;
--font-mono: 'SF Mono', Monaco, Consolas, monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
--text-4xl: 2.25rem;
--font-light: 300;
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
/* Spacing */
--spacing-0: 0;
--spacing-1: 0.25rem;
--spacing-2: 0.5rem;
--spacing-3: 0.75rem;
--spacing-4: 1rem;
--spacing-5: 1.25rem;
--spacing-6: 1.5rem;
--spacing-8: 2rem;
--spacing-10: 2.5rem;
--spacing-12: 3rem;
--spacing-16: 4rem;
/* Borders */
--radius-none: 0;
--radius-sm: 0.125rem;
--radius-md: 0.25rem;
--radius-lg: 0.375rem;
--radius-xl: 0.5rem;
--radius-2xl: 1rem;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
/* Transitions */
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-base: 300ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1);
/* Layout */
--container-max-width: 1280px;
--header-height: 4rem;
--sidebar-width: 16rem;
}
/* Base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-sans);
color: var(--color-gray-900);
background: var(--color-gray-50);
line-height: 1.5;
}
/* Container */
.container {
max-width: var(--container-max-width);
margin: 0 auto;
padding: 0 var(--spacing-4);
}
/* Header */
.header {
background: white;
border-bottom: 1px solid var(--color-gray-200);
height: var(--header-height);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
}
.logo {
font-size: var(--text-xl);
font-weight: var(--font-bold);
color: var(--color-primary-600);
}
.nav {
display: flex;
gap: var(--spacing-6);
}
.nav-link {
color: var(--color-gray-600);
text-decoration: none;
font-size: var(--text-base);
font-weight: var(--font-medium);
transition: color var(--transition-fast);
}
.nav-link:hover {
color: var(--color-primary-600);
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--spacing-2) var(--spacing-4);
font-size: var(--text-base);
font-weight: var(--font-medium);
line-height: 1.5;
border-radius: var(--radius-md);
border: 1px solid transparent;
cursor: pointer;
transition: all var(--transition-fast);
text-decoration: none;
}
.btn-primary {
background: var(--color-primary-600);
color: white;
}
.btn-primary:hover {
background: var(--color-primary-700);
}
.btn-secondary {
background: white;
border-color: var(--color-gray-300);
color: var(--color-gray-700);
}
.btn-secondary:hover {
background: var(--color-gray-50);
border-color: var(--color-gray-400);
}
.btn-danger {
background: var(--color-danger);
color: white;
}
.btn-danger:hover {
background: #dc2626;
}
.btn-sm {
padding: var(--spacing-1) var(--spacing-3);
font-size: var(--text-sm);
}
.btn-lg {
padding: var(--spacing-3) var(--spacing-6);
font-size: var(--text-lg);
}
/* Cards */
.card {
background: white;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
padding: var(--spacing-6);
transition: box-shadow var(--transition-base);
}
.card:hover {
box-shadow: var(--shadow-md);
}
.card-header {
margin-bottom: var(--spacing-4);
}
.card-title {
font-size: var(--text-xl);
font-weight: var(--font-semibold);
color: var(--color-gray-900);
}
.card-subtitle {
font-size: var(--text-sm);
color: var(--color-gray-500);
margin-top: var(--spacing-1);
}
.card-content {
color: var(--color-gray-600);
}
.card-footer {
margin-top: var(--spacing-6);
padding-top: var(--spacing-4);
border-top: 1px solid var(--color-gray-200);
}
/* Alerts */
.alert {
padding: var(--spacing-4);
border-radius: var(--radius-md);
margin-bottom: var(--spacing-4);
border-left: 4px solid transparent;
}
.alert-info {
background: var(--color-primary-50);
border-left-color: var(--color-primary-600);
color: var(--color-primary-800);
}
.alert-success {
background: #ecfdf5;
border-left-color: var(--color-success);
color: #065f46;
}
.alert-warning {
background: #fffbeb;
border-left-color: var(--color-warning);
color: #92400e;
}
.alert-danger {
background: #fef2f2;
border-left-color: var(--color-danger);
color: #991b1b;
}
/* Forms */
.form-group {
margin-bottom: var(--spacing-4);
}
.form-label {
display: block;
margin-bottom: var(--spacing-1);
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--color-gray-700);
}
.form-input {
width: 100%;
padding: var(--spacing-2) var(--spacing-3);
font-size: var(--text-base);
border: 1px solid var(--color-gray-300);
border-radius: var(--radius-md);
transition: border-color var(--transition-fast);
}
.form-input:focus {
outline: none;
border-color: var(--color-primary-500);
box-shadow: 0 0 0 3px var(--color-primary-100);
}
/* Grid */
.grid {
display: grid;
gap: var(--spacing-6);
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
/* Utilities */
.text-center { text-align: center; }
.text-sm { font-size: var(--text-sm); }
.text-lg { font-size: var(--text-lg); }
.font-bold { font-weight: var(--font-bold); }
.mt-4 { margin-top: var(--spacing-4); }
.mb-4 { margin-bottom: var(--spacing-4); }
.p-4 { padding: var(--spacing-4); }
</style>
</head>
<body>
<header class="header">
<div class="container header-content">
<div class="logo">DesignSystem</div>
<nav class="nav">
<a href="#" class="nav-link">Home</a>
<a href="#" class="nav-link">Components</a>
<a href="#" class="nav-link">Documentation</a>
<a href="#" class="nav-link">Examples</a>
</nav>
</div>
</header>
<main class="container">
<div class="grid" style="margin: var(--spacing-8) 0;">
<div class="card">
<div class="card-header">
<h3 class="card-title">Component Library</h3>
<p class="card-subtitle">Built with CSS variables</p>
</div>
<div class="card-content">
<p>All components use design tokens defined in :root. Change one value, update everywhere.</p>
</div>
<div class="card-footer">
<button class="btn btn-primary">Learn More</button>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Theme Support</h3>
<p class="card-subtitle">Dark mode ready</p>
</div>
<div class="card-content">
<p>Switch themes by updating a few CSS variables. Perfect for dark mode support.</p>
</div>
<div class="card-footer">
<button class="btn btn-secondary">Try Demo</button>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Responsive Design</h3>
<p class="card-subtitle">Mobile-first approach</p>
</div>
<div class="card-content">
<p>All spacing and typography scales automatically using CSS variables and media queries.</p>
</div>
<div class="card-footer">
<button class="btn btn-primary">View Examples</button>
</div>
</div>
</div>
<div class="alert alert-info">
<strong>Info:</strong> This entire design system is powered by CSS custom properties. Try inspecting the variables!
</div>
<div class="alert alert-success">
<strong>Success:</strong> Your changes have been saved successfully.
</div>
<div class="alert alert-warning">
<strong>Warning:</strong> Please check your input before submitting.
</div>
<div class="alert alert-danger">
<strong>Error:</strong> An error occurred while processing your request.
</div>
<div style="margin: var(--spacing-8) 0;">
<form>
<div class="form-group">
<label class="form-label" for="name">Name</label>
<input type="text" id="name" class="form-input" placeholder="Enter your name">
</div>
<div class="form-group">
<label class="form-label" for="email">Email</label>
<input type="email" id="email" class="form-input" placeholder="Enter your email">
</div>
<div style="display: flex; gap: var(--spacing-2);">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-secondary">Cancel</button>
</div>
</form>
</div>
</main>
</body>
</html>
Example 2: Interactive Dashboard with Variables
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Dashboard</title>
<style>
:root {
/* Dashboard theme */
--bg-primary: #f0f2f5;
--bg-secondary: white;
--text-primary: #1a1a1a;
--text-secondary: #666;
--accent: #4361ee;
--accent-light: #e0e7ff;
--danger: #ef4444;
--warning: #f59e0b;
--success: #10b981;
--sidebar-width: 250px;
--header-height: 60px;
--card-radius: 12px;
--transition-speed: 0.3s;
}
[data-theme="dark"] {
--bg-primary: #1a1a1a;
--bg-secondary: #2d2d2d;
--text-primary: #ffffff;
--text-secondary: #b0b0b0;
--accent: #64b5f6;
--accent-light: #334155;
--danger: #f87171;
--warning: #fbbf24;
--success: #4ade80;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
transition: background-color var(--transition-speed), color var(--transition-speed);
}
.dashboard {
display: grid;
grid-template-columns: var(--sidebar-width) 1fr;
min-height: 100vh;
}
/* Sidebar */
.sidebar {
background: var(--bg-secondary);
border-right: 1px solid var(--text-secondary);
padding: 1.5rem;
transition: background-color var(--transition-speed);
}
.logo {
font-size: 1.5rem;
font-weight: bold;
color: var(--accent);
margin-bottom: 2rem;
}
.nav-item {
padding: 0.75rem 1rem;
margin-bottom: 0.5rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
color: var(--text-secondary);
}
.nav-item:hover,
.nav-item.active {
background: var(--accent-light);
color: var(--accent);
}
/* Main content */
.main {
padding: 1.5rem;
}
/* Header */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.header h1 {
font-size: 2rem;
color: var(--text-primary);
}
.theme-toggle {
background: var(--bg-secondary);
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
cursor: pointer;
color: var(--text-primary);
border: 1px solid var(--text-secondary);
}
/* Stats grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: var(--bg-secondary);
border-radius: var(--card-radius);
padding: 1.5rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: all 0.3s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.stat-title {
color: var(--text-secondary);
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
color: var(--accent);
}
.stat-change {
font-size: 0.9rem;
color: var(--success);
}
/* Charts grid */
.charts-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 1.5rem;
margin-bottom: 2rem;
}
.chart-card {
background: var(--bg-secondary);
border-radius: var(--card-radius);
padding: 1.5rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.chart-placeholder {
height: 300px;
display: flex;
align-items: flex-end;
gap: 1rem;
padding: 1rem 0;
}
.bar {
flex: 1;
background: linear-gradient(to top, var(--accent), var(--accent-light));
border-radius: 4px 4px 0 0;
transition: height 0.3s;
height: calc(var(--height) * 2px);
}
/* Table */
.table-card {
background: var(--bg-secondary);
border-radius: var(--card-radius);
padding: 1.5rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
text-align: left;
padding: 1rem;
color: var(--text-secondary);
font-weight: normal;
border-bottom: 1px solid var(--text-secondary);
}
td {
padding: 1rem;
border-bottom: 1px solid var(--text-secondary);
color: var(--text-primary);
}
.badge {
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.85rem;
background: var(--accent-light);
color: var(--accent);
}
.badge.success {
background: #d1fae5;
color: var(--success);
}
.badge.warning {
background: #fef3c7;
color: var(--warning);
}
.badge.danger {
background: #fee2e2;
color: var(--danger);
}
/* Responsive */
@media (max-width: 1024px) {
.dashboard {
grid-template-columns: 1fr;
}
.sidebar {
display: none; /* Would need mobile menu */
}
.charts-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="dashboard">
<aside class="sidebar">
<div class="logo">📊 Dashboard</div>
<nav>
<div class="nav-item active">🏠 Overview</div>
<div class="nav-item">📈 Analytics</div>
<div class="nav-item">👥 Users</div>
<div class="nav-item">📦 Products</div>
<div class="nav-item">⚙️ Settings</div>
</nav>
</aside>
<main class="main">
<header class="header">
<h1>Dashboard Overview</h1>
<button class="theme-toggle" onclick="toggleTheme()">
Toggle Dark Mode
</button>
</header>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-title">Total Revenue</div>
<div class="stat-value">$124,563</div>
<div class="stat-change">↑ 12.5% from last month</div>
</div>
<div class="stat-card">
<div class="stat-title">Active Users</div>
<div class="stat-value">2,345</div>
<div class="stat-change">↑ 8.2% from last month</div>
</div>
<div class="stat-card">
<div class="stat-title">Conversion Rate</div>
<div class="stat-value">3.6%</div>
<div class="stat-change">↑ 1.2% from last month</div>
</div>
<div class="stat-card">
<div class="stat-title">Avg. Session</div>
<div class="stat-value">4m 32s</div>
<div class="stat-change">↓ 12s from last month</div>
</div>
</div>
<div class="charts-grid">
<div class="chart-card">
<h3 style="margin-bottom: 1rem;">Revenue Overview</h3>
<div class="chart-placeholder">
<div class="bar" style="--height: 60"></div>
<div class="bar" style="--height: 85"></div>
<div class="bar" style="--height: 45"></div>
<div class="bar" style="--height: 70"></div>
<div class="bar" style="--height: 90"></div>
<div class="bar" style="--height: 55"></div>
<div class="bar" style="--height: 75"></div>
</div>
</div>
<div class="chart-card">
<h3 style="margin-bottom: 1rem;">Traffic Sources</h3>
<div style="display: flex; flex-direction: column; gap: 1rem;">
<div>Direct: 45%</div>
<div>Organic: 30%</div>
<div>Social: 15%</div>
<div>Referral: 10%</div>
</div>
</div>
</div>
<div class="table-card">
<h3 style="margin-bottom: 1rem;">Recent Orders</h3>
<table>
<thead>
<tr>
<th>Order ID</th>
<th>Customer</th>
<th>Product</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>#12345</td>
<td>John Doe</td>
<td>Product A</td>
<td><span class="badge success">Completed</span></td>
<td>$129.99</td>
</tr>
<tr>
<td>#12346</td>
<td>Jane Smith</td>
<td>Product B</td>
<td><span class="badge warning">Pending</span></td>
<td>$89.99</td>
</tr>
<tr>
<td>#12347</td>
<td>Bob Johnson</td>
<td>Product C</td>
<td><span class="badge danger">Cancelled</span></td>
<td>$199.99</td>
</tr>
<tr>
<td>#12348</td>
<td>Alice Brown</td>
<td>Product A</td>
<td><span class="badge success">Completed</span></td>
<td>$129.99</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
<script>
function toggleTheme() {
const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme');
if (currentTheme === 'dark') {
html.removeAttribute('data-theme');
} else {
html.setAttribute('data-theme', 'dark');
}
}
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
const html = document.documentElement;
if (!localStorage.getItem('theme')) {
if (e.matches) {
html.setAttribute('data-theme', 'dark');
} else {
html.removeAttribute('data-theme');
}
}
});
</script>
</body>
</html>
Conclusion
CSS Custom Properties (Variables) have revolutionized how we write and maintain CSS:
Key Takeaways
- Dynamic Styling: Variables can be updated at runtime with JavaScript
- Maintainability: Change one value, update everywhere
- Theming: Easy implementation of dark mode and theme switching
- Cascade Friendly: Variables respect the CSS cascade
- Scoped: Can be global or component-specific
- Responsive: Media queries can update variables
- Performance: Efficient updates with minimal overhead
When to Use CSS Variables
✅ Global design tokens (colors, spacing, typography)
✅ Theme switching (dark/light modes)
✅ Component variants
✅ Responsive scaling
✅ Dynamic updates with JavaScript
✅ Design systems
When to Consider Alternatives
❌ Very old browser support needed (use preprocessor variables)
❌ Simple static values (Sass/Less variables might be simpler)
❌ Values that never change (regular CSS is fine)
Best Practices Summary
- Use meaningful names with consistent naming conventions
- Provide fallbacks for robustness
- Scope appropriately (global vs component)
- Document complex variables
HTML & CSS Learning Guides, Exercises, Projects, Design Systems, Accessibility & Performance (Related to HTML & CSS Development)
HTML & CSS Quiz – Comprehensive Assessment:
This quiz evaluates core knowledge of HTML and CSS including structure, styling, layout, forms, and responsive design. It is used to test practical understanding of front-end fundamentals and identify skill levels in web development.
Read more: https://macronepal.com/bash/html-and-css-quiz-comprehensive-assessment/
Complete Guide to HTML & CSS Tooling & Automation:
This guide explains tools and automation workflows used in modern web development, such as preprocessors, build tools, and task runners that improve efficiency in HTML and CSS projects.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-tooling-automation/
Complete HTML & CSS Exercises:
A collection of practical exercises designed to strengthen HTML and CSS skills through hands-on coding tasks, covering layout, styling, and responsive design concepts.
Read more: https://macronepal.com/bash/complete-html-and-css-exercises/
Complete HTML & CSS Landing Page Project:
This project focuses on building a full landing page using HTML and CSS, helping learners understand real-world website structure, layout design, and UI development.
Read more: https://macronepal.com/bash/complete-html-css-landing-page-project/
HTML & CSS Debugging and Testing Guide:
This guide teaches how to identify and fix errors in HTML and CSS code, along with testing techniques to ensure websites work correctly across browsers.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-debugging-testing/
HTML & CSS Design Systems Guide:
A design system approach helps maintain consistency in UI development using reusable components, styles, and patterns across web projects.
Read more: https://macronepal.com/html-and-css/complete-guide-to-html-and-css-design-systems/
HTML & CSS Accessibility (A11y) Guide:
This guide focuses on making websites accessible for all users, including proper semantic HTML, keyboard navigation, alt text, and screen reader support.
Read more: https://macronepal.com/bash/complete-guide-to-html-css-accessibility-a11y/
HTML & CSS Performance Guide:
This topic explains optimization techniques such as reducing file size, improving loading speed, and writing efficient HTML and CSS for better website performance.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-performance/
HTML & CSS Design Systems (Advanced Overview):
Design systems help developers maintain scalable and consistent UI components across large projects using structured HTML and reusable CSS patterns.
Read more: https://macronepal.com/html-and-css/complete-guide-to-html-and-css-design-systems/