8 min read

CSS Custom Properties (Variables): Reusable Values in Your Stylesheets

8 min read
298 words
13 sections11 code blocks

What Are CSS Custom Properties?

CSS Custom Properties, commonly called CSS Variables, let you define reusable values in your stylesheets. Instead of repeating the same color, spacing, or font value everywhere, you define it once and reference it throughout your CSS.

CSS
:root {
  --primary-color: #3498db;
  --font-size-base: 16px;
  --spacing-md: 1.5rem;
}

.button {
  background: var(--primary-color);
  font-size: var(--font-size-base);
  padding: var(--spacing-md);
}

Declaring Custom Properties

Custom properties are declared with a double hyphen prefix (--) and can be placed on any element:

CSS
/* Global variables on :root */
:root {
  --brand-blue: #3498db;
  --brand-red: #e74c3c;
  --text-color: #333;
  --bg-color: #ffffff;
  --border-radius: 8px;
}

/* Component-scoped variables */
.card {
  --card-padding: 1.5rem;
  --card-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

The :root selector targets the html element and is the conventional place for global variables.

Using Custom Properties

Use the var() function to reference a custom property:

CSS
.header {
  background-color: var(--brand-blue);
  color: var(--bg-color);
}

.card {
  padding: var(--card-padding);
  box-shadow: var(--card-shadow);
  border-radius: var(--border-radius);
}

h1 {
  color: var(--text-color);
}

Fallback Values

The var() function accepts a second argument as a fallback value in case the variable is not defined:

CSS
.element {
  /* If --accent-color is not defined, use #e74c3c */
  color: var(--accent-color, #e74c3c);

  /* Fallback can reference another variable */
  background: var(--card-bg, var(--bg-color, white));
}

Fallbacks are especially useful for component libraries where users may or may not define certain variables.

Scope and Inheritance

Custom properties follow CSS inheritance rules. Variables defined on a parent are available to all children:

CSS
:root {
  --text-color: #333;
}

/* This inherits --text-color from :root */
p { color: var(--text-color); }

/* Override for a specific section */
.dark-section {
  --text-color: #fff;
}

/* Paragraphs inside .dark-section use white */
.dark-section p { color: var(--text-color); } /* #fff */

This scoping is powerful — you can redefine variables on any element to change values for its entire subtree.

Practical Use Cases

Color System

CSS
:root {
  /* Primary palette */
  --color-primary: #3498db;
  --color-primary-light: #5dade2;
  --color-primary-dark: #2471a3;

  /* Neutral palette */
  --color-gray-100: #f8f9fa;
  --color-gray-200: #e9ecef;
  --color-gray-300: #dee2e6;
  --color-gray-600: #6c757d;
  --color-gray-800: #343a40;
  --color-gray-900: #212529;

  /* Semantic colors */
  --color-success: #27ae60;
  --color-warning: #f39c12;
  --color-error: #e74c3c;
}

Spacing Scale

CSS
:root {
  --space-xs: 0.25rem;   /* 4px */
  --space-sm: 0.5rem;    /* 8px */
  --space-md: 1rem;       /* 16px */
  --space-lg: 1.5rem;    /* 24px */
  --space-xl: 2rem;       /* 32px */
  --space-2xl: 3rem;     /* 48px */
  --space-3xl: 4rem;     /* 64px */
}

.section { padding: var(--space-2xl) 0; }
.card { padding: var(--space-lg); margin-bottom: var(--space-md); }
.button { padding: var(--space-sm) var(--space-lg); }

Typography Scale

CSS
:root {
  --font-family-base: 'Inter', system-ui, sans-serif;
  --font-family-heading: 'Poppins', sans-serif;
  --font-family-mono: 'Fira Code', monospace;

  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  --font-size-2xl: 1.5rem;
  --font-size-3xl: 2rem;
  --font-size-4xl: 2.5rem;
}

body { font-family: var(--font-family-base); font-size: var(--font-size-base); }
h1 { font-family: var(--font-family-heading); font-size: var(--font-size-4xl); }
h2 { font-family: var(--font-family-heading); font-size: var(--font-size-3xl); }
code { font-family: var(--font-family-mono); font-size: var(--font-size-sm); }

Component Theming

CSS
.button {
  --btn-bg: var(--color-primary);
  --btn-color: white;
  --btn-padding: var(--space-sm) var(--space-lg);

  background: var(--btn-bg);
  color: var(--btn-color);
  padding: var(--btn-padding);
  border: none;
  border-radius: var(--border-radius);
}

.button:hover {
  --btn-bg: var(--color-primary-dark);
}

.button-danger {
  --btn-bg: var(--color-error);
}

.button-outline {
  --btn-bg: transparent;
  --btn-color: var(--color-primary);
  border: 2px solid var(--color-primary);
}

Dark Mode with CSS Variables

CSS variables make implementing dark mode straightforward:

CSS
:root {
  --bg-color: #ffffff;
  --text-color: #333333;
  --card-bg: #f8f9fa;
  --border-color: #dee2e6;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg-color: #1a1a2e;
    --text-color: #e0e0e0;
    --card-bg: #16213e;
    --border-color: #2a2a4a;
  }
}

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

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

Your entire site theme changes by simply redefining the variable values.

Custom Properties vs Preprocessor Variables

CSS custom properties are different from Sass/LESS variables:

  • CSS Variables: Live in the browser, can be changed dynamically, respect scope and inheritance
  • Sass Variables: Compiled at build time, static once compiled, no runtime changes
CSS
/* CSS Variable: can change at runtime */
:root { --color: blue; }
.dark { --color: lightblue; } /* Overrides in this scope */

/* Sass Variable: fixed at compile time */
/* $color: blue; -- Cannot be changed at runtime */

Summary

CSS custom properties bring reusability, maintainability, and dynamic theming to your stylesheets. Define global variables on :root, use var() to reference them, and leverage scope and inheritance to create flexible component themes. CSS variables are the foundation of modern CSS architecture, enabling features like dark mode, design systems, and responsive typography with clean, maintainable code.