CSS Variables (Custom Properties): The Complete Cheatsheet with 31 Essential Techniques

Introduction to CSS Variables

CSS Variables, officially called Custom Properties, are entities defined by developers that contain specific values to be reused throughout a document. They help create more maintainable, themeable, and dynamic CSS by allowing you to define values once and reference them throughout your stylesheets. Unlike preprocessor variables (like Sass or Less), CSS Variables are part of the DOM, can be manipulated with JavaScript, and can be cascaded and inherited, making them powerful for responsive design, theming, and component-based architectures.

Core Concepts of CSS Variables

Basic Syntax

CSS Variables have two essential parts:

  1. Declaration: Define a variable using the double hyphen prefix (--) within a selector scope:

    :root {
      --main-color: #3498db;
    }
    
  2. Usage: Reference a variable using the var() function:

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

Scope and Inheritance

CSS Variables follow the standard CSS cascading and inheritance rules:

  • Variables defined in :root are globally available
  • Variables defined in specific selectors are scoped to those elements and their descendants
  • Variables are inherited by child elements
  • Variables can be redefined in any selector to override inherited values
:root {
  --text-color: black;
}

.container {
  --text-color: blue;  /* Overrides for .container and its children */
}

p {
  color: var(--text-color);  /* Will be black or blue depending on context */
}

The var() Function

The var() function has two parameters:

var(--custom-property-name, fallback-value)
  • First parameter: The custom property name (required)
  • Second parameter: A fallback value (optional) used if the variable is not defined
