Custom properties and modern CSS

You pick a brand blue, type it into forty different rules, and ship the site. A month later marketing wants a slightly different blue. Now you are hunting through every stylesheet, hoping you catch all forty and do not miss the two that were spelled with a capital letter. Custom properties fix this: you name the colour once, use the name everywhere, and change it in one place. That single habit is the start of theming, design tokens, and stylesheets that survive a redesign.
Declaring and using a variable
A custom property is a value you give a name so you can reuse it. You write the name with two dashes in front, set it to a value, and later read it back with var():
:root {
--color-brand: #2563eb; /* declare the value once, under a name */
}
.button {
background: var(--color-brand); /* read it back here */
}
.link {
color: var(--color-brand); /* and here, same value */
}--color-brand is the name (the two dashes are required), and var(--color-brand) is how you use it. Declaring it on :root (which is the <html> element, the top of the page) makes the name available everywhere. Change the one line on :root and every rule that reads it updates.
--color-brand, then read it with var(--color-brand). Put it on :root so the whole page can use it. Change the one line and every rule that reads the name updates with it. They cascade and inherit
If you have used variables in another language, this next part is the surprise: a custom property is not fixed. It follows the same rules as any other CSS property, which means it inherits down to child elements, and you can give it a different value in one place without touching the rest.
:root {
--color-text: #1f2937; /* the default for the whole page */
}
.callout {
--color-text: #92400e; /* a different value, only inside .callout */
color: var(--color-text);
}
p {
color: var(--color-text); /* dark grey normally, amber inside a callout */
}The same var(--color-text) gives you dark grey in most paragraphs but amber inside a callout, because the callout set its own value. This is what makes theming possible, and you have seen the cascade behind it in how CSS works.
var(--color-text) can be grey in most paragraphs and amber inside a callout. Redefine the name on a container and everything inside it picks up the new value. Fallbacks and calc()
var() can take a second value, used only if the custom property was never set. This is the fallback, and it keeps a rule working even when a token is missing:
.card {
gap: var(--gap, 1rem); /* use --gap if it exists, otherwise 1rem */
}You can also do maths with a custom property using calc(). If you have a base spacing value, you can build a bigger one from it instead of hard-coding a second number:
:root {
--space-md: 16px;
}
.section {
padding: calc(var(--space-md) * 2); /* 32px, derived from the base */
}Deriving values like this means one change to --space-md ripples through everything built on top of it.
var(--gap, 1rem) uses the token if it exists and falls back to 1rem if it does not, so a rule keeps working when a value is missing. And calc() lets you build values from a token, like calc(var(--space-md) * 2) for double the base spacing. Change the base and everything built on it follows. Theming
Because a custom property can be redefined lower down the tree, you can build a dark theme by changing the tokens under a class, and leave every component rule alone:
:root {
--color-bg: #ffffff;
--color-text: #1f2937;
}
.theme-dark {
--color-bg: #0f172a; /* same names, new values */
--color-text: #e2e8f0;
}
body {
background: var(--color-bg);
color: var(--color-text);
}Add class="theme-dark" to the page and every rule that reads --color-bg or --color-text flips to the dark values. You never touched the body rule, or any component, to make the theme switch. That is the payoff of naming your colours as tokens instead of writing the hex codes directly.
.theme-dark. Set class="theme-dark" and every rule reading those tokens flips, without editing a single component. That is why you name your colours as tokens instead of typing the hex code straight into each rule. 
