a.k.a. CSS Variables
brand colors, sizes, fonts, etc…
button-background, primary-grid…
hsl(331deg 42% 30%)
color-mix(in oklab, darkRed 73.29%, mediumBlue)
deep-raspberry
’
best-color
’
action
’
link-text
’ button-background
’
--deep-raspberry: hsl(322, 92%, 24%);
--best-color: var(--deep-raspberry);
--action: var(--best-color);
--link-text: var(--action);
--button-background: var(--action);
(in the cascade)
--
name:
value
;
--dashed-idents
For mixing custom & built-in identifiers
<dashed-ident>
s are <custom-ident>
s, but not vice versabuilt-in-property: value;
--custom-property: value;
-
webkit
-property
-
moz
-property
-
ms
-property
--property
-<empty>-property
.warning {
--state-color: maroon;
}
.alert {
border: medium solid var(--state-color);
}
var(
--property
)
var
functionbutton {
background-color: var(--button-background);
}
html { /* or :root */
--all-my: variables;
--defined: on the;
--root: element;
}
result: 0-1 cascaded values
result: one specified value
value from direct parent
demo:
[demo]:
background
(including text colors)
(including anything box-related)
span
s) If I added a
span
in the middle of this paragraph, what styles would I expect to apply automatically inside thespan
so that the span doesn’t change anything?
— Internal Monologue
[demo]:
all:initial
on the span/* 😭 Nooooooo */
html { box-sizing: border-box; }
* { box-sizing: inherit; }
/* 🥳 Much better */
* { box-sizing: border-box; }
Each property has an initial value, defined in the property’s definition table.
— Cascade & Inheritance, § 7.1. Initial Values
[Demo lookup]:
If the property is not an inherited property, and the cascade does not result in a value, then the specified value of the property is its initial value.
— Cascade & Inheritance, § 7.1. Initial Values
html {
font-size: var(--i-never-specified-a-value);
}
like Undefined
in JS
var()
become…
Invalid At Computed Value Time
_unset_
--my-variable: initial;
color: var(--my-variable, CanvasText);
var(--my-color, teal)
/* fallback: 'Georgia, Palatino, serif' */
var(--my-font, Georgia, Palatino, serif)
var(--btn-color, var(--action, teal))
all
shorthandall
shorthand propertyinitial
inherit
unset
(inherit
or initial
)revert
(previous cascade origin)revert-layer
(previous cascade layer)all
initial
& inherit
choose your default, explicitly
unset
whichever default is appropriate
unset
insteadinitial
or inherit
ed valuebody { margin: unset; }
initial
value,display:inline
revert
defer to previous origin
revert
revert-layer
defer to previous layer (or previous origin)
revert-layer
unset
or one of the revert
valueshtml {
--color: teal;
--color: initial;
background: black;
background: var(--color, red);
}
0+
declared values)0|1
cascaded value)1
specified value)1
computed value)1
used value)1
actual value)p {
color: hotPink; /* rgb(255, 105, 180) */
border-color: currentColor; /* rgb(255, 105, 180) ? */
font-size: 1.2em; /* 19.2px ? */
/* these require formatting */
width: 80%; /* still 80% */
height: auto; /* still auto */
}
currentColor
!)and this is not legal advice
currentColor
Inherits as Keyword
It should re-calculate when the color changes!
p {
--my-size: 1.5lh; /* 1.5em */
--my-color: hotPink; /* hotPink */
}
lh
knowing what line to measurehtml {
--my-size: 1.5lh; /* 1.5lh */
--calc: calc(var(--my-size) * 2);
}
p {
/* --calc: calc(1.5lh * 2) */
/* not calc(var(--my-size) * 2) */
}
var()
p {
/* inherited calc(1.5lh * 2) */
color: var(--calc); /* unset */
min-height: var(--calc); /* 3lh */
}
demo:
@property --brand-color {
syntax: "<color>";
inherits: true;
initial-value: hotPink;
}
syntax
, inherits
, and (usually) initial
inherits
true
or false
/* replace 'initial' with any default value */
* { --this-property: initial; }
syntax
…
Supported Names
Or "*"
for ‘universal syntax’
(depending on syntax
)
demo:
.yes-number-to-length {
--number: 1;
--length: calc( var(--number) * 1em);
/* not `var(--number)em` */
}
calc()
.no-length-to-number {
--length: 1em;
--number: calc( var(--length) / 1em );
}
initial-value
…
Must be Absolute
(when the syntax
is not *
)
*
~= Browser Default
initial
values equivalent to spec initialflex: 1
on a non-flex item)display:inline
any time it’s not in flow)html {
color-scheme: dark light;
/* ~ used: dark; */
}
div {
width: 80%;
/* ~ used: 1049.6px; */
/* ~ actual: 1050px; */
}
:root { --btn-color: maroon; }
html
/:root
, then inherit):root { --btn-color: maroon; }
/* html { --btn-color: maroon; } */
html
or :root
Like <svg>
or <xml>
or <html>
demo:
demo:
* { --btn-color: hotPink; }
button { --btn-color: maroon; }
button[type='submit'] {
--btn-type: seaGreen;
}
[aria-pressed=true] {
--btn-state: rebeccaPurple;
}
[data-grid] > * { --grid-area: main; }
button {
background: var(--btn-context, maroon);
}
main { --btn-context: mediumVioletRed; }
[type='submit'] { --btn-context: steelBlue; }
button:focus { --btn-context: seaGreen; }
<button style="--btn-color: red;">
You're not the boss of me
</button>
/* --btn-color is not being used */
button {
background: seaGreen;
color: white;
}
[attr]
➡ Presence (even if empty)[attr
=
"..."]
➡ Exact match[attr
*
="..."]
➡ Any match[attr
~
="..."]
➡ Space-delimited (like classes)[attr
|
="..."]
➡ Hyphen-delimited[attr
^
="..."]
➡ Starts with…[attr
$
="..."]
➡ Ends with…[attr="..."
i
|
s
]
➡ Case sensitivity<!-- Element-attached/inline -->
<div style="--ease: var(--in-out-back);">
[style*='--ease'] {
--in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--in-out-back: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
html {
@media (prefers-color-scheme: dark) {
--os-mode: -1;
}
@media (prefers-color-scheme: light) {
--os-mode: 1;
}
}
[data-colors='light'] {
--html-mode: 1;
}
[data-colors='dark'] {
--html-mode: -1;
}
[data-colors] {
--mode: var(
--html-mode, var(
--user-mode, var(
--os-mode, 1
)
)
);
}
--user-mode
set via JS<section
class="sprite-demo"
:style="{
'--src': show.sprite.src,
'--columns': show.sprite.columns,
'--rows': show.sprite.rows,
}">...</section>
<div
v-for="action in show.actions"
:key="action.name"
:data-action="action.name"
:style="{
'--row': action.row,
}"
/>
(as we’ve known them)