Skip to content
intermediate Phase 3 · CSS Advanced

CSS Custom Properties

Use CSS variables for dynamic theming, design tokens, and responsive design systems.

45m
0 problems
Topic Progress 0%

Introduction to CSS Custom Properties

CSS Custom Properties (CSS Variables)

CSS Custom Properties, commonly known as CSS variables, allow you to define reusable values throughout your stylesheet using the -- prefix. Unlike preprocessor variables (Sass, Less), CSS custom properties are natively supported in the browser and can be dynamically changed at runtime using JavaScript or CSS itself.

Why CSS Variables Matter

CSS variables solve a long-standing problem in web development: maintaining consistent design tokens across large stylesheets. Before CSS variables, changing a primary color meant searching and replacing every instance manually. With CSS custom properties, you define the value once and reference it everywhere.

:root {
  --primary-color: #2563eb;
  --spacing-unit: 8px;
  --font-size-base: 16px;
}

.button {
  background-color: var(--primary-color);
  padding: var(--spacing-unit) calc(var(--spacing-unit) * 2);
  font-size: var(--font-size-base);
}

Key Concepts

  • Declaration: Variables are declared with -- prefix inside a selector (e.g., --primary-color: #2563eb;)
  • Reference: Variables are accessed using the var() function (e.g., var(--primary-color))
  • Scope: Variables inherit down the DOM tree, so a variable set on :root is available everywhere
  • Fallback: var() accepts a second argument as a fallback value (e.g., var(--missing, #000))

When to Use CSS Variables

Use CSS custom properties when you need design tokens that may change based on context, theme, or user preference. They are essential for dark mode implementations, component libraries, and any design system that requires runtime flexibility.

Common Pitfalls

Avoid declaring variables on high-specificity selectors where they become difficult to override. Also, remember that CSS custom properties are not replaced at compile time—they resolve in the browser, which can affect performance in deeply nested scenarios.

Dynamic Theming with CSS Variables

Implementing Dynamic Theming

CSS custom properties excel at creating dynamic theming systems. By defining variables on a parent element and overriding them on children or state changes, you can create powerful theme switching without JavaScript re-rendering.

Dark Mode Implementation

The most common use case is implementing dark mode. By toggling a class on the html or body element, you can redefine all color variables at once:

:root {
  --bg-primary: #ffffff;
  --bg-secondary: #f3f4f6;
  --text-primary: #111827;
  --text-secondary: #6b7280;
  --border-color: #e5e7eb;
}

.dark-theme {
  --bg-primary: #111827;
  --bg-secondary: #1f2937;
  --text-primary: #f9fafb;
  --text-secondary: #9ca3af;
  --border-color: #374151;
}

body {
  background-color: var(--bg-primary);
  color: var(--text-primary);
}

.card {
  background-color: var(--bg-secondary);
  border: 1px solid var(--border-color);
}

Component-Level Theming

Variables can be overridden on specific components to create variant themes without affecting the global context:

.alert {
  --alert-bg: #fef2f2;
  --alert-text: #991b1b;
  --alert-border: #fca5a5;
  background-color: var(--alert-bg);
  color: var(--alert-text);
  border: 1px solid var(--alert-border);
  padding: var(--spacing-md);
  border-radius: var(--radius-sm);
}

.alert--success {
  --alert-bg: #f0fdf4;
  --alert-text: #166534;
  --alert-border: #86efac;
}

Media Query Theming

CSS variables can also respond to media queries for responsive theming:

:root {
  --content-max-width: 100%;
  --font-size-heading: 1.5rem;
  --spacing-layout: 1rem;
}

@media (min-width: 768px) {
  :root {
    --content-max-width: 720px;
    --font-size-heading: 2rem;
    --spacing-layout: 2rem;
  }
}

Best Practices for Theming

Always define your base theme tokens on :root so they cascade to all elements. Use semantic variable names (e.g., --color-surface) rather than literal color values (e.g., --white) to make theme switching intuitive. Keep your variable namespacing consistent with prefixes like --color-, --spacing-, and --font-.

Design Tokens and Scalable Systems

Building Design Systems with CSS Variables

Design tokens are the foundational values of a design system—colors, typography, spacing, shadows, and breakpoints that establish visual consistency. CSS custom properties are the ideal mechanism for managing these tokens in a browser-native way.

Token Architecture

Organize your tokens into a layered architecture: primitive tokens, semantic tokens, and component tokens:

/* Primitive tokens - raw values */
:root {
  --blue-50: #eff6ff;
  --blue-100: #dbeafe;
  --blue-500: #3b82f6;
  --blue-600: #2563eb;
  --blue-900: #1e3a8a;
  --gray-50: #f9fafb;
  --gray-100: #f3f4f6;
  --gray-900: #111827;
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;
}

/* Semantic tokens - purpose-driven */
:root {
  --color-primary: var(--blue-600);
  --color-primary-hover: var(--blue-900);
  --color-surface: var(--gray-50);
  --color-text: var(--gray-900);
  --color-border: var(--gray-100);
  --spacing-element: var(--spacing-md);
  --spacing-section: var(--spacing-xl);
}

/* Component tokens - component-specific */
.button {
  --button-bg: var(--color-primary);
  --button-text: #ffffff;
  --button-radius: var(--radius-md);
  --button-padding: var(--spacing-sm) var(--spacing-md);
}

Typography Scales

CSS variables enable fluid typography that scales smoothly across breakpoints:

:root {
  --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;
  --font-family-sans: 'Inter', system-ui, sans-serif;
  --font-family-mono: 'Fira Code', monospace;
  --font-weight-normal: 400;
  --font-weight-semibold: 600;
  --line-height-tight: 1.25;
  --line-height-normal: 1.5;
}

Shadow and Elevation Systems

Define a consistent elevation system using layered shadows:

:root {
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}

Runtime Token Modification

One of the most powerful features of CSS custom properties is the ability to modify tokens at runtime via JavaScript:

// Change spacing scale dynamically
document.documentElement.style.setProperty('--spacing-md', '20px');

// Respond to user preferences
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
if (prefersDark.matches) {
  document.documentElement.classList.add('dark-theme');
}

This approach allows you to create adaptive interfaces that respond to user preferences, accessibility settings, or contextual changes without page reloads.

Quiz

1. What prefix is used to declare a CSS custom property?

Question 1 options

2. How do you provide a fallback value when using a CSS custom property?

Question 2 options

3. What is the main advantage of CSS custom properties over Sass/Less variables?

Question 3 options

Flashcards

Question

What is the syntax for declaring a CSS custom property?

Answer

CSS custom properties are declared with a double-hyphen prefix inside a selector: `--property-name: value;`. Example: `:root { --primary-color: #3b82f6; }`. They are then referenced using the `var()` function: `color: var(--primary-color);`.

Question

How do CSS custom properties enable dynamic theming?

Answer

CSS custom properties cascade down the DOM tree, so overriding a variable on a parent element (like toggling a `.dark-theme` class) automatically updates all child elements that reference it. This allows instant theme switching without recalculating or replacing values.

Question

What is the purpose of design tokens in CSS?

Answer

Design tokens are named values (colors, spacing, typography, shadows) stored as CSS custom properties that establish a consistent visual language. They create a single source of truth for design decisions, making it easy to update the entire design system by changing a few token values.

Revision Notes

Key Takeaways

  • 1. CSS custom properties use the `--` prefix for declaration and `var()` for reference, with optional fallback values as a second argument
  • 2. Variables cascade and inherit down the DOM tree, making them ideal for component-level and theme-level overrides
  • 3. Define primitive tokens (raw values), semantic tokens (purpose-driven), and component tokens for scalable design systems
  • 4. CSS variables can be modified at runtime via JavaScript, enabling dynamic theming, user preferences, and responsive adjustments
  • 5. Always use semantic naming conventions (e.g., `--color-primary`) rather than literal values (e.g., `--blue-500`) for maintainability

Interview Tips

  • Explain the difference between CSS custom properties and preprocessor variables—the key distinction is runtime dynamic modification vs. compile-time static replacement
  • Describe a dark mode implementation strategy using CSS variables on `:root` with a `.dark-theme` class override
  • Discuss performance considerations: CSS variables resolve in the browser, so avoid excessive re-computation in deeply nested selectors
  • Know how to use `var()` with fallback values and how variables cascade through the DOM tree for component scoping
  • Be prepared to explain design token architecture: primitive, semantic, and component-level tokens

Cheat Sheet

Declare: --var-name: value; | Reference: var(--var-name, fallback) | Scope: Cascades down DOM tree | Runtime: Change via element.style.setProperty('--var', val) | Theming: Override on .dark-theme or media query | Tokens: Use --color-*, --spacing-*, --font-* namespaces