Complete Guide to HTML and CSS Custom Properties (CSS Variables)

Table of Contents

  1. Introduction to CSS Custom Properties
  2. Basic Syntax and Usage
  3. Variable Scope and Inheritance
  4. The Cascade with Custom Properties
  5. Dynamic Updates with JavaScript
  6. Practical Applications
  7. Theming and Dark Mode
  8. Responsive Design with Variables
  9. Performance Considerations
  10. Browser Support and Fallbacks
  11. Advanced Techniques
  12. Best Practices
  13. 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

  1. Dynamic Styling: Variables can be updated at runtime with JavaScript
  2. Maintainability: Change one value, update everywhere
  3. Theming: Easy implementation of dark mode and theme switching
  4. Cascade Friendly: Variables respect the CSS cascade
  5. Scoped: Can be global or component-specific
  6. Responsive: Media queries can update variables
  7. 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

  1. Use meaningful names with consistent naming conventions
  2. Provide fallbacks for robustness
  3. Scope appropriately (global vs component)
  4. 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/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper