~/hackweb.dev
CSS Variables (Custom Properties)
Quiz
...

CSS Variables (Custom Properties)

beginner · updated Tue Sep 08 2026Contribute

Master CSS variables, theming, and dynamic styles with custom properties.

CSS Variables (Custom Properties)

CSS variables store reusable values. They make CSS more maintainable and enable dynamic theming.

Basic Syntax

Define Variables

:root {
  --primary-color: #3498db;
  --secondary-color: #2ecc71;
  --font-size: 16px;
  --spacing: 1rem;
}

Use Variables

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

.container {
  max-width: 1200px;
  padding: var(--spacing);
  color: var(--primary-color);
}

Fallback Values

Provide default values:

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

.text {
  font-size: var(--font-size, 16px);
}

/* Multiple fallbacks */
.element {
  color: var(--text-color, var(--primary-color, black));
}

Scope

Variables can be scoped:

/* Global scope */
:root {
  --primary-color: #3498db;
}

/* Component scope */
.card {
  --card-bg: white;
  --card-padding: 1rem;
  background-color: var(--card-bg);
  padding: var(--card-padding);
}

.card-dark {
  --card-bg: #333;
  --card-padding: 1.5rem;
}

Dynamic Variables

Variables can be changed with JavaScript:

:root {
  --primary-color: #3498db;
}
// Change variable
document.documentElement.style.setProperty('--primary-color', '#e74c3c');

// Reset variable
document.documentElement.style.removeProperty('--primary-color');

Complete Example: Theme System

/* Light theme (default) */
:root {
  --bg-primary: #ffffff;
  --bg-secondary: #f5f5f5;
  --text-primary: #333333;
  --text-secondary: #666666;
  --accent-color: #3498db;
  --border-color: #dddddd;
  --shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

/* Dark theme */
[data-theme="dark"] {
  --bg-primary: #1a1a1a;
  --bg-secondary: #2d2d2d;
  --text-primary: #ffffff;
  --text-secondary: #b0b0b0;
  --accent-color: #3498db;
  --border-color: #444444;
  --shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}

/* Apply theme */
body {
  background-color: var(--bg-primary);
  color: var(--text-primary);
  font-family: var(--font-family);
}

.card {
  background-color: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: var(--shadow);
}

.button {
  background-color: var(--accent-color);
  color: white;
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
// Toggle theme
function toggleTheme() {
  const html = document.documentElement;
  const currentTheme = html.getAttribute('data-theme');
  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
  html.setAttribute('data-theme', newTheme);
}

Complete Example: Design Tokens

/* Spacing scale */
:root {
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;
}

/* Typography scale */
:root {
  --font-size-xs: 0.75rem;
  --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;
}

/* Colors */
:root {
  --color-primary: #3498db;
  --color-secondary: #2ecc71;
  --color-success: #27ae60;
  --color-warning: #f39c12;
  --color-danger: #e74c3c;
}

/* Border radius */
:root {
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;
}

/* Usage */
.card {
  padding: var(--space-lg);
  font-size: var(--font-size-base);
  border-radius: var(--radius-md);
  background-color: var(--color-primary);
}

Complete Example: Responsive Typography

/* Base typography */
:root {
  --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
  --text-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
  --text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
  --text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.25rem);
  --text-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
  --text-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
}

body {
  font-size: var(--text-base);
  line-height: 1.6;
}

h1 {
  font-size: var(--text-2xl);
}

h2 {
  font-size: var(--text-xl);
}

p {
  font-size: var(--text-base);
}

Complete Example: Component Library

/* Button variants */
:root {
  --btn-primary-bg: #3498db;
  --btn-primary-hover: #2980b9;
  --btn-secondary-bg: #95a5a6;
  --btn-secondary-hover: #7f8c8d;
  --btn-success-bg: #27ae60;
  --btn-success-hover: #219a52;
  --btn-padding: 0.75rem 1.5rem;
  --btn-border-radius: 5px;
  --btn-font-weight: 500;
}

.button {
  padding: var(--btn-padding);
  border-radius: var(--btn-border-radius);
  font-weight: var(--btn-font-weight);
  border: none;
  cursor: pointer;
  transition: background-color 0.3s, transform 0.2s;
}

.button-primary {
  background-color: var(--btn-primary-bg);
  color: white;
}

.button-primary:hover {
  background-color: var(--btn-primary-hover);
  transform: translateY(-2px);
}

.button-secondary {
  background-color: var(--btn-secondary-bg);
  color: white;
}

.button-secondary:hover {
  background-color: var(--btn-secondary-hover);
}

Dynamic Values

Change variables based on state:

.progress {
  --progress: 0%;
  width: var(--progress);
  height: 20px;
  background-color: var(--color-primary);
  transition: width 0.3s ease;
}
// Update progress
element.style.setProperty('--progress', '75%');

Media Queries with Variables

:root {
  --spacing: 1rem;
  --font-size: 14px;
}

@media (min-width: 768px) {
  :root {
    --spacing: 1.5rem;
    --font-size: 16px;
  }
}

@media (min-width: 992px) {
  :root {
    --spacing: 2rem;
    --font-size: 18px;
  }
}

Best Practices

  1. Use :root for global variables — Available everywhere
  2. Use meaningful names--color-primary not --blue
  3. Provide fallback values — Always have a default
  4. Group related variables — Colors, spacing, typography
  5. Use design tokens — Consistent, reusable values

Common Mistakes

  1. Not providing fallbacks — Breaks if variable undefined
  2. Overusing variables — Not everything needs a variable
  3. Inconsistent naming — Stick to a convention
  4. Global scope pollution — Use component scope when appropriate
  5. Not documenting variables — Hard to understand later