Introduction to CSS Variables

CSS custom properties (variables) allow you to store values that can be reused throughout your stylesheet. They make CSS more maintainable, enable theming, and can be updated with JavaScript.

Basic CSS Variables



css

/* Define variables in :root for global scope */
:root {
  --primary-color: #3498db;
  --secondary-color: #e74c3c;
  --font-size-base: 16px;
  --spacing-unit: 8px;
  --border-radius: 4px;
}

/* Use variables with var() function */
.button {
  background: var(--primary-color);
  color: white;
  font-size: var(--font-size-base);
  padding: var(--spacing-unit) calc(var(--spacing-unit) * 2);
  border-radius: var(--border-radius);
  border: none;
  cursor: pointer;
}

.button:hover {
  background: var(--secondary-color);
}

Variable Fallbacks



css

/* Provide fallback values */
.element {
  color: var(--text-color, #333); /* Falls back to #333 if --text-color is undefined */
  background: var(--bg-color, var(--fallback-bg, white)); /* Multiple fallbacks */
}

Local Scope Variables



css

/* Variables can be scoped to specific elements */
.card {
  --card-padding: 20px;
  --card-bg: #f9f9f9;
  
  background: var(--card-bg);
  padding: var(--card-padding);
}

.card.featured {
  --card-bg: #e8f4f8; /* Override for featured cards */
  --card-padding: 30px;
}

The calc() Function

The calc() function performs calculations with mixed units:



css

/* Basic calculations */
.container {
  width: calc(100% - 40px); /* Full width minus padding */
  height: calc(100vh - 60px); /* Full height minus header */
  margin: calc(var(--spacing-unit) * 2) auto;
}

/* Complex calculations */
.grid-item {
  width: calc((100% - 40px) / 3); /* 3 columns with gaps */
  margin-right: calc(var(--gap) / 2);
}

/* Math operations in calc() */
.element {
  font-size: calc(16px + 2vw); /* Responsive font size */
  padding: calc(var(--base-padding) * 1.5);
  top: calc(50% - var(--element-height) / 2); /* Center vertically */
}

Practical Examples

Color System with Variables



css

:root {
  /* Base colors */
  --color-blue: #3498db;
  --color-red: #e74c3c;
  --color-green: #2ecc71;
  
  /* Semantic colors using base colors */
  --color-primary: var(--color-blue);
  --color-danger: var(--color-red);
  --color-success: var(--color-green);
  
  /* Variants */
  --color-primary-light: #5dade2;
  --color-primary-dark: #2980b9;
}

.btn-primary { background: var(--color-primary); }
.btn-danger { background: var(--color-danger); }
.text-success { color: var(--color-success); }

Spacing System



css

:root {
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 16px;
  --space-4: 24px;
  --space-5: 32px;
  --space-6: 48px;
}

.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.p-3 { padding: var(--space-3); }
.gap-4 { gap: var(--space-4); }

Responsive Typography



css

:root {
  --font-scale: 1.2;
  --font-size-sm: calc(var(--font-size-base) / var(--font-scale));
  --font-size-base: 16px;
  --font-size-lg: calc(var(--font-size-base) * var(--font-scale));
  --font-size-xl: calc(var(--font-size-lg) * var(--font-scale));
  --font-size-xxl: calc(var(--font-size-xl) * var(--font-scale));
}

h1 { font-size: var(--font-size-xxl); }
h2 { font-size: var(--font-size-xl); }
h3 { font-size: var(--font-size-lg); }
p { font-size: var(--font-size-base); }
small { font-size: var(--font-size-sm); }

Complete Theme System Example



html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSS Variables Theme System</title>
  <style>
    :root {
      /* Light theme (default) */
      --bg-primary: #ffffff;
      --bg-secondary: #f8f9fa;
      --text-primary: #212529;
      --text-secondary: #6c757d;
      --accent: #007bff;
      --border: #dee2e6;
      
      /* Spacing */
      --space-xs: 4px;
      --space-sm: 8px;
      --space-md: 16px;
      --space-lg: 24px;
      --space-xl: 32px;
      
      /* Sizing */
      --border-radius: 8px;
      --font-size-base: 16px;
      --line-height: 1.5;
      
      /* Transitions */
      --transition-fast: 0.2s ease;
      --transition-normal: 0.3s ease;
    }
    
    /* Dark theme */
    [data-theme="dark"] {
      --bg-primary: #121212;
      --bg-secondary: #1e1e1e;
      --text-primary: #ffffff;
      --text-secondary: #b0b0b0;
      --accent: #4dabf7;
      --border: #333;
    }
    
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    body {
      background: var(--bg-primary);
      color: var(--text-primary);
      font-family: Arial, sans-serif;
      font-size: var(--font-size-base);
      line-height: var(--line-height);
      transition: background var(--transition-normal), color var(--transition-normal);
    }
    
    .container {
      max-width: 800px;
      margin: 0 auto;
      padding: var(--space-lg);
    }
    
    .header {
      background: var(--bg-secondary);
      padding: var(--space-lg);
      border-radius: var(--border-radius);
      margin-bottom: var(--space-lg);
      border: 1px solid var(--border);
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    
    .theme-toggle {
      background: var(--accent);
      color: white;
      border: none;
      padding: var(--space-sm) var(--space-md);
      border-radius: calc(var(--border-radius) / 2);
      cursor: pointer;
      font-size: calc(var(--font-size-base) * 0.9);
      transition: transform var(--transition-fast);
    }
    
    .theme-toggle:hover {
      transform: scale(1.05);
    }
    
    .card {
      background: var(--bg-secondary);
      border: 1px solid var(--border);
      border-radius: var(--border-radius);
      padding: var(--space-lg);
      margin-bottom: var(--space-md);
      transition: all var(--transition-normal);
    }
    
    .card:hover {
      transform: translateY(calc(var(--space-xs) * -1));
      box-shadow: 0 calc(var(--space-sm)) calc(var(--space-lg)) rgba(0,0,0,0.1);
    }
    
    .card h3 {
      color: var(--accent);
      margin-bottom: var(--space-sm);
      font-size: calc(var(--font-size-base) * 1.25);
    }
    
    .card p {
      color: var(--text-secondary);
      margin-bottom: var(--space-md);
    }
    
    .button {
      background: var(--accent);
      color: white;
      border: none;
      padding: calc(var(--space-sm) * 1.5) var(--space-md);
      border-radius: calc(var(--border-radius) / 2);
      cursor: pointer;
      font-size: var(--font-size-base);
      transition: all var(--transition-fast);
      text-decoration: none;
      display: inline-block;
    }
    
    .button:hover {
      background: color-mix(in srgb, var(--accent) 80%, black);
      transform: translateY(calc(var(--space-xs) * -0.5));
    }
    
    .stats {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: var(--space-md);
      margin-top: var(--space-lg);
    }
    
    .stat {
      background: var(--bg-secondary);
      padding: var(--space-md);
      border-radius: var(--border-radius);
      text-align: center;
      border: 1px solid var(--border);
    }
    
    .stat-number {
      font-size: calc(var(--font-size-base) * 2);
      font-weight: bold;
      color: var(--accent);
      display: block;
    }
    
    .stat-label {
      color: var(--text-secondary);
      font-size: calc(var(--font-size-base) * 0.9);
      margin-top: var(--space-xs);
    }
  </style>
</head>
<body>
  <div class="container">
    <header class="header">
      <h1>CSS Variables Demo</h1>
      <button class="theme-toggle" onclick="toggleTheme()">🌙 Dark Theme</button>
    </header>
    
    <main>
      <div class="card">
        <h3>What are CSS Variables?</h3>
        <p>CSS custom properties allow you to store values that can be reused throughout your stylesheet, making maintenance easier and enabling features like theming.</p>
        <a href="#" class="button">Learn More</a>
      </div>
      
      <div class="card">
        <h3>Dynamic Calculations</h3>
        <p>The calc() function lets you perform calculations with different units, creating responsive and flexible layouts.</p>
        <a href="#" class="button">Explore calc()</a>
      </div>
      
      <div class="stats">
        <div class="stat">
          <span class="stat-number">100%</span>
          <span class="stat-label">Browser Support</span>
        </div>
        <div class="stat">
          <span class="stat-number">50%</span>
          <span class="stat-label">Less CSS Code</span>
        </div>
        <div class="stat">
          <span class="stat-number">∞</span>
          <span class="stat-label">Possibilities</span>
        </div>
      </div>
    </main>
  </div>
  
  <script>
    function toggleTheme() {
      const body = document.body;
      const button = document.querySelector('.theme-toggle');
      
      if (body.getAttribute('data-theme') === 'dark') {
        body.removeAttribute('data-theme');
        button.textContent = '🌙 Dark Theme';
      } else {
        body.setAttribute('data-theme', 'dark');
        button.textContent = '☀️ Light Theme';
      }
    }
    
    // You can also update CSS variables with JavaScript
    function updateAccentColor(color) {
      document.documentElement.style.setProperty('--accent', color);
    }
  </script>
</body>
</html>

Advanced calc() Techniques



css

/* Responsive font sizes */
.responsive-text {
  font-size: calc(16px + (24 - 16) * ((100vw - 320px) / (1200 - 320)));
  /* Min 16px at 320px viewport, max 24px at 1200px viewport */
}

/* Perfect centering */
.centered {
  position: absolute;
  top: calc(50% - var(--element-height) / 2);
  left: calc(50% - var(--element-width) / 2);
}

/* Grid with gaps */
.grid-3-col {
  width: calc((100% - 40px) / 3); /* 3 columns, 20px gap each side */
  margin-right: 20px;
}

.grid-3-col:nth-child(3n) {
  margin-right: 0; /* Remove margin from last column */
}

JavaScript Integration



javascript

// Get CSS variable value
const primaryColor = getComputedStyle(document.documentElement)
  .getPropertyValue('--primary-color');

// Set CSS variable value
document.documentElement.style.setProperty('--primary-color', '#ff6b6b');

// Remove CSS variable
document.documentElement.style.removeProperty('--primary-color');

// Check if CSS variables are supported
if (CSS.supports('--test', 'value')) {
  // CSS variables are supported
  console.log('CSS variables supported!');
}

Best Practices

  1. Use meaningful names - --primary-color not --blue
  2. Group related variables - Keep spacing, colors, typography together
  3. Provide fallbacks - Always include fallback values
  4. Use :root for globals - Define global variables in :root
  5. Scope appropriately - Use local scope for component-specific values
  6. Document your system - Comment your variable usage