·4 min read

CSS Custom Properties (Variables): A Practical Guide

CSSVariablesTheming

CSS custom properties (variables) are one of the most impactful modern CSS features. They make your stylesheets maintainable, themeable, and dynamic.

Basic Syntax

Define variables with -- prefix, use them with var():

css
:root {
  --color-primary: #6366f1;
  --radius: 12px;
  --shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.card {
  background: var(--color-primary);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

Fallback Values

Provide a default in case the variable isn't defined:

css
color: var(--text-color, #333);
padding: var(--spacing, 16px);

This makes components portable — they work with or without the variable being set.

Dark Mode with Variables

The most practical use case — theme switching with zero JavaScript:

css
:root {
  --bg: #ffffff;
  --text: #111111;
  --card: #f5f5f5;
  --border: #e5e5e5;
  --accent: #6366f1;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0a0a0b;
    --text: #e5e5e5;
    --card: #18181b;
    --border: #27272a;
    --accent: #818cf8;
  }
}

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

Change the variables, and every element using them updates automatically.

Scoped Variables

Variables cascade, so you can override them at any level:

css
:root { --accent: #6366f1; }

.danger-zone { --accent: #ef4444; }
.success-zone { --accent: #22c55e; }

.button { background: var(--accent); }

A .button inside .danger-zone is red. Inside .success-zone, it's green. No extra classes needed.

Responsive Design with Variables

Change spacing or sizing at breakpoints:

css
:root {
  --container-padding: 16px;
  --font-size-heading: 1.5rem;
}

@media (min-width: 768px) {
  :root {
    --container-padding: 32px;
    --font-size-heading: 2.5rem;
  }
}

Update variables in one place instead of overriding individual properties across your stylesheet.

Variables in Calculations

Combine with calc() for dynamic values:

css
:root { --base-spacing: 8px; }

.element {
  padding: calc(var(--base-spacing) * 2);     /* 16px */
  margin-bottom: calc(var(--base-spacing) * 3); /* 24px */
  gap: var(--base-spacing);                     /* 8px */
}

This creates a consistent spacing system from a single variable.

Component Patterns

Build configurable components with internal variables:

css
.badge {
  --badge-bg: var(--accent);
  --badge-text: white;
  --badge-size: 0.75rem;

  background: var(--badge-bg);
  color: var(--badge-text);
  font-size: var(--badge-size);
  padding: 2px 8px;
  border-radius: 9999px;
}

.badge-outline {
  --badge-bg: transparent;
  --badge-text: var(--accent);
  border: 1px solid var(--accent);
}

JavaScript Integration

Read and write CSS variables from JavaScript:

javascript
// Read
getComputedStyle(document.documentElement).getPropertyValue('--accent');

// Write
document.documentElement.style.setProperty('--accent', '#ec4899');

This is how JS-based theme switchers work — toggle a class or set variables directly.


CSS variables are the foundation of every tool on CSS Make. Try our Gradient Generator to see how custom properties can store and reuse gradient values, or the Glassmorphism Generator to create themeable glass effects.

Try These Tools