Skip to main content
CodeOath
← All posts

HTML & CSS82 min total · 18 parts

CSS Fundamentals: The Box Model, Specificity, Positioning, and Layout

Part 16 of 18 · ~1 min

CSS Custom Properties (Variables)

Custom properties — the everyday name for them is "CSS variables" — let a value get written down exactly once and then referenced anywhere it's needed. A Sass or Less variable is a build-time trick: it gets swapped out for a literal value before the browser ever opens the file. A custom property is nothing like that — the browser keeps the variable itself alive and re-resolves it on demand, which is exactly what lets JavaScript, a media query, or something as small as a :hover state change its value on the fly, with no build step involved anywhere.

:root {
  --accent-color: #b1500f;
  --card-gap: 24px;
}

.card__add {
  background: var(--accent-color);
}
.grid, .rail {
  gap: var(--card-gap); /* one variable, reused by both the Grid page-grid and the Flexbox rail */
}

The storefront sells more than one category, and each one gets its own accent without a single selector changing:

.category--electronics { --accent-color: #2f6fed; }
.category--apparel      { --accent-color: #c2185b; }

Every card's "Add to cart" button and sale ribbon read var(--accent-color), so dropping a .category--electronics or .category--apparel class on the page's outer wrapper rethemes every card beneath it — because custom properties inherit and cascade exactly like any other property, redefining one further down the tree changes it for everything below that point without touching the component's CSS at all. Dark mode works the same way, at the root:

[data-theme="dark"] {
  --surface: #1a1a1a;
  --text-color: #f0f0f0;
}

var(--name, fallback) also accepts a second, optional argument, and that's the value it falls back to whenever the custom property was never actually declared anywhere in scope:

.card__ribbon {
  background: var(--accent-color, #b1500f); /* falls back to this if --accent-color is missing entirely */
}