AD JB Toolbox 01 EN

CSS Tutorial / CSS Variables: var(), Scope, Inheritance, and Fallbacks

CSS custom properties store reusable values and are referenced with var() in property values. Their scope follows the cascade and inheritance, and a var() fallback does not automatically rescue a defined value that is invalid for the receiving property.

Choose meaningful shared variables

If you use the same accent color for multiple buttons and cards, you can group it into a meaningful variable instead of copying the color code into each selector. When changing the accent color, if you modify a declaration, the place that references that value also changes. If you make variables, even values that are only used in one place and have no separate role, the burden of keeping track of names can actually increase.

:root {
  --accent: #1d5f91;
  --space-unit: .5rem;
}
.button { color: white; background: var(--accent); }
.card { border: 2px solid var(--accent); padding: calc(var(--space-unit) * 3); }

Just declaring a color in the code above does not change the screen. Actual properties, such as background or border, must be referenced as var. If the name indicates the role, such as --accent, rather than fixing the current color, such as --blue, it will be less confusing when changing to a different color later. --accent and --Accent are different names, so match the case and spelling.

var() is used for the value of properties such as color or padding. Selectors or property names cannot be replaced with variables, and a breakpoint cannot be determined by putting @media (min-width: ...) or var() in the condition value of a container size query. Write breakpoints directly in the corresponding conditional statement, and distinguish them from values to which variables can be applied, such as the color or spacing of elements.

Understand root, inheritance, and component scope

In an HTML document, :root matches the top-level HTML element, so it is convenient to have a common default value in the document. Common -- custom properties are inherited by descendants. Values declared within a card can be used by descendants, such as the title, but are not passed on to unrelated sibling cards.

