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/