Scoping Custom Properties

Custom Properties Follow Normal Cascade Rules

Unlike Sass variables, which are resolved once at compile time, CSS custom properties are live and follow the same cascade and inheritance rules as any other CSS property - meaning where a variable is declared directly controls where it is visible and what value it holds.

Global Scope with :root

:root {
  --gap: 16px;
}

A variable declared on :root is inherited by every element in the document, making it effectively global.

Local Scope on a Specific Element

.dense-list {
  --gap: 4px; /* only visible to .dense-list and its descendants */
}

Declaring the same variable name inside a specific selector creates a new value that only applies within that selector's scope - anything outside it still sees the global :root value.

A Practical Pattern: Component-Level Variables

.card {
  --card-padding: 16px;
  padding: var(--card-padding);
}
.card.compact {
  --card-padding: 8px; /* overrides just for this variant */
}

Defining a variable scoped to a component, then overriding it on a modifier class, is a common way to build variant styles without duplicating every property.

Why Understanding Scope Matters

A variable that seems to "not work" is very often actually working correctly, but scoped somewhere unexpected - defined inside a sibling element rather than a shared ancestor, for instance, so it never inherits down to where it is being used. Checking where a variable is declared, relative to where it is read, is the first step in debugging this.

Written by Lorens Mishra

Software Engineer Notes Management System Administrator

Continue reading

All CSS notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.