.card { --card-accent: var(--accent); }
.card.warning { --card-accent: #a63e32; }
.card { border-color: var(--card-accent); }
.card h2 { color: var(--card-accent); }

This rule is a component part of the full example below. .card.warning matches cards with both classes. Winning or losing is not determined by the listing order of HTML classes, but by matching the CSS Inheritance, Specificity, and the Cascade to determine the value. Changing the global accent will change the default card, and warning cards with their own card-accent will retain their original warning colors.

When combining variables with other variables, it is also important which element the value is calculated from. If --line-color: var(--accent) is calculated in the parent and then passed on to the child, the already inherited line-color will not always be recalculated just because the accent is overwritten in the child. Derived values that should follow the accent of their descendants are obviously better declared in the element or directly referencing the accent in the actual attribute. For definitions of scope and inheritance, see CSS custom property specification.

Distinguish fallbacks and invalid computed values

var(--notice-color, #6a3989) provides a purple fallback value when a variable value is missing and cannot be used. Even if a variable is defined and has a meaningful value in other properties, such as 20px, if the value does not match color, the color declaration becomes invalid at the time of calculation. In this case, you should not expect to be “replaced by purple”.

variable state Result of color declaration Colors for the entire example
No notice-color Using fallback values for var purple
notice-color is red Use defined values red
notice-color is 20px Inheritance/initial value rules applied because it does not match the color Because color inherits, use the parent's green text color.

As shown below, even if there is a blue declaration first in the same rule, if the value after variable substitution in the second declaration does not match, it will not return to the first blue. After the declaration selected in the cascade is invalid at the time of evaluation, color's inheritance rules are applied. In the same situation, a property that is not inherited may be treated as the initial value of that property. MDN Custom Property Guide explains the difference between replacement and invalid values.

.sample {
  color: #1d5f91;
  color: var(--notice-color, #6a3989);
}
.sample.bad { --notice-color: 20px; }

You can also put other vars in the fallback value. For example, var(--button-accent, var(--accent, #1d5f91)) looks for the value to use, starting with the button-specific value, then the common value, and finally the primary color. However, if the uses of numbers, lengths, and colors are mixed, the same problem occurs, so variable names and usage must be managed together.

Combine values and units with calc()

The example multiplies --space-unit: .5rem by 3 to set card padding. rem uses the root font size, so changing that size changes the spacing. Use calc() for the calculation rather than concatenating strings, and leave spaces around addition and subtraction operators. For further detail, see CSS Colors and Units: px, em, rem, and Percentages.

:root {
  --space-unit: .5rem;
  --space-card: calc(var(--space-unit) * 3);
}
.card { padding: var(--space-card); }

Consistently choose whether variables include units or just numbers. If you enter a unitless value such as 2 in the length variable, it may not be suitable for applications that require units. Avoid combinations of different uses, such as applying calc to a variable containing a color. For the basic syntax of the formula, refer to the MDN calc document.

Run the scope and fallback comparison

Save the following code as custom-properties.html in UTF-8 and open it in your browser. Compare the accent colors of the two cards and the text colors of the three sentences. The meaning of the colors was also written in letters so that the results could not be distinguished by color alone.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>CSS variable scope and substitution values</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
:root { --space-unit: .5rem; --space-card: calc(var(--space-unit) * 3); --accent: #1d5f91; }
.cards { display: grid; gap: 1rem; }
.card { --card-accent: var(--accent); padding: var(--space-card); border: 2px solid var(--card-accent);
  border-radius: .5rem; background: #f6f9fb; }
.card h2 { color: var(--card-accent); margin-block: 0 .5rem; }
.card p { margin: 0; }
.card.warning { --card-accent: #a63e32; }
.fallback-area { margin-block: 2rem; padding: 1rem; border: 1px solid #8a9ba7; color: #28633b; }
.sample { color: #1d5f91; color: var(--notice-color, #6a3989); }
.sample.bad { --notice-color: 20px; }
.sample.good { --notice-color: #a63e32; }
  </style>
</head>
<body>
<main>
<h1>Variable scope and fallbacks</h1>
<div class="cards">
  <article class="card"><h2>Basic card</h2><p>The heading and border share the same accent color.</p></article>
  <article class="card warning"><h2>warning card</h2><p>The accent color changes only inside this card.</p></article>
</div>
<section class="fallback-area">
  <h2>The parent's text color is green</h2>
  <p class="sample missing">Undefined: purple fallback</p>
  <p class="sample bad">20px: Doesn't match the color, so it inherits the green color from the parent.</p>
  <p class="sample good">Defined as red: use that color</p>
</section>
</main>
</body>
</html>
  1. Make sure that the title and border of the basic cards are blue and that the warning cards are red. The three sentences below are colored purple, green, and red, respectively.
  2. Change only the root accent to #28633b. The default card's heading and border should change, while the warning card keeps its own warning color. Restore the root accent to #1d5f91 after checking.
  3. Replace only space-unit with 1rem. The paddings of both cards stretch together. After confirmation, restore to .5rem.
  4. Change only the notice-color of bad from 20px to #1d5f91. The sentence will change from green to blue. After confirmation, restore it to 20px.
  5. Delete only the notice-color declaration of bad. This time a purple fallback is used. Compare with the blue result from the previous step and restore the original declaration.

Diagnose missing or incorrect variables

  • In developer tools, check if the variable name and value actually exist in that element. Look at spelling, capitalization, and declaration scope first.
  • Check whether descendants or other rules overwrite the same variable. It may not be an error for a component to retain its unique values even if its common value changes.
  • Check whether the variable is referenced in the actual property and whether the units and type of the substituted value are correct.
  • If a value is created through multiple variables, check the calculated element and inherited value. Also avoid declarations that refer to the same variable as themselves or that refer circularly.

You can take advantage of variables by distinguishing between common colors and spacing of the design and exceptions for each component, and checking the results before and after the change. Rather than increasing the number of variable names, making it clear which values are shared in which scope is more helpful for maintenance.

More in This Category
CSS Tutorial / Organize CSS with Shared Styles, Nesting, and Layers

CSS Tutorial / Organize CSS with Shared Styles, Nesting, and Layers

Refactor a small stylesheet without changing its layout, compare nesting and cascade layers, and check relative file paths and selector specificity.

CSS Tutorial / CSS Width, Height, min/max Limits, and box-sizing

CSS Tutorial / CSS Width, Height, min/max Limits, and box-sizing

Compare content-box and border-box sizing, percentage heights, fluid width limits, and the consequences of fixed heights for growing text.

CSS Tutorial / Fluid CSS Layouts with min, max, and clamp

CSS Tutorial / Fluid CSS Layouts with min, max, and clamp

Build a bounded fluid layout, compare flexible sizes and spacing, and check that longer content fits without introducing horizontal page scrolling.

CSS Tutorial / CSS Syntax: Selectors, Declarations, Comments, and Errors

CSS Tutorial / CSS Syntax: Selectors, Declarations, Comments, and Errors

Read and write CSS rules, use comments, and distinguish syntax errors from selectors that do not match and declarations overridden by other rules.

CSS Tutorial / Apply CSS with Inline, Internal, and External Styles

CSS Tutorial / Apply CSS with Inline, Internal, and External Styles

Compare three ways to apply CSS, create a working external stylesheet, and diagnose common file-path and loading problems.

CSS Tutorial / Check Responsive CSS with Width, Zoom, and Keyboard Tests

CSS Tutorial / Check Responsive CSS with Width, Zoom, and Keyboard Tests

Follow a repeatable browser checklist, test breakpoint boundaries and longer text, and record layout problems before changing CSS rules.

CSS Tutorial / Build a Responsive Website with HTML and CSS

CSS Tutorial / Build a Responsive Website with HTML and CSS

Create a complete two-file learning website with responsive cards and navigation, then test long text, keyboard focus, zoom, and reduced motion.

CSS Tutorial / Inspect CSS with DevTools: Styles and Computed

CSS Tutorial / Inspect CSS with DevTools: Styles and Computed

Use Styles, Computed, and the box model display to diagnose CSS, compare temporary edits, and transfer a confirmed change to the correct source file.

CSS Tutorial / CSS Borders, Rounded Corners, and Shadows

CSS Tutorial / CSS Borders, Rounded Corners, and Shadows

Build cards with borders and shadows, compare circles and ellipses, and inspect how ancestor overflow changes visible shadows.

CSS Tutorial / CSS position: relative, absolute, fixed, and sticky

CSS Tutorial / CSS position: relative, absolute, fixed, and sticky

Compare positioning modes in a complete example, identify containing blocks, and troubleshoot sticky limits and overlapping content.