Advanced

Custom properties and theming

A custom property is a value stored on an element that other rules read. That makes a theme a set of named values in one place, and it makes a component able to restyle itself from its own container. Both are ordinary CSS, not a preprocessor.

What you will see

A card inside .warning reads the red ink without any new rule on the card itself - the inheritance did the work.

About 1 min of reading

Try It

Theme a component with a scoped override.

1

Global theme

:root { --ink: #222; --gap: 12px; } .card { padding: var(--gap); color: var(--ink); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Every card uses the shared spacing and ink colour.
2

Scoped override

.warning { --ink: #8b0000; } .warning .card { }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
A card inside .warning reads the red ink without any new rule on the card itself - the inheritance did the work.

Data Flow

A named value flows down the tree.
1 Define the property on a container
↓
2 Descendants inherit it
↓
3 Rules read it with var
↓
4 Redefine on a subtree to override
Why this works
Name the role, not the colour

A property called --ink is read wherever text is drawn; one called --dark-blue describes a look and has to be renamed when the design changes. Naming by role is what makes a theme swap a one file change.

Inheritance is the mechanism

A custom property set on a container is visible to every descendant, which is why the definition belongs high in the tree. Setting it on a leaf makes it visible only there, and that is usually the bug.

Examples

Define once, read many

Set the colours and spacing on the root, then read them with var(). Changing one value changes every rule that reads it, which is what makes a theme editable in one place.

Scope to a subtree

Redefine the same property on a component and everything inside it reads the new value. That scoping is how a dark card sits in a light page without a second set of rules.

A fallback for a missing value

var(--gap, 8px) uses the fallback when the property is not set. That default is what lets a component work standalone and inside a themed container without either breaking.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← The cascade and specificity
Previous Lesson
The cascade and specificity
Next Lesson
Pseudo classes and states
Pseudo classes and states →