1. Defining & Using Variables

CSS custom properties start with -- and are accessed with var(). Define global variables in :root.

Primary Color
--demo-primary

Uses var(--demo-primary)

Secondary Color
--demo-secondary

Uses var(--demo-secondary)

Success Color
--demo-success

Uses var(--demo-success)

/* Define global variables in :root */
:root {
  --primary: #6366f1;
  --secondary: #8b5cf6;
  --success: #10b981;
  --spacing: 1rem;
  --radius: 0.5rem;
}

/* Use variables with var() */
.button {
  background: var(--primary);
  padding: var(--spacing);
  border-radius: var(--radius);
}

2. Fallback Values

Provide fallback values for undefined variables. You can even chain fallbacks.

Undefined Variable
Using fallback color

var(--undefined, #64748b) falls back to gray.

/* Single fallback */
.element {
  color: var(--text-color, #1e293b);
}

/* Chained fallback (variable to variable) */
.element {
  background: var(--brand-color, var(--primary, blue));
}

/* Fallback for spacing */
.card {
  padding: var(--card-padding, var(--spacing, 1rem));
}

3. Using calc() with Variables

Combine variables with calc() for dynamic spacing, sizing, and more.

Dynamic Padding Scale
0.5x 1x 1.5x 2x

Padding multiplied with calc().

:root {
  --spacing: 1rem;
}

.box--sm { padding: calc(var(--spacing) * 0.5); } /* 0.5rem */
.box--md { padding: calc(var(--spacing) * 1); }   /* 1rem */
.box--lg { padding: calc(var(--spacing) * 1.5); } /* 1.5rem */
.box--xl { padding: calc(var(--spacing) * 2); }   /* 2rem */

/* Create a spacing scale */
:root {
  --space-unit: 0.25rem;
  --space-1: calc(var(--space-unit) * 1);  /* 0.25rem */
  --space-2: calc(var(--space-unit) * 2);  /* 0.5rem */
  --space-4: calc(var(--space-unit) * 4);  /* 1rem */
  --space-8: calc(var(--space-unit) * 8);  /* 2rem */
}

4. Scoped Variables

Variables cascade down the DOM. Define them on specific elements to create variants.

Default Card
Light theme card

Uses default card variables.

Dark Variant
Dark theme card

Overrides with --dark class.

Brand Variant
Brand theme card

Overrides with --brand class.

/* Base component with scoped variables */
.card {
  --card-bg: white;
  --card-color: #1e293b;
  --card-border: #e2e8f0;

  background: var(--card-bg);
  color: var(--card-color);
  border: 2px solid var(--card-border);
}

/* Dark variant - just override the variables */
.card--dark {
  --card-bg: #1e293b;
  --card-color: #f8fafc;
  --card-border: #334155;
}

/* Brand variant */
.card--brand {
  --card-bg: #eef2ff;
  --card-color: #4338ca;
  --card-border: #c7d2fe;
}

5. Component Variants Pattern

Create component variants by overriding internal variables instead of rewriting styles.

Button Variants

All buttons share the same base styles. Variants just change variables.

/* Base button with internal variables */
.btn {
  --btn-bg: var(--primary);
  --btn-color: white;
  --btn-hover-bg: #4f46e5;

  background: var(--btn-bg);
  color: var(--btn-color);
  /* ...other styles... */
}

.btn:hover {
  background: var(--btn-hover-bg);
}

/* Variants just override the variables */
.btn--success {
  --btn-bg: var(--success);
  --btn-hover-bg: #059669;
}

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

6. Theme Switching

Override variables with data-theme attribute for dark mode and other themes.

Live Theme Demo

Theme Preview

Card Title

This card adapts to the current theme using CSS variables.

Learn more →

Click "Toggle Dark" to switch themes using data-theme.

/* Light theme (default) */
.app {
  --bg: #ffffff;
  --surface: #f8fafc;
  --text: #1e293b;
  --muted: #64748b;
  --border: #e2e8f0;
  --accent: #6366f1;
}

/* Dark theme override */
.app[data-theme="dark"] {
  --bg: #0f172a;
  --surface: #1e293b;
  --text: #f8fafc;
  --muted: #94a3b8;
  --border: #334155;
  --accent: #818cf8;
}

/* Components use the variables */
.card {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
}

/* JS to toggle */
const toggle = () => {
  const app = document.querySelector('.app');
  const isDark = app.dataset.theme === 'dark';
  app.dataset.theme = isDark ? '' : 'dark';
};

7. Design Token: Spacing Scale

Build a consistent spacing scale using a single base unit multiplied with calc().

Spacing Scale Visualization
--space-1
--space-2
--space-4
--space-6
--space-8
--space-12
--space-16

Each step is a multiple of --space-unit: 0.25rem.

/* Spacing scale based on 0.25rem unit */
:root {
  --space-unit: 0.25rem;
  
  --space-1:  calc(var(--space-unit) * 1);   /* 0.25rem */
  --space-2:  calc(var(--space-unit) * 2);   /* 0.5rem */
  --space-3:  calc(var(--space-unit) * 3);   /* 0.75rem */
  --space-4:  calc(var(--space-unit) * 4);   /* 1rem */
  --space-6:  calc(var(--space-unit) * 6);   /* 1.5rem */
  --space-8:  calc(var(--space-unit) * 8);   /* 2rem */
  --space-12: calc(var(--space-unit) * 12);  /* 3rem */
  --space-16: calc(var(--space-unit) * 16);  /* 4rem */
}

/* Usage */
.card {
  padding: var(--space-4);
  margin-bottom: var(--space-6);
  gap: var(--space-2);
}

8. Design Token: Color Palette

Define a color palette with shade variations for consistent theming.

Brand Color Palette
Rose
Orange
Amber
Emerald
Teal
Cyan
Sky
Indigo
Violet
Fuchsia

Define semantic color tokens for primary, success, warning, and accent colors.

/* Brand color palette */
:root {
  --rose:    #f43f5e;
  --orange:  #f97316;
  --amber:   #f59e0b;
  --emerald: #10b981;
  --teal:    #14b8a6;
  --cyan:    #06b6d4;
  --sky:     #0ea5e9;
  --indigo:  #6366f1;
  --violet:  #8b5cf6;
  --fuchsia: #d946ef;

  /* Semantic color tokens */
  --color-primary: var(--indigo);
  --color-success: var(--emerald);
  --color-warning: var(--amber);
  --color-danger:  var(--rose);
}