.element {
  color: var(--text-color, #333);  /* Uses #333 if --text-color is not defined */
}

Step-by-Step Process for Implementing CSS Variables

1. Plan Your Variable System

Before coding, identify values that:

  • Repeat throughout your CSS
  • Might need to change together (theme colors, spacing units)
  • Form logical groups (typography, colors, layout)

2. Define Global Variables

Start with global variables in the :root selector:

:root {
  /* Colors */
  --primary-color: #3498db;
  --secondary-color: #2ecc71;
  --text-color: #333;
  --background-color: #f8f8f8;
  
  /* Typography */
  --font-family-base: 'Open Sans', sans-serif;
  --font-size-base: 16px;
  --line-height-base: 1.5;
  
  /* Spacing */
  --spacing-unit: 8px;
  --spacing-small: calc(var(--spacing-unit) * 1);
  --spacing-medium: calc(var(--spacing-unit) * 2);
  --spacing-large: calc(var(--spacing-unit) * 3);
  
  /* Layout */
  --container-width: 1200px;
  --border-radius: 4px;
}

3. Implement Variables in Your CSS

Replace hardcoded values with variable references:

body {
  font-family: var(--font-family-base);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: var(--text-color);
  background-color: var(--background-color);
}

.button {
  background-color: var(--primary-color);
  padding: var(--spacing-small) var(--spacing-medium);
  border-radius: var(--border-radius);
}

4. Create Component-Specific Variables

Define scoped variables for components:

.card {
  --card-padding: var(--spacing-medium);
  --card-bg: white;
  
  padding: var(--card-padding);
  background-color: var(--card-bg);
  border-radius: var(--border-radius);
}

.card.dark {
  --card-bg: #222;
  --text-color: white;
}

5. Add Dynamic Functionality with JavaScript

Manipulate variables with JavaScript for interactivity:

// Change theme color on button click
document.getElementById('theme-toggle').addEventListener('click', function() {
  document.documentElement.style.setProperty('--primary-color', '#e74c3c');
});

// Get current value of a CSS variable
const currentColor = getComputedStyle(document.documentElement)
  .getPropertyValue('--primary-color');

Key Techniques for Working with CSS Variables

1. Using Calculations with CSS Variables

Combine variables with calc() for dynamic sizing:

:root {
  --header-height: 60px;
  --footer-height: 80px;
}

.main-content {
  min-height: calc(100vh - (var(--header-height) + var(--footer-height)));
}

2. Nested Variables

Reference variables within other variables:

:root {
  --brand-hue: 220; /* Blue */
  --brand-saturation: 70%;
  --brand-lightness: 50%;
  
  --primary-color: hsl(var(--brand-hue), var(--brand-saturation), var(--brand-lightness));
  --primary-light: hsl(var(--brand-hue), var(--brand-saturation), 70%);
  --primary-dark: hsl(var(--brand-hue), var(--brand-saturation), 30%);
}

3. Responsive Design with Media Queries

Adjust variables for different screen sizes:

:root {
  --font-size-base: 16px;
  --spacing-unit: 8px;
}

@media (max-width: 768px) {
  :root {
    --font-size-base: 14px;
    --spacing-unit: 6px;
  }
}

4. Creating Color Schemes

Build complete themes with variables:

:root {
  /* Light theme (default) */
  --bg-primary: #ffffff;
  --bg-secondary: #f0f0f0;
  --text-primary: #333333;
  --text-secondary: #666666;
  --accent-color: #3498db;
}

.dark-theme {
  --bg-primary: #121212;
  --bg-secondary: #1e1e1e;
  --text-primary: #ffffff;
  --text-secondary: #b0b0b0;
  --accent-color: #29b6f6;
}

5. Animation with CSS Variables

Create dynamic animations:

.button {
  --transition-speed: 0.3s;
  transition: transform var(--transition-speed) ease;
}

.button:hover {
  --hover-scale: 1.05;
  transform: scale(var(--hover-scale));
}

.slow-button {
  --transition-speed: 0.8s;
}

6. Working with Units

Combine CSS variables with different units:

:root {
  --baseline: 24;
  --font-size-heading: calc(var(--baseline) * 2px);
  --width-percentage: 80;
  --max-width: calc(var(--width-percentage) * 1%);
}

7. Creating Design Systems

Build comprehensive design systems with variables:

:root {
  /* Typography Scale */
  --text-xs: 0.75rem;   /* 12px */
  --text-sm: 0.875rem;  /* 14px */
  --text-md: 1rem;      /* 16px */
  --text-lg: 1.125rem;  /* 18px */
  --text-xl: 1.25rem;   /* 20px */
  --text-2xl: 1.5rem;   /* 24px */
  --text-3xl: 1.875rem; /* 30px */
  
  /* Spacing Scale */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-3: 0.75rem;  /* 12px */
  --space-4: 1rem;     /* 16px */
  --space-6: 1.5rem;   /* 24px */
  --space-8: 2rem;     /* 32px */
  --space-12: 3rem;    /* 48px */
}

Comparison Tables

CSS Variables vs. Preprocessor Variables

FeatureCSS VariablesSass/Less Variables
Runtime modificationYes (with JavaScript)No (compile-time only)
DOM accessYesNo
Cascade & inheritanceYesNo
Browser supportModern browsers (IE11 needs polyfill)All browsers (compiles to CSS)
Conditionally set valuesYes (with media queries)Yes (with conditionals)
PerformanceSlight runtime costNo runtime cost
ScopeSet by CSS selector rulesLimited to file or block
DebuggingVisible in DevToolsNot visible after compilation

CSS Variable Function Comparisons

FunctionPurposeExample
var()Access a CSS variablevar(--primary-color)
calc() with variablesDynamic calculationscalc(var(--spacing) * 2)
env()Access environment variablesenv(safe-area-inset-top)
min() with variablesTake the smallest valuemin(var(--width), 100%)
max() with variablesTake the largest valuemax(var(--min-width), 20rem)
clamp() with variablesValue within a rangeclamp(var(--min), var(--preferred), var(--max))

Common Challenges and Solutions

Challenge: Variable Not Working

Problem: Variable isn’t applied as expected.

Solutions:

  • Check spelling and case (variables are case-sensitive)
  • Verify scope (make sure the variable is defined in the current scope or a parent scope)
  • Add a fallback value: var(--color, black)
  • Check browser support and consider a polyfill for older browsers

Challenge: Cascading Complexity

Problem: Hard to track which variable definition applies in complex projects.

Solutions:

  • Use descriptive naming conventions
  • Document your variable system
  • Use browser DevTools to inspect computed values
  • Minimize variable redefinitions across different scopes

Challenge: Performance Concerns

Problem: Excessive use of variables might impact performance.

Solutions:

  • Don’t create variables for single-use values
  • Group related variables under common selectors
  • Use critical variables in frequently accessed properties judiciously
  • Test performance in your target browsers

Challenge: Using Variables in Media Queries

Problem: Variables can’t be used in media query conditions.

Solution:

/* Won't work: */
@media (max-width: var(--breakpoint)) { ... }

/* Solution: Change the variables inside media queries instead */
@media (max-width: 768px) {
  :root {
    --column-count: 2;
  }
}

Challenge: IE11 Support

Problem: Limited support in older browsers like IE11.

Solutions:

  • Use a polyfill like css-vars-ponyfill
  • Provide fallbacks for critical properties
  • Consider using a preprocessor alongside CSS variables

Best Practices and Tips

Naming Conventions

  • Use descriptive, semantic names: --primary-color not --color1
  • Group related variables with prefixes: --font-size-small, --font-size-medium
  • Use kebab-case (lowercase with hyphens)
  • Indicate units in variable names when helpful: --spacing-small-px

Organization Tips

  • Define global variables in :root
  • Group variables by function (colors, typography, spacing, etc.)
  • Create component-specific variables for encapsulation
  • Document your variable system for team use

Performance Optimization

  • Don’t create variables for values used only once
  • Minimize deep nesting of variable references
  • Use local scoping for component-specific variables
  • Test performance in production-like environments

Advanced Techniques

  1. Generating Shades and Tints:

    :root {
      --color-base: 200, 100%, 50%;
      --color-light-10: hsl(var(--color-base) / 0.1);
      --color-light-25: hsl(var(--color-base) / 0.25);
      --color-dark-10: hsl(var(--color-base) / 0.9);
      --color-dark-25: hsl(var(--color-base) / 0.75);
    }
    
  2. Creating Property Maps:

    .margin-top {
      --size: var(--spacing-medium);
      margin-top: var(--size);
    }
    
    .margin-top.small {
      --size: var(--spacing-small);
    }
    
  3. Feature Toggling:

    :root {
      --feature-new-header: 1; /* 1 = on, 0 = off */
    }
    
    .header-new {
      display: calc(var(--feature-new-header) * 1); /* 1 or 0 */
    }
    
    .header-old {
      display: calc(1 - var(--feature-new-header)); /* 0 or 1 */
    }
    
  4. Stateful Components:

    .component {
      --active: 0;
      opacity: calc(0.5 + (var(--active) * 0.5));
    }
    
    .component.is-active {
      --active: 1;
    }
    
  5. Container Queries Simulation:

    .container {
      --container-width: 1000;
    }
    
    @media (max-width: 800px) {
      .container {
        --container-width: 800;
      }
    }
    
    .container .child {
      font-size: calc(12px + (var(--container-width) / 100));
    }
    

JavaScript Integration with CSS Variables

Reading CSS Variables

// Get a variable from :root
const rootStyles = getComputedStyle(document.documentElement);
const primaryColor = rootStyles.getPropertyValue('--primary-color').trim();

// Get a variable from a specific element
const buttonStyles = getComputedStyle(document.querySelector('.button'));
const buttonPadding = buttonStyles.getPropertyValue('--button-padding').trim();

Writing CSS Variables

// Set a variable on :root (global)
document.documentElement.style.setProperty('--primary-color', '#e74c3c');

// Set a variable on a specific element (local)
const button = document.querySelector('.button');
button.style.setProperty('--button-padding', '20px');

Creating Theme Switchers

const darkModeToggle = document.getElementById('dark-mode-toggle');

darkModeToggle.addEventListener('click', () => {
  document.body.classList.toggle('dark-theme');
});

// Or using variables directly:
const colorModeToggle = document.getElementById('color-mode-toggle');

colorModeToggle.addEventListener('click', () => {
  if (document.documentElement.style.getPropertyValue('--bg-primary') === '#ffffff') {
    document.documentElement.style.setProperty('--bg-primary', '#121212');
    document.documentElement.style.setProperty('--text-primary', '#ffffff');
  } else {
    document.documentElement.style.setProperty('--bg-primary', '#ffffff');
    document.documentElement.style.setProperty('--text-primary', '#333333');
  }
});

Reactive Variables with ResizeObserver

// Update CSS variables based on element size
const container = document.querySelector('.container');
const observer = new ResizeObserver(entries => {
  for (const entry of entries) {
    const width = entry.contentRect.width;
    entry.target.style.setProperty('--container-width', width + 'px');
    entry.target.style.setProperty('--columns', Math.floor(width / 200));
  }
});

observer.observe(container);

Browser Support and Fallbacks

Current Browser Support

CSS Variables are supported in all modern browsers including:

  • Chrome 49+
  • Firefox 31+
  • Safari 9.1+
  • Edge 16+
  • Opera 36+

Internet Explorer 11 does not support CSS Variables.

Providing Fallbacks

Method 1: Direct Property Fallbacks

.button {
  background-color: #3498db; /* Fallback */
  background-color: var(--primary-color);
}

Method 2: Var() Fallbacks

.button {
  background-color: var(--primary-color, #3498db);
}

Method 3: Feature Detection

@supports (--css: variables) {
  /* Styles that use CSS variables */
  .element {
    color: var(--text-color);
  }
}

@supports not (--css: variables) {
  /* Fallback styles */
  .element {
    color: #333;
  }
}

Method 4: JavaScript Polyfill

For IE11 support, use a polyfill:

<script src="https://cdn.jsdelivr.net/npm/css-vars-ponyfill@2"></script>
<script>
  cssVars({
    // Options
    onlyLegacy: true,
    watch: true
  });
</script>

Resources for Further Learning

Documentation

Tutorials and Articles

Tools and Libraries

CSS Variable Examples and Patterns

This cheatsheet covers the essential concepts, techniques, and use cases for CSS Variables (Custom Properties). By leveraging these powerful features, you can create more maintainable, dynamic, and flexible stylesheets for modern web development.

Scroll to Top