Content updated
By the end, you can…
- Define a value with --name, read it with var(--name), and change it where the relevant elements can inherit it.
- Explain the example, change one value, and check the result in your own page.
One idea at a time
Repeated colors and spacing values are difficult to maintain when every rule contains a separate copy. A custom property gives a value a meaningful name. --surface is easier to discuss than an unexplained color value repeated throughout a file. Naming the value does not apply it anywhere: ordinary properties still need to read it with var().
A declaration such as --ink: #1e3a8a follows the same selector and cascade rules as other declarations. The ordinary custom properties used here inherit into descendants, the elements nested inside another element. :root matches the document's root element, html here, making it a useful place for shared defaults. A class can override the defaults for one subtree, meaning that element and everything nested inside it, without changing sibling elements.
What to remember first
Define a value with --name, read it with var(--name), and change it where the relevant elements can inherit it.
Read the CSS before trying it
--surface: #eff6ffDefine a named value- Custom-property names start with two dashes and are case-sensitive.
var(--surface)Read the value- Use it in a property value, for example background-color. Defining --surface alone does not paint anything.
:rootDocument-wide defaults- In this HTML document it selects html. Descendants normally inherit the custom properties declared there.
.dark-themeOverride a subtree- A class supplies different values on that element, which its descendants can inherit. CSS does not add the class automatically.
var(--missing, navy)Fallback value- Uses navy when the referenced custom property is missing or otherwise invalid for substitution. It is not a universal type checker for arbitrary values.
Walk through an example
<div class="card">
<h1>Shared theme</h1>
<p>The heading and this paragraph share the ink value.</p>
</div>
<div class="card dark-theme">
<h2>Local theme</h2>
<p>This second card can override the shared defaults.</p>
</div>:root {
--surface: #eff6ff;
--ink: #1e3a8a;
--space: 16px;
}
.card {
background-color: var(--surface);
color: var(--ink);
padding: var(--space);
}
.card p {
color: var(--ink);
}Both cards read the same root values at first. Changing --ink updates every place that reads it. In the local-theme version, the second card overrides surface and ink; its nested paragraph inherits those custom-property values and uses them in its own color rule. The first card is a sibling, so it keeps the root defaults.
See it happen: try a small change
Predict what will change, choose a version, and compare the live result with the CSS. Use the real controls when the example includes them.
Code to notice
:root {
--surface: #eff6ff;
--ink: #1e3a8a;
--space: 16px;
}
.card {
background-color: var(--surface);
color: var(--ink);
padding: var(--space);
}
.card p {
color: var(--ink);
}Live result
What changed: Both cards read the same root values.
Look a little closer
A custom property defined on .card can be used on that card and inherited by descendants; it is not restricted to elements that also have the card class. It does not flow upward to ancestors or sideways to siblings. This distinction matters when you set a theme on body: its descendants can inherit the theme, while the root itself does not inherit from body.
Use names that describe purpose, such as --surface and --ink, rather than a particular color. A dark theme can then change the values while the rules remain readable. A missing value can use a var() fallback, but a present value of 16px used as a color still makes that color declaration invalid. Standard custom properties cannot replace a selector, property name, or media-query condition.
Try it in your own project
Copy both cards, including the second card’s dark-theme class. Start with the root defaults, then append the local override from the preview. Change --space once and check both cards; change the second card’s --ink and check that only its descendants use the new color.
The preview uses a small set of readable starting styles for its font, spacing, and controls. The code to notice shows the lesson's own rules. Your local browser defaults may look different before you add those rules; focus on the relationship being demonstrated rather than matching every pixel of the preview.
A common wrong turn
Changing the definition without using var() in the actual rule has no visible effect. Check the property spelling, the selector where it is defined, and whether the consuming element inherits it. A .dark-theme class is just a selector until that class exists in the HTML.
Your turn: reuse values with CSS custom properties
Give two reading cards shared surface, text, and spacing values. Override the second card's colors without copying the entire card rule.
Try this in your own HTML and CSS files, then open the page in a browser. There is no separate checked practice for this task yet.
- Define the three custom properties on :root and use var() in the card rules.
- Add dark-theme to only the second card and supply a readable alternate pair there.
- Change one shared value, then inspect both cards and their paragraphs. Explain which values inherit and which are locally overridden.
Go to the source
Examples are authored for Tuklas and checked with code and reference documentation. This is not a claim of independent human review. Suggest a correction.