- INDEX
They let us define variables in CSS that can be reused throughout the stylesheet.
-
they're also called custom properties
- they're called like that as they act as css-properties where they're
inheritedand theycascade(override based on specificity)
- they're called like that as they act as css-properties where they're
-
It enables us to abstract values and work with them in a more dynamic way like
propsinReact<div style="--primary: red"></div>
div { color: var(--primary); }
- It's different than writing inline style directly, as it make the code abstract and maintainable
-
Difference between CSS variables and Sass variables:
-
Declaring them is usually in the
:rootselector (global scope) or in the element itself (local scope):root { --primary: blue; }
- because the
:rootpseudo-class represents the root<html>element and is identical to the selectorhtml, except that its specificity is higher (because:rootis apseudo-classselector andhtmlis atypeselector)- We do this so that the variable is available everywhere in the stylesheet (Global scope)
- We can use them because they're inherited from the
rootelement
- because the
:rootis an alias forhtml, so by hanging CSS variables on the top-level element, they become globally available (every element, after all, is below the root HTML tag). But you can attach CSS variables to any element, not just the root one!
-
Using css variables is done by using the
var()function:.box { background-color: var(--primary); }
"Not Global": A common misconception is that CSS variables are "global". When we attach a CSS variable to an element, it's only available to that element and its children.
Rules of scope like in Javascript also applies in css variables, but:
-
Sassvariables are scoped on{}blocks (block-scoped) (lexical-scoped) -
CSSvariables are scoped on elements (element(local)-scoped) (dynamic-scoped)-
this is great if you want the variable-value to only be applied in this element and elements inside it (its descendant elements) only
-
also it's great if you want to override the variable-value in a specific element and its descendant elements only
-
we can do this by:
-
declaring the variable in the element itself
div { --primary: red; }
-
or declaring it in the inlined-style of the element
<div style="--primary: red"></div>
-
or declaring it using javascript
document.documentElement.style.setProperty('--primary', 'red');
-
-
-
/* global scope */
:root {
--main-bg-color: coral;
}
/* local scope */
#div1 {
--main-bg-color: coral;
background-color: var(--main-bg-color);
}It's a way to provide a default value for the variable if it's not defined, but if it's invalid, the browser will set the value to unset which is equivalent to initial and use the fallback value
-
when using the variable, We can use a Fall-back variable/ value after a comma (
,) and you can make many fallbacksdiv { color:var(--primary, orange) /* to request the fallback value manually -> use (initial) */ color: initial }
-
Another way:
@property --primary { syntax: '<color>'; /* Type of the value (optional) */ inherits: true; initial-value: orange; }
-
DRY fallback strategies:
-
use the variable in the fallback value
div { --color-initial: black; color: var(--color, var(--color-initial)); }
-
Pseudo-private properties
div { --__color: var(--color, black); color: var(--__color); }
-
Using
@property@property --color { syntax: '<color>'; inherits: true; initial-value: black; } div { color: var(--color); }
-
By default, all CSS variables are inheritable and cascade. This is why CSS variables hung on :root are available globally.
-
css variables are inherited from the parent element if it's declared in the parent element to the (
classes/pseudo-elements/pseudo-classes/IDs) in the same elementbutton { --color: red; color: var(--color); } button:hover { background: black; color: var(--color); /* red -> inherited */ } button.pink { border-color: #f06; color: var(--color); /* red -> inherited */ } button.pink:hover { background: #f06; color: var(--color); /* red -> inherited */ }
-
you can disable inheritance by:
-
setting the property to
initialon the element itself:root { --corner-size: 2em; } p { border-radius: initial; /* won't inherit and will be initial value */ } p .text { --corner-size: inherit; /* Will be inherited */ }
-
Or register property using:
@property:-
@propertyallows us to register ourproperties/variablesand control how they behave (configure it), but watch out for browser support<style> @property --text-color { syntax: '<color>'; inherits: false; initial-value: black; } main { --text-color: deeppink; color: var(--text-color); } section { color: var(--text-color); } </style> <main> This text will have a color of deeppink. <section>This text will have a color of black (initial value).</section> </main>
-
-
IACVT: it's when calling the variable result in error (due to empty value or wrong value or unsupported value by browser), so the value will be set to unset which is equivalent to initial

Parse time: CSS is read and converted to CSSOM; computed value: relative units resolved to
px; used value: final values for painting, % widths resolved topx.
/* Ex: */
div {
background: red;
--accent-color: 42deg; /* not a valid color */
background: var(--accent-color, orange);
/* result will be (color-transparent) */
/* or */
background: red;
background: var(
--accent-color,
42deg
); /* no value for --accent-color variable, and 42deg is not a valid color for the fallback */
}- This is because the
--accent-colorvariable is not a valid color, so the browser will set the value tounsetwhich is equivalent toinitialwhich is equivalent totransparent.- It didn't fallback to
orangebecause the variable is not a valid color - It also didn't use the
initialvalue of the property (red) because it was overridden by the variable tounset
- It didn't fallback to
-
When we have a cycle in the variable-value, the browser will set the value to
unsetwhich is equivalent toinitialdiv { --primary-color: var(--secondary-color); --secondary-color: var(--primary-color); }
-
Here, the browser will set the value of
--primary-colortounsetwhich is equivalent toinitialbecause it's dependent on--secondary-colorwhich is dependent on--primary-color, and the cycle will continue forever- Note: the browser will not throw an error, it will just set the value to
unsetwhich is equivalent toinitial
- Note: the browser will not throw an error, it will just set the value to
-
-
Space Toggles Hack: it's a way to toggle between two values using a space character

-
For no space in the variable-value:
- Color will be
orange(indicating no value for --color)
- Color will be
-
For space in the variable-value:
- Color will be
unset(transparent) (indicating error for --color) because the space character is not a valid color - The space character is used to toggle between the two values
- Color will be
In css variables, you can make the variable contains a number, a string, or an image, but if you tried to mix them, it will not work (it works with SASS variables though!)
:root {
/* works ✅ */
--height: 100vh;
height: var(--height);
/* Doesn't work ❌ */
--height: 100;
height: var(--height) vh;
/* Fix (works) ✅ */
--height-scale: 100;
--height: calc(var(--height-scale) * 1vh);
}-
Tokens are the values that can be used in the variable, and they can't contain parts of tokens
-
You can make the variable contains the following:

-
the opening part of a css function, ex:
linear-gradient(html { --type: "linear-gradient("; background: var(--type) white, black ); color: red; }
-
a number without a unit like
--p: 65;calc(var(--p) * 100vw);
-
use variables for pure-data like
65, not css-values like65% -
this way you can use the number for any css-value with any unit unlike if you made the variable with css-value
- but watch out for numbers as string or integers as here we don't have type-coercion
-
-
-
One of the main difference between the old sass variables and the new css custom properties is that in custom-properties, you can use
Javascriptto change the variables values after the compilation and the loading in the browser which will enable more dynamic styling -
You can get / set css-variable-value of an element in javascript by having the variable declared in the root before, then change it's value locally in the element styles:
// Get variable from inline style element.style.getPropertyValue('--foo'); // Get variable from wherever getComputedStyle(element).getPropertyValue('--foo'); // Set variable on inline style element.style.setProperty('--foo', 38 + 4); // --------------------------------------------------- // // or change it in the root element let root = document.documentElement; document.addEventListener('pointermove', evt => { let x = evt.clientX / innerWidth; let y = evt.clientY / innerHeight; root.style.setProperty('--mouse-x', x); root.style.setProperty('--mouse-y', y); });
-
Reactivity
-
CSS variables are reactive—when their value changes, any properties that reference that value also change. In this case, clicking the button causes us to update the value for --inflated-size, which automatically updates the button's font-size property.
<button id="button">Click me!</button>
:root { --inflated-size: 1em; } button { font-size: var(--inflated-size); }
const button = document.getElementById('button'); button.addEventListener('click', () => { const inflatedSize = parseFloat(getComputedStyle(button).getPropertyValue('--inflated-size')); button.style.setProperty('--inflated-size', inflatedSize * 1.1 + 'em'); // now the button will be 10% bigger as the value of the variable is increased by 10% });
-
This is very useful when working with dynamic styles in JS frameworks like
ReactorVueconst App = () => { const [color, setColor] = useState('red'); return ( <div style={{ '--color': color }}> <button onClick={() => setColor('blue')}>Change color</button> </div> ); };
-
-
CSS Reset variables
:root { /* 1. colors */ --primary: #333; --secondary: #f06; /* 2. font */ --font: 'Arial', sans-serif; /* 3. spacing */ --padding: 1em; --margin: 1em; --max-width: 1200px; /* 4. border-radius */ --radius: 5px; /* 5. box-shadow */ --shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 6. transition */ --transition: 0.3s ease all; }
-
Responsive design
body { font-size: calc(90% * var(--font-size-scale)); } @media (min-width: 800px) { :root { --font-size-scale: 1.2; } }
-
Color Control
-
We can use css variables to control the color intensity to create dynamic palettes
article { --hue: calc(50 - var(--intensity) * 10); --color: hsl(var(--hue, 200) 70% 40%); background: var(--color); }
<article style="--intensity: 1">yellow</article> <article style="--intensity: 2">brown</article> <article>default blue</article> <article style="--intensity: 3">red</article>
-
-
We can't depend on fallbacks for browsers that don't support css variables (doesn't support
var()function), so we can:-
use postcss to convert the css variables to static values
-
Or, use
@supportsto check if the browser supports css variables or not.box { background: red; color: blue; } @supports (--css: variables) { .box { background: var(--primary); color: var(--secondary); } }
-




