1.
Introduction
This is a diff spec against
CSS Values and Units Level 4
1.1.
Module Interactions
This module extends
[CSS-VALUES-4]
which replaces and extends the data type definitions in
[CSS21]
sections
1.4.2.1
4.3
and
A.2
2.
Textual Data Types
See
CSS Values 4
§ 4 Textual Data Types
3.
Value Definition Syntax
See
CSS Values 4
§ 2 Value Definition Syntax
Additionally,
Boolean combinations of a conditional notation.
These are written using the

notation,
and represent recursive expressions of boolean logic
using keywords and parentheses,
applied to the grammar specified in brackets,
e.g.

to express
media queries
3.1.
Functional Notation Definitions
See
CSS Values 4
§ 2.6 Functional Notation Definitions
3.1.1.
Commas in Function Arguments
Functional notation
often uses commas
to separate parts of its internal grammar,
and occasionally other syntax
(such as
or
in
if()
).
However, some functions
(such as
mix()
allow values that, themselves,
could contain these argument-separating tokens.
These values
(currently


, and

are
free-form productions
To accommodate these sorts of grammars unambiguously,
the
free-form productions
can be optionally wrapped in curly braces {}.
These braces are syntactic, not part of the actual value,
and only serve to explicitly indicate the bounds of the production.
free-form production
can either
(after optional whitespace)
start with a
<{-token>
, or not:
If it does not start with a "{" token
The production does not match any top-level commas or {} blocks.
(The production stops parsing at that point,
so the comma or {} block is matched by the next grammar term instead;
probably the function’s own argument-separating comma.)
Individual usages of the production can define additional tokens
that are similarly restricted from matching at the top-level.
If it does start with a "{" token
The production matches just the {} block that the "{" token opens.
It represents the
contents
of that block,
ignoring the {} block wrapper itself.
Note:
General restrictions defined for a particular
free-form production
like

not matching

s,
apply regardless of whether it’s {}-wrapped or not.
For example, the grammar of the
random-item()
function is:
random-item


The
indicates comma-separated repetitions,
so randomly choosing between three keywords
would be written as normal for functions,
like:
font-family
random-item
--x
serif
sans-serif
monospace
);
However, sometimes the values you want to choose between
need to include commas.
When this is the case,
wrapping the values in {}
allows their commas to be distinguished
from the function’s argument-separating commas:
font-family
random-item
--x
Times
serif
},
Arial
sans-serif
},
Courier
monospace
});
This randomly chooses one of three font-family lists:
either
Times, serif
, or
Arial, sans-serif
, or
Courier, monospace
This is not all-or-nothing;
you can use {} around
some
arguments that need it,
while leaving others bare when they don’t need it.
You are also allowed to use {} around a value when it’s not strictly required.
For example:
font-family
random-item
--x
Times
serif
},
sans-serif
monospace
});
This represents choosing between three font-family lists:
either
Times, serif
, or
sans-serif
, or
monospace
However, this {}-wrapping is
only
allowed for some function arguments—​those defined as
free-form productions
It’s not valid for any other productions;
if you use {} around other function arguments,
it’ll just fail to match the function’s grammar
and become invalid.
For example, the following is
invalid
background-image
linear-gradient
to left
red
},
magenta
);
Note:
Because {} wrappers are allowed even when not explicitly required,
they can be used defensively around values
when the author isn’t sure if they’ll end up containing commas or not,
due to
arbitrary substitution functions
like
var()
For example,
font-family: random-item(--x, {var(--list1)}, monospace)
will work correctly
regardless of whether the
--list1
custom property
contains a comma-separated list or not.
Functional notations
are serialized without {} wrappers whenever possible.
The following generic productions are
free-form productions
For legacy compat reasons,
the

defined for the fallback value of
var()
is a
non-strict free-form production
It ignores the rules restricting what it can contain
when it does not start with a "{" token:
it is allowed to contain commas and {} blocks.
It still follows the standard
free-form production
rules
when it
does
start with a "{" token, however:
the fallback is just the contents of the {} block,
and doesn’t include the {} wrapper itself.
Other contexts
may
define that they use
non-strict free-form productions
but it
should
be avoided unless necessary.
3.2.
Boolean Expression Multiplier

Several contexts
(such as
@media
@supports
if()
, ...)
specify conditions,
and allow combining those conditions with boolean logic (and/or/not/grouping).
Because they use the same non-trivial recursive syntax structure,
the special

production represents this pattern generically.
The

notation wraps another value type in the square brackets within it,
e.g. ]>,
and represents that value type alone as well as
boolean combinations
using the
not
and
, and
or
keywords
and grouping parenthesis.
It is formally equivalent to:

> = not

and
or
=


The

production represents a true, false, or unknown value.
Its value is resolved using 3-value Kleene logic,
with top-level unknown values
(those not directly nested inside the grammar of another

resolving to false unless otherwise specified;
see
Appendix B: Boolean Logic
for details.
For example, the
@container
rule allows a wide variety of tests:
including size queries, style queries, and scroll-state queries.
All of these are arbitrarily combinable with boolean logic.
Using

, the grammar for an
@container
query
could be written as:
=
=

style

scroll-state

=

=

=

The

branch of the logic allows for future compatibility—​unless otherwise specified new expressions in an older UA
will be parsed and considered “unknown”,
rather than invalidating the production.
For consistency with that allowance,
the

term in a

should be defined to match

3.3.
Specifying CSS Syntax in CSS: the

type
Some features in CSS,
such as the
attr()
function
or
registered custom properties
allow you to specify how
another
value is meant to be parsed.
This is declared via the

production,
which resembles a limited form of the CSS
value definition syntax
used in specifications to define CSS features,
and which represents a
syntax definition

= '*'







'<' transform-list '>'

= '<'

'>'


= angle
color
custom-ident
image
integer
length
length-percentage
number
percentage
resolution
string
time
url
transform-function

= '|'

= [ '#'
'+' ]



consists of either

between <> (angle brackets),
which maps to one of the
supported syntax component names
or an

, which represents any
keyword
Additionally,

may contain a
multiplier
which indicates a
list
of values.
Note:
This means that

and
length
are two different types:
the former describes a

whereas the latter describes a
keyword
length
Multiple

s may be
combined
with a

causing the syntax components to be matched
against a value
in the specified order.
| | auto
The above, when parsed as a

would accept

values,

values,
as well as the keyword
auto
The

represents the
universal syntax definition
The

production
is a convenience form equivalent to
+
Note that

may not
be followed by a

Whitespace
is not allowed
between the angle bracket

s (
and the

they enclose,
nor is
whitespace
allowed to precede a

Note:
The
whitespace
restrictions also apply to


is a

whose value successfully
parses
as a

and represents the same value as that

would.
Note:

mostly exists for historical purposes;
before

was defined,
the
@property
rule used a

for this purpose.
3.3.1.
Parsing as

The purpose of a

is usually to specify how to parse another value
(such as the value of a
registered custom property
or an attribute value in
attr()
).
However, the generic
parse something according to a CSS grammar
algorithm
returns an unspecified internal structure,
since parse results might be ambiguous
and need further massaging.
To avoid these issues and get a well-defined result,
use
parse with a
To
parse with a

given a
string
or
list
or
component values
values

value
syntax
and optionally an element
el
for context,
perform the following steps.
It returns either CSS values,
or the
guaranteed-invalid value
Parse a list of component values
from
values
and let
raw parse
be the result.
If
el
was given,
substitute arbitrary substitution functions
in
raw parse
and set
raw parse
to that result.
parse
values
according to
syntax
with a
value treated as

and let
parsed result
be the result.
If
syntax
used a
combinator,
let
parsed result
be the parse result from the first matching clause.
If
parsed result
is failure,
return the
guaranteed-invalid value
Assert:
parsed result
is now a well-defined list of one or more CSS values,
since each branch of a

defines an unambiguous parse result
(or the
syntax is unambiguous on its own).
Return
parsed result
Note:
This algorithm does not resolved the parsed values
into
computed values
the context in which the value is used will usually do that already,
but if not,
the invoking algorithm will need to handle that on its own.
4.
Extensions to Level 4 Value Types
See
CSS Values and Units Level 4
4.1.
Resource Locators: the

type
See
CSS Values 4
§ 4.5 Resource Locators: the type
4.1.1.
Request URL Modifiers

s are

that affect the

’s resource
request
by applying associated
URL request modifier steps
See
CSS Values 4
§ 4.5.4 URL Processing Model
This specification defines the following

s:





cross-origin
anonymous
use-credentials

integrity


referrer-policy
no-referrer
no-referrer-when-downgrade
same-origin
origin
strict-origin
origin-when-cross-origin
strict-origin-when-cross-origin
unsafe-url

cross-origin
anonymous
use-credentials
The
URL request modifier steps
for this modifier given
request
req
are:
Set
req
’s
mode
to "cors".
If the given value is
use-credentials
set
req
’s
credentials mode
to "include".
Otherwise, set
req
’s
credentials mode
to "same-origin".

integrity

The
URL request modifier steps
for this modifier given
request
req
are to set
request
’s
integrity metadata
to the given


referrer-policy
no-referrer
no-referrer-when-downgrade
same-origin
origin
strict-origin
origin-when-cross-origin
strict-origin-when-cross-origin
unsafe-url
The
URL request modifier steps
for this modifier given
request
req
are to set
request
’s
referrer policy
to the
ReferrerPolicy
that matches the given value.
4.2.
2D Positioning: the

type
The

value specifies the position
of an
alignment subject
(e.g. a background image)
inside an
alignment container
(e.g. its
background positioning area
as a pair of offsets between the specified edges
(defaulting to the left and top).
Its syntax is:





left
center
right
top
bottom
x-start
x-end
y-start
y-end
block-start
block-end
inline-start
inline-end


left
center
right
x-start
x-end
&&
top
center
bottom
y-start
y-end
left
center
right
x-start
x-end

top
center
bottom
y-start
y-end

block-start
center
block-end
&&
inline-start
center
inline-end
start
center
end

left
right
x-start
x-end

&&
top
bottom
y-start
y-end

block-start
block-end

&&
inline-start
inline-end

start
end

If only one value is specified (

),
the second value is assumed to be
center
If two values are given (

),

as the first value represents
the horizontal position as the offset between
the left edges of the
alignment subject
and
alignment container
and a

as the second value represents
the vertical position as an offset between their top edges.
If both keywords are one of
start
or
end
the first one represents the
block axis
and the second the
inline axis
Note:
A pair of axis-specific keywords can be reordered,
while a combination of keyword and length or percentage cannot.
So
center left
or
inline-start block-end
is valid,
while
50% left
is not.
start
and
end
aren’t axis-specific,
so
start end
and
end start
represent two different positions.
If four values are given (

then each

represents an offset between
the edges specified by the preceding keyword.
For example,
background-position: bottom 10px right 20px
represents a
10px
vertical offset up from the bottom edge
and a
20px
horizontal offset leftward from the right edge.
Positive values represent an offset
inward
from the edge of the
alignment container
Negative values represent an offset
outward
from the edge of the
alignment container
The following declarations give the stated (horizontal, vertical)
offsets from the top left corner:
background-position
left
10
px
top
15
px
/* 10px, 15px */
background-position: left top
/* 0px, 0px */
background-position:
10
px
15
px
/* 10px, 15px */
background-position: left
15
px
/* 0px, 15px */
background-position:
10
px
top
/* 10px, 0px */

s can also be relative to other corners than the top left.
For example, the following puts the background image
10px from the bottom and 3em from the right:
background-position
right
em
bottom
10
px
The
computed value
of a

is
a pair of offsets (horizontal and vertical),
each given as a computed

value,
representing the distance between the left edges and top edges (respectively)
of the
alignment subject
and
alignment container


value specifies the size of the offset
between the specified edges of the
alignment subject
and
alignment container
For example, for
background-position: 2cm 1cm
the top left corner of the background image is placed
2cm to the right and 1cm below
the top left corner of the
background positioning area

for the horizontal offset is relative to
width of
alignment container
width of
alignment subject
).

for the vertical offset is relative to
height of
alignment container
height of
alignment subject
).
For example, with a value pair of
0% 0%
the upper left corner of the
alignment subject
is aligned with
the upper left corner of the
alignment container
A value pair of
100% 100%
places
the lower right corner of the
alignment subject
in the lower right corner of the
alignment container
With a value pair of
75% 50%
the point 75% across and 50% down the
alignment subject
is to be placed at the point 75% across and 50% down the
alignment container
Diagram of the meaning of
background-position: 75% 50%
top
right
bottom
left
Offsets the top/left/right/bottom edges (respectively)
of the
alignment subject
and
alignment container
by the specified amount (defaulting to
0%
in the corresponding axis.
y-start
y-end
x-start
x-end
Computes the same as the physical edge keyword
corresponding to the
start
end
side
in the
axis.
block-start
block-end
inline-start
inline-end
Computes the same as the physical edge keyword
corresponding to the
start
end
side
in the
block
inline
axis.
center
Computes to a
50%
offset in the corresponding axis.
Unless otherwise specified, the
flow-relative
keywords are resolved
according to the
writing mode
of the element on which the value is specified.
Note:
The
background-position
property also accepts a three-value syntax.
This has been disallowed generically because it creates parsing ambiguities
when combined with other length or percentage components in a property value.
Need to define how this syntax would expand to the longhands of
background-position
if e.g.
var()
is used for some (or all) of the components.
[Issue #9690]
When specified in a grammar alongside other keywords,

s, or

s,

is
greedily
parsed;
it consumes as many components as possible.
For example,
transform-origin
defines a 3D position
as (effectively)


A value such as
left 50px
will be parsed as a 2-value

with an omitted z-component;
on the other hand,
a value such as
top 50px
will be parsed as a single-value

followed by a

When serializing the
specified value
of a

If only one component is specified:
If two components are specified:
Keywords are serialized as keywords.

s are serialized as

s.
Components are serialized horizontal first, then vertical.
If four components are specified:
Keywords and offsets are both serialized.
Components are serialized horizontal first, then vertical;
alternatively
block-axis
first, then
inline-axis
Note:

values are never serialized as a single value,
even when a single value would produce the same behavior,
to avoid causing parsing ambiguities in some grammars
where a

is placed next to a

such as
transform-origin
The
computed value
of a

is serialized as a pair of

representing offsets from the left and top edges, in that order.
4.2.3.
Combination of

Interpolation
of

is defined as
the independent interpolation of each component (x, y)
normalized as an offset from the top left corner
as a

Addition
of

is likewise defined as
the independent
addition
each component (x, y)
normalized as an offset from the top left corner
as a

5.
Interpolation Progress Calculations: the
progress()
notation
The
progress()
functional notation
represents the proportional distance
of a given value (the
progress value
from one value (the
progress start value
to another value (the
progress end value
),
each represented as a
calculation
It is a
math function
and can be input into other calculations
such as a
math function
or a
mix notation
The syntax of
progress()
is defined as follows:
progress
()
progress



where the first, second, and third

values represent
the
progress value
progress start value
, and
progress end value
respectively.
The argument
calculations
can resolve to any


, or

but must have a
consistent type
or else the function is invalid.
Do we need a
percent-progress()
notation,
or do enough places auto-convert that it’s not necessary?
Should progress() functions clamp to 0-100%?
[Issue #11825]
Note:
The
progress()
function is essentially syntactic sugar
for a particular pattern of
calc()
notations.
6.
Weighted Average Notations: the *-mix() family
Several
mix notations
in CSS
allow representing the weighted average of a set of values.
These
functional notations
follow the syntactic pattern:
mix
()
mix
options
value
&&

where the
options
can provide type-specific mixing options,
and each
value
and optional

pair in the argument list
is a
mix item
representing
an input to the mix and its weight in the average.
The
mix notations
in CSS include:
6.1.
Normalizing Mix Percentages
If the sum of the mix percentages are greater than 100%,
they are scaled down;
if it is less, any remaining percentage is distributed to values whose

is omitted,
or else assigned to a “none” type value as defined by the specific
mix notation
To
normalize mix percentages
given
list
of
mix items
items
(a value and optional percentage, each between 0% and 100% if specified)
an optional
force normalization
flag
(defaulting to false),
returning a list of
mix items
with normalized percentages
and a “leftover” percentage:
Let
specified sum
be the sum of the percentages specified in
items
(clamped to 100%),
or 0% if the percentages are omitted for all items.
For each omitted percentage in
items
set it to
100
specified sum
number of omitted percentages
Let
total
be the sum of the percentages of all the items.
If
total
is greater than 100%,
or if
total
is greater than 0% and the
force normalization
flag is true,
multiply every percentage in
items
by
100
total
If
total
is less than 100%,
let
leftover
be
100
total
Otherwise, let
leftover
be 0%.
Return
items
and
leftover
Note:
At the end of this algorithm, every percentage is set,
and the sum of all percentages is either 0%
(if they were all specified as 0% to begin with)
or 100%.
The
leftover
value will be applied to the usage-specific “none” type value
mixed into the final result.
6.2.
Mix Resolution
The
used value
of a valid
mix notation
is
the weighted average of its arguments,
as specified by the specific notation.
The
computed value
is the
used value
if it is possible to calculate.
Otherwise,
it is the
mix notation
itself,
with its arguments computed individually.
6.3.
Weighted Average of Numeric and Dimensional Values: the
calc-mix()
notation
The
calc-mix()
mix notation
represents a weighted average of numeric or dimensional value.
Like
calc()
, it is a
math function
with the following syntactic form:
calc-mix
()
calc-mix

100
The

arguments can resolve
to any


, or

but must have a
consistent type
or else the function is invalid.
The result’s type will be the
consistent type
made consistent
with the type of the

values.
The
used value
of a valid
calc-mix()
is
the result of producing a weighted average of its

values,
with the weight of each item given by its corresponding

after
normalizing these mix percentages
(Any “leftover” mix percentage is applied to a consistently-typed zero value,
and thus effectively discarded.)
The
computed value
of a valid
calc-mix()
is
its
used value
if all of the

and

values in it
can be resolved,
and is a
calc-mix()
notation
with each of its

and

values computed individually
otherwise.
6.4.
Weighted Average of Transform Values: the
transform-mix()
notation
The
transform-mix()
mix notation
represents a weighted average of

with the following syntactic form:
transform-mix
()
transform-mix

&&
100
The
used value
of a valid
transform-mix()
is
the result of producing a weighted average of its

values,
with the weight of each item given by its corresponding

after
normalizing these mix percentages
Any “leftover” mix percentage is applied to an
identity transform
appended to the list.
The weighting can be calculated by
interpolating each

using its

weight as the interpolation progress
from the
identity transform
towards the

See
CSS Transforms 1
§ 9 Interpolation of Transforms
If every

weight can be fully resolved,
and the

s can be interpolated
without used-value-time information,
then the
computed value
is the
used value
it is otherwise the
transform-mix()
notation itself
with its arguments each computed according to their type.
transform-mix()
is, itself, a

7.
Interpolation Mapping Notations: the *-interpolate() family
Several
interpolation notations
in CSS
allow representing an interpolated value
corresponding to a certain amount of
interpolation progress
along a defined scale or mapping function
(the
interpolation map
).
The
functional notations
follow the syntactic pattern:
interpolate
()
interpolate
progress
&&
global-options
stop
between-options
stop
Where:
For example,
the following changes the background color of an element
depending on the width of the viewport:
background
color-interpolate
100
vw
in lch
200
px
: palegoldenrod
800
px
: palegreen
2000
px
: powderblue
);
In the following example,
the font size interpolates from a smaller size on small screens
to a larger size on large screens,
easing along the interpolation curve.
The author is storing the progress scale in a custom property
to be able to re-use it across multiple
font-size
declarations
throughout the document.
html
--font-scale
progress
100
vw
200
px
2000
px
ease-in-out
font-size
calc-interpolate
var
--font-scale
),
16
px
70
20
px
100
24
px
);
h1
font-size
calc-interpolate
var
--font-scale
),
1.2
rem
40
rem
100
rem
);
If anyone has better examples to punch in here let us know...
The
interpolation notations
in CSS include:
and finally the generic
interpolate()
notation,
which can represent the interpolation of any property’s values
(but only the property’s entire value, not individual components).
The
interpolate()
notation also has a variant that takes a set of keyframes.
It does this by referring to an
@keyframes
rule,
and pulling the corresponding property declaration out of that.
It would be nice to allow the other mix notations to take keyframe also,
but how would we represent a set of keyframes for a
component value
(rather than a full property value)?
Do we have enough use-cases to motivate adding
palette-interpolate()
palette-mix()
already handles transitions.
7.1.
Global Syntax of the *-interpolate() family
The generic syntax of the
interpolation notations
is as follows:
interpolate-function
()
interpolate-function

&&
by

&&

&&




||



These represent the
interpolation progress
and
interpolation map
as described below.
7.1.1.
Specifying the Interpolation Progress
The

value type represents
the
interpolation progress
in an
interpolation notation
Its syntax is:




'animation-timeline'
where:

Represents the
interpolation progress
as a percentage,
with
0%
computing to zero and
100%
computing to 1.

Represents the
interpolation progress
as a number directly.
Note:
This allows the use of the
progress()
notations
and other
math functions
that output a


Represents the
interpolation progress
as a dimension,
which is converted to a number as specified in
§ 7.2 Validation and Normalization
Note:
Using a

value as

requires at least some of the stops in the
interpolation map
to also use

positions of the same type.
See
§ 7.2 Validation and Normalization
<'animation-timeline'>
Represents the
interpolation progress
as the progress of the specified
animation timeline
The values
none
and
auto
are invalid.
[CSS-ANIMATIONS-2]
[WEB-ANIMATIONS-2]
Note:
Progress values below
0%
and above
100%
are unconventional, and potentially awkward, but valid.
For example, most
easing functions
, though they will accept any input,
are defined in consideration of progress defined between the [0,1] (i.e. [0%,100%]) range.
7.1.1.1.
Easing Interpolation Progress: the
by

argument
The
interpolation progress
given by

may be optionally modified
by the

specified after the
by
keyword.
It applies the specified
easing function
to the “timeline” of progress as a whole
by modifying the
interpolation progress
before it’s applied to the
interpolation map
analogous to
animation-timing-function
If omitted, defaults to
linear
7.1.2.
Defining the Interpolation Map
Similar to the
gradient functions
the
interpolation notations
define an
interpolation map
using a stop list,
associating
input positions
with
output values
Each
interpolation stop
represents, essentially,
a keyframe of the
interpolation map
values between
interpolation stops
are interpolated between these adjacent keyframes
in accordance with the

and

arguments applying between those keyframes.
The
interpolation map
values are defined as follows:

{1,2} :

Represents an
interpolation stop
associating the specified
input position
(s) with the specified
output values
As with the
gradient functions
if two

s are specified,
it is treated the same as two stops with the same





Note:

is not given a grammar here,
as the specific functions specify what their outputs are.

, however, is linked to

see
§ 7.2.1 Type Checking

When appearing in the first argument,
specifies the “default”
easing function
to be used between each stop.
When appearing between stops,
specifies the
easing function
to be used between the two surrounding stops,
overriding any default provided by a global

argument.
(It is analogous to
animation-timing-function
.)
If omitted, defaults to
linear
The
input progress value
to this
easing function
is the
segment interpolation progress
—​how far the
interpolation progress
is
between the
input positions
of the nearest stops preceding and following it.

When appearing in the first argument,
provides any type-specific interpolation options
that apply to every segment in the
interpolation map
(For example,
color-interpolate()
allows

.)
When appearing between stops,
provides any type-specific interpolation options
that apply to the interpolation segment between
the stops on either side of this argument,
overriding any default provided by a corresponding global

argument.
All positions before the first
interpolation stop
map to the first stop’s

and all positions after the last
interpolation stop
map to the last stop’s

In other words, the map fills outward from the first/last stop (just like gradients)—​it does not interpolate beyond them.
7.2.
Validation and Normalization
7.2.1.
Type Checking
Each

and

has a
type
which can be
proportional


, or
<'animation-timeline'>
or
absolute
(all other types, and
<'animation-timeline'>
).
The proportional types represent progress as a percentage;
any mix of these types is valid.
The
absolute
types represent progress in absolute dimensions,
and all

and

values that are not
proportional
must have a
consistent type
for the notation to be valid.
Additionally, an
interpolation notation
whose

is not
proportional
must have at least one
absolute

(in order to define the
interpolation range
),
or else the notation is invalid.
Note:
An
<'animation-timeline'>
has both a
proportional
type (the percentage progress)
as well as an
absolute
type (

stock::before
display
block
content
"To scale, the lengths of materials in stock are:"
stock > *
display
block
width
attr
length em
px
);
height
em
border
solid thin
margin
0.5
em
wood
background
orange
url
wood.png
);
metal
background
silver
url
metal.png
);
8.7.1.
Substitution
attr()
is an
arbitrary substitution function
similar to
var()
and so is replaced with the value it represents (if possible)
at
computed value
time;
otherwise, it’s replaced with the
guaranteed-invalid value
which will make its declaration
invalid at computed-value time
To
replace an attr() function
given a list of
arguments
Let
el
be the element that the style containing the
attr()
function
is being applied to.
Let
first arg
be the first

in
arguments
Let
second arg
be the

? passed after the comma,
or null if there was no comma.
Substitute arbitrary substitution functions
in
first arg
then
parse
it as


If that returns failure, jump to the last step (labeled FAILURE).
Otherwise, let
attr name
and
syntax
be the results of parsing
(with
syntax
being null if

was omitted),
processed as specified in the definition of those arguments.
If
attr name
exists as an attribute on
el
let
attr value
be its value;
otherwise jump to the last step (labeled FAILURE).
If
syntax
is the keyword
number
or an

value,
parse
attr value
against

If that succeeds, return the result;
otherwise, jump to the last step (labeled FAILURE).
Note:
No parsing or modification of any kind is performed on the value.
If
syntax
is null
or the keyword
raw-string
return a CSS

whose value is
attr value
Note:
No parsing or modification of any kind is performed on the value.
Substitute arbitrary substitution functions
in
attr value
with «"attribute",
attr name
» as the
substitution context
then
parse with a
attr value
, with
syntax
and
el
If that succeeds, return the result;
otherwise, jump to the last step (labeled FAILURE).
FAILURE:
If
second arg
is null,
and
syntax
was omitted,
return an empty CSS

If
second arg
is null,
return the
guaranteed-invalid value
Substitute arbitrary substitution functions
in
second arg
and return the result.
8.7.2.
Security
An
attr()
function can reference attributes
that were never intended by the page to be used for styling,
and might contain sensitive information
(for example, a security token used by scripts on the page).
In general, this is fine.
It is difficult to use
attr()
to extract information from a page
and send it to a hostile party,
in most circumstances.
The exception to this is URLs.
If a URL can be constructed with the value of an arbitrary attribute,
purely from CSS,
it can easily send any information stored in attributes
to a hostile party,
if 3rd-party CSS is allowed at all.
To guard against this,
the values produced by an
attr()
are considered
attr()-tainted
as are functions that contain an
attr()-tainted
value.
The substitution value of an
arbitrary substitution function
is
attr()-tainted
as a whole
if any
attr()-tainted
values were involved
in creating that substitution value.
This extends to the
equivalent token sequence
when substituting values of
registered custom properties
Using an
attr()-tainted
value as or in a

makes a declaration
invalid at computed-value time
Note:
Implementing this restriction
requires tracking a dirty bit
on values constructed from
attr()
values,
since they can be fully resolved into a string
via
registered custom properties
so you can’t rely on just examining the value expression.
Note that non-string types can even trigger this,
via functions like
string()
that can stringify other types of values:
--foo: attr(foo type()); background-image: src(string(var(--foo)))
needs to be invalid as well.
The
ident()
function represents an

and can be used to manually construct

values from several parts.
ident
()
ident





Should we allow a fallback value?
[w3c/csswg-drafts Issue #11424]
The
ident()
function can be used as a value for any property or function argument
that accepts a

In the following example,
each matched element gets a unique
<'view-timeline-name'>
in the format of
"vtl-
number
The
number
is generated using
sibling-index()
.item
/* vtl-1, vtl-2, vtl-3, … */
view-timeline-name:
ident
"vtl-"
sibling-index
());
While in many cases
attr()
with the

set to
type(

will do,
ident()
can be used to construct a

using values that come from other elements.
In the following example, the
ident()
function uses a custom property
which is defined on a parent.
.card
id
/* E.g. card1, card2, card3, … */
--id:
attr
id
);
view-transition-name
ident
var
--id
));
view-transition-class
card
h1
/* E.g. card1-title, card2-title, card3-title, … */
view-transition-name:
ident
var
--id
"-title"
);
view-transition-class
card-title
To generate a

, put
"--"
as the first argument.
.element
anchor-name
ident
"--"
attr
id
));
9.
Generating Random Values
It is often useful to incorporate some degree of "randomness" to a design,
either to make repeated elements on a page feel less static and identical,
or just to add a bit of "flair" to a page without being distracting.
The
random()
and
random-item()
functions
(the
random functions
allow authors to incorporate randomness into their page,
while keeping this randomness predictable from a design perspective,
letting authors decide whether a random value should be reused in several places
or be unique between instances.
The exact random-number generation method is UA-defined.
It
should
be the case that two distinct random values
have no easily-detectable correlation,
but this specification intentionally does not specify what that means
in terms of cryptographic strength.
Authors
must not
rely on
random functions
for any purposes that depend on quality cryptography.
9.1.
Generating a Random Numeric Value: the
random()
function
The
random()
function is a
math function
that represents a random value between a minimum and maximum value,
drawn from a uniform distribution,
optionally limiting the possible values to a step between those limits:
random
()
> =
random




See
§ 9.3 Evaluating Random Values
and
§ 9.4 Sharing (Or Not) Random Values: the value
for details on how the function is evaluated.
Its arguments are:

The optional

controls which
random functions
in the document
will share a
random base value
and which will get distinct values.
If

is omitted,
it behaves as
auto
See
§ 9.4 Sharing (Or Not) Random Values: the value
for a full description of this value.


The two required
calculations
specify the minimum and maximum value
the function can resolve to.
Both limits are inclusive
(the result can be the min or the max).
If the maximum value is less than the minimum value,
it behaves as if it’s equal to the minimum value.
For example,
random(100px, 300px)
will resolve to a random

between
100px
and
300px
it might be
100px
300px
, or any value between them like
234.5px

The final optional argument
specifies a step value:
the values the function can resolve to
are further restricted to the form
min +
N * step
where N is a non-negative integer
chosen uniformly randomly from the possible values
that result in an in-range value.
For example,
random(100px, 300px, 50px)
can only resolve to
100px
150px
200px
250px
, or
300px
it will never return a value like
120px
Note that the max value might not actually show up as a possible value;
for example, in
random(100px, 200px, 30px)
the possible values are
100px
130px
160px
, and
190px
Note:
Even if it’s
intended
that
min
and
max
are exactly some integer multiple of
step
apart,
numeric precision issues can prevent that from being true.
To avoid these issues,
if the max value is
very close
to equalling a stepped value
(above or below it),
the max value is used as the final stepped value instead.
See
§ 9.3 Evaluating Random Values
for precise details.
As explained in the definition of
round()
CSS has no "natural" precision for values,
but the step value can be used to assign one.
For example,
random(100px, 500px, 1px)
restricts it to resolving only to whole px values;
random(1, 10, 1)
is restricted to resolving only to integers;
etc.
Note:
Providing a step value
is not equivalent
to generating a random value and then rounding it;
random(100px, 200px, 50px)
will generate three possible values
(100px, 150px, 200px)
all with a 1/3 chance,
while
round(random(100px, 200px), 50px)
will generate the same three possible values,
but
150px
will occur 1/2 the time,
while
100px
and
200px
will only occur 1/4 of the time each.
All of the
calculation
arguments can resolve to any value,
but must have a
consistent type
or else the function is invalid;
the result’s type will be the consistent type.
For example,
random(50px, 100%, 1em)
is valid
(assuming percentages are valid in the context this is used,
and resolve to a

),
as all three arguments resolve to a length.
However,
random(50px, 180deg)
is invalid,
as lengths and angles are not the same type.
9.1.1.
Argument Ranges
In
random(A, B)
if A is infinite,
the result is infinite.
If A is finite,
but the difference between A and B
is either infinite
or large enough to be treated as infinite in the user agent,
the result is NaN.
In
random(A, B, C)
the same behavior for A and B as defined above applies.
If C is infinite,
the result is A.
If C is negative, zero,
or positive but close enough to zero
that the range for the step multiplier
(the
mentioned in
§ 9.3 Evaluating Random Values
would be infinite in the user agent,
the step must be
ignored
(The function is treated as if only A and B were provided.)
Note:
As usual for
math functions
if any argument calculation is NaN,
the result is NaN.
9.2.
Picking a Random Item From a List: the
random-item()
function
The
random-item()
function
resolves to a random item
from among its list of items.
random-item
()
> =
random-item


The
random-item()
function’s
argument grammar
is:

random-item


See
§ 9.3 Evaluating Random Values
and
§ 9.4 Sharing (Or Not) Random Values: the value
for details on how the function is evaluated.
The
required

is interpreted identically to
random()
(See
§ 9.4 Sharing (Or Not) Random Values: the value
for details.)
Note:
The

argument is required in
random-item()
but optional in
random()
for parsing reasons
(it’s impossible to tell whether
random-item(--foo, --bar, --baz)
has three

arguments
or two and a

).
The remaining arguments are arbitrary sequences of CSS values.
The
random-item()
function is substituted with one of these sequences,
chosen uniformly at random.
The
random-item()
function is an
arbitrary substitution function
like
var()
9.3.
Evaluating Random Values
Given a
random function
with a
random base value
the value of the function is:
for a
random()
function with
min
and
max
, but no step
Return
min
max
min
for a
random()
function with
min
max
, and
step
Let
epsilon
be
step
1000
or the smallest representable value greater than zero
in the numeric type being used
if
epsilon
would round to zero.
Let
be the largest integer
such that
min
step
is less than or equal to
max
If
produces a value that is not within
epsilon
of
max
but
+1 would produce a value within
epsilon
of
max
set
to
+1.
Let
step index
be a
random integer
less than
+1, given
Let
value
be
min
step index
step
If
step index
is
and
value
is within
epsilon
of
max
, return
max
Otherwise, return
value
Epsilon/max Details
epsilon
was chosen to be
step
1000
as a useful "middle ground" value:
small enough that it’s very unlikely to accidentally trigger
when the author didn’t intend it,
but large enough that it’s guaranteed to catch floating-point precision errors.
It’s size is also within the realm for authors to exploit themselves;
if their
max
is meant to be an integer multiple of their
step
but the
step
is not a terminating decimal,
as long as they write the
step
with 5 or 6 digits of precision
then the largest value should land within
epsilon
of
max
For example, while
random(0px, 100px, 33.3px)
will not generate a
100px
value
(the epsilon is
.0333px
but 3 steps yields
99.9px
which is
.1px
from
max
, more than the epsilon),
random(0px, 100px, 33.333px)
will
(the epsilon is
.033333px
similar to the previous example,
but 3 steps yields
99.999px
which is
.001px
from
max
, much less than the epsilon).
(A sufficiently large number of steps
could still cause enough divergence to defeat this,
but it works for any remotely reasonable use-case.)
Using a
calculation
to "exactly" represent non-terminating decimals
will also work, of course:
random(0px, 100px, 100px / 3)
is guaranteed to be able to generate a
100px
value,
as the step value will only be subject to floating-point precision errors
far smaller than the epsilon.
for a
random-item()
function with

? arguments:
To
generate a random integer
less than some integer
limit
given a
random base value
return
round(down,
limit
, 1)
9.4.
Sharing (Or Not) Random Values: the

value
CSS is a declarative language,
so functions don’t have a specific “time” when they’re evaluated;
the user agent is free to “execute” functions in any order,
as many times as they want,
as every execution will return the same value.
This is difficult to square with random-value functions,
which are inherently
stateful
and care about
when, how often, and in what order they’re executed.
To solve this, the
random functions
are associated with a
random cache name
ensuring that any time the random function is evaluated with the same cache key,
it will return the same
random base value
and also that
random functions
with
different
caching keys
will return
different
random base value
from each other
(unless they are, randomly, the same).
The

value controls this behavior,
and defaults to
auto
when omitted.
It’s syntax is as follows, and interpreted as described below:

= auto

fixed


||
element-scoped
||
property-scoped
property-index-scoped



auto
The
random function
is roughly “as random as possible”:
the
random cache name
, and thus the result,
varies across every
random()
instance in a multi-component value,
across different properties,
and across different elements.
This is equivalent to specifying
element-scoped property-index-scoped
and simplifies in exactly the same way.

The

specifies the
random cache name
allowing it to be explicitly shared (or not)
with other uses of
random functions
Every
random function
using the same
random cache name
will get the same random value;
every one using a
different
random cache name
will get unrelated random values.
The

gives the value an explicit name.
If omitted, this part of the name is null.
element-scoped
adds an element-specific identifier
to the
random cache name
so different elements will get different random values.
property-scoped
adds the property name the
random function
is being used on
to the
random cache name
so different properties will get different random values.
Note that shorthand declarations will apply the
shorthand property
’s name.
property-index-scoped
adds the property name
and
the index of the random function
among all the random functions used in the same property value
to the
random cache name
so multiple instances of a
random function
in the same declaration
will each get different
random cache names
Note this index is assigned
before
shorthand
expansion.
See
§ 9.4.1 Simplification of
for how these values resolve.
The

value,
like
fixed
below, isn’t intended to be specified by authors,
but can show up in
computed values
It is a

that must start with the prefix
ua-
or else it is invalid.
When generated automatically by the user agent,
it will always be
ua-PROPERTY
or
ua-PROPERTY-INDEX
with the
PROPERTY
and
INDEX
parts
replaced with a property name or integer index,
like
ua-margin-3
fixed

If
fixed

is specified,
the
random function
bypasses the
random cache name
entirely,
and just uses the

as its
random base value
directly.
While
is technically allowed as a valid value
(because CSS grammars can only express closed ranges),
the
random base value
is clamped to the highest representable value
less than
so
random base values
remain in the half-open range `[0, 1)`.
Note:
While this value
might
be useful for authors
to get predictable values while testing a page,
the reason it exists is to fix some corner cases in
inheritance
that would otherwise cause elements to
not
share
random base values
when authors would expect them to.
It is observable in some rare
computed values
but otherwise will never show up in an element’s styles.
Imagine you have several
.box
elements,
all styled with the following CSS:
.box
width
random
???
100
px
200
px
);
height
random
???
100
px
200
px
);
border
thin solid
Depending on what

you provide
in place of the
???
you’ll get significantly different behavior for the elements:
auto
/omitted,
maximum random
Each property gets a different value,
and it’s different per element too,
so you get a bunch of
different
random
rectangles
--foo
shared by name
Both properties use the same value,
and every element shares that value,
so you get a bunch of
identical
random
squares
element-scoped
scoped per element
Both properties use the same value,
but each element gets a different value,
so you get a bunch of
different
random
squares
property-scoped
scoped per property
Each property gets a different value,
but each element shares that value,
so you get a bunch of
identical
random
rectangles
Note that only the
random cache name
controls whether two
random functions
have the same value or not;
their
other
arguments don’t contribute in any way.
Thus, the following two styles:
.foo
width
random
--foo
100
px
200
px
);
animation
--pulse linear
random
--foo
);
will actually share a
random base value
because their
random cache names
are identical.
If this
random cache name
is linked to a
random base value
of 0.25,
for example,
this will give the element a width of
125px
and an animation-duration of
1.25s
If you want the values to be completely uncorrelated,
give them distinct names (
--foo
vs
--bar
or mix in additional information that will uniquify their
random cache names
(specifying
--foo property-scoped
, or omitting the

argument entirely)
Details about how
auto
works
The
auto
value is identical to specifying
--foo element-scoped property-index-scoped
just with an unobservable name that you can’t manually match with a

Usually
, this ensures that every instance of a
random function
on every element,
gets a different
random cache name
and thus a different
random base value
However, this can fail in some corner cases,
causing random values to be shared when it’s unexpected.
For example, in the following:
.foo
animation
flicker linear
random
);
.foo:hover
animation
glow linear
random
);
The two
random()
functions have the same
random cache name
on a given element
(both are the first
random function
used in
animation
),
so they’ll share
random base values
on a single element
and generate related random values,
even though the author likely considers them to be
different instances of
random()
This unexpected sharing is, unfortunately, unavoidable.
CSS generally doesn’t care
how
you organize styles for your elements;
in particular, whether you style several elements
with a single style rule like
.foo, .bar {...}
multiple style rules like
.foo {...} .bar {...}
or by setting the
style
attribute on each element individually,
you’ll get the same results.
This needs to continue to be true even when you’re using random values,
so we can’t distinguish random values by how they’re
set
only how they’re
used
There’s no syntactic distinction in a stylesheet
between styling "related" elements and "independent" elements,
or distinguishing "independent" states,
so we must treat all elements as potentially related for this purpose.
If this is undesirable,
you can always supply a sufficiently unique

yourself,
like
random(--sidebar-width, ...)
on one set of elements
and
random(--card-width, ...)
on another,
or
random(--flicker, ...)
and
random(--glow, ...)
on the different rules.
More specifically,
the
random()
and
random-item()
functions are defined to generate random values
under the following caching semantics:
Each instance of a
random function
in styles
has an associated
random base value
If the
random function’s

is
fixed

the
random base value
is that number.
Otherwise, the
random base value
is a pseudo-random real number
in the range `[0, 1)`
(greater than or equal to 0 and less than 1),
generated from a uniform distribution,
and influenced by the function’s
random cache name
Each
random function
also specifies a
random cache name
Two
random functions
with the same
random cache name
must also have the same
random base value
two functions with different
random cache names
must have distinct
random base values
(That is, generated by a fresh random operation;
randomness might of course produce the same actual value.)
It is intentionally unspecified
how
the
random cache name
is used to achieve this.
It can literally cache a generated random value,
or be used as a seed for a PRNG,
etc.
random cache name
is a
tuple
of:
A nullable
string
name:
the value of the

, if specified;
otherwise null.
A nullable
string
property/index value:
the value of the

, if specified/calculated;
otherwise null.
A nullable /element identifier/
uniquely identifying the
Element
or
pseudo-element
the style is being applied to,
if
element-scoped
is specified;
otherwise null.
A /document identifier/ identifying the
Document
the styles are from.
The /element identifier/ and /document identifier/
must have the same lifetimes and equivalence semantics
as a JavaScript reference to the
Element
or
Document
The behavior for pseudo-elements needs to be clarified.
The
random cache name
and
random base value
must be determined by
computed value
time,
before
inheritance
so that a random function that is unresolved by inheritance time
(due to containing, for example, a layout-sensitive percentage)
does not have a different behavior on children
than one that resolves immediately.
Shorthands are recorded as part of the
random cache name
rather than the longhand the value gets expanded to.
For example:
.same-tb-same-rl
margin
random
px
10
px
random
px
10
px
);
Here, the

part of the
random cache name
is "ua-margin-1" and "ua-margin-2", respectively.
After expansion, then,
margin-top
and
margin-bottom
will share the same random value,
and
margin-left
and
margin-right
will share a different random value.
Combining shorthands with longhands will produce different caching keys:
.different-tb-same-rl
margin
random
px
10
px
random
px
10
px
);
margin-bottom
random
px
10
px
);
The
margin-bottom
value has a
random cache name

of
"ua-margin-bottom-1"
which is different from the
margin-top
key of
"ua-margin-1"
so the top and bottom margins will be different random values.
margin-left
and
margin-right
continue to share a third random value
"ua-margin-2"
as they still share the name
"margin"
property name and
index.
The evaluation semantics of
custom properties
can make
random functions
act a little unpredictably
if you’re not careful.
For example, in the following:
.square-or-rect
--size
random
100
px
500
px
);
width
var
--size
);
height
var
--size
);
If
--size
isn’t a
registered custom property
or is registered but with the universal grammar
the
random()
function isn’t evaluated
(or even recognized *as* a
random()
function)
in
--size
so the default rules for an omitted

aren’t applied.
Instead, it’s evaluated when it’s substituted into
width
and
height
so each gets a distinct
random cache name
and this ends up defining a random
rectangle
rather than a square.
Similarly,
element-scoped
in the

won’t cause the function to determine its "element identifier"
until substitution actually happens—​which might be
after
--size
has inherited through multiple elements—​so again multiple elements using
var(--size)
would end up with distinct random values
rather than sharing one value defined on their ancestor.
The first issue can be resolved,
if desired,
by specifying a

explicitly,
rather than relying on the defaulting rules.
Both issues can be resolved
by instead using a
registered custom property
with a non-universal grammar,
so the
random()
function will be parsed and evaluated by the
custom property
(gaining a

of "ua---size-1"),
and then the resolved random value will be substituted into each property.
9.4.1.
Simplification of

At parse time,
certain transformations are performed on the

of a
random function
If the

is
auto
(or omitted),
it’s turned into
element-scoped property-index-scoped
(And then subject to the below transformations.)
If the

contains
property-scoped
or
property-index-scoped
that keyword is replaced by a

either
ua-PROPERTY
with PROPERTY being the property the value was parsed as,
or
ua-PROPERTY-INDEX
with PROPERTY as the previous
and INDEX being the 1-indexed integer index of this
random function
among all
random functions
being used in the declaration.
The INDEX value is based on the ordering in the parsed value,
before any canonicalization/reordering might occur
that could shuffle the values around.
If the

contains a

this must not be omitted in the serialization of the value,
even if this would normally be valid per the
"shortest serialization principle"
For example,
specifying
margin: random(10px, 20px) random(10px, 20px)
will cause the top and bottom margins to be one random value,
and the left and right margins to be another random value.
The omitted

s get rewritten at parse-time
to
element-scoped ua-margin-1
and
element-scoped ua-margin-2
respectively,
before they expand into the
margin
longhands.
This way, the
margin-top
and
margin-bottom
longhands
will both contain the key
element-scoped ua-margin-1
ensuring that the two values remain linked
even if you read and then set the value back.
The
random base value
of a
random function
is generally known at
specified value
time,
once it’s known which element the function is being applied to.
As a
math function
, a
random()
function can be
simplified
as soon as its argument
calculations
can be simplified to compatible numeric values.
If a
random()
function can’t be fully
simplified
by
computed value
time,
then its arguments are maximally simplified,
and its specified

is replaced with
fixed BASE
where
BASE
is the function’s
random base value
Note:
As an
arbitrary substitution function
random-item()
is always replaced at
computed value
time.
The
sibling-count()
functional notation
represents,
as an

the total number of child
elements
in the parent of the element on which the notation is used.
The
sibling-index()
functional notation
represents,
as an

the index of the element
on which the notation is used
among its
inclusive siblings
Like
:nth-child()
sibling-index()
is 1-indexed.
Together, these are known as the
tree-counting functions
Note:
The
counter()
function can provide similar abilities as
sibling-index()
but returns a

rather than an

When used on an
element-backed pseudo-element
that is also a real element,
the
tree-counting functions
resolve for that real element.
For other
pseudo-elements
they resolve as if they were resolved against the
ultimate originating element
tree-counting function
is a type of
loosely-matched
tree-scoped reference
which is resolved as if the element were given an automatic
tree-scoped name
that matches an identical automatic name on the
tree-scoped reference
of the
tree-counting function
If the reference fails to match
(because the element is, for example, in a descendent shadow tree from the stylesheet),
the function returns 0.
Note:
This restriction is to avoid leaking shadow tree information to outer
trees
The following examples show how
sibling-index()
resolves
for
pseudo-elements
and across
shadow tree
boundaries:
#target
/* Based on the sibling-index() of #target */
width:
calc
sibling-index
()
10
px
);
#target::before
/* Based on the sibling-index() of #target */
width:
calc
sibling-index
()
10
px
);
#target::before::marker
/* Based on the sibling-index() of #target */
width:
calc
sibling-index
()
10
px
);
::slotted
::before
/* Based on the sibling-index() of the slotted element in the outer tree */
width:
calc
sibling-index
()
10
px
);
::part
my-part
/* Returns 0px, because referencing a shadow tree */
width:
calc
sibling-index
()
10
px
);
:host
/* Based on the host's sibling-index() in the outer tree */
width:
calc
sibling-index
()
10
px
);
Note:
Like
:nth-child()
and other counting selectors,
the
tree-counting functions
operate on the DOM tree,
rather than the
flat tree
like most CSS operations do.
They may, in the future, have variants that support counting
flat tree
siblings.
Note:
These functions may, in the future,
be extended to accept an
of

argument,
similar to
:nth-child()
to filter on a subset of the children.
11.
Calculating With Intrinsic Sizes: the
calc-size()
function
When transitioning between two
definite
sizes,
or slightly adjusting an existing definite size,
calc()
works great:
halfway between
100%
and
20px
is
calc(50% + 10px)
20%
with a margin of
15px
on either side is
calc(20% + 15px * 2)
etc.
But these operations are no longer possible if the size you want to adjust
or transition to/from
is an
intrinsic size
for both practical and backward-compatibility reasons.
The
calc-size()
function
allows math to be performed on intrinsic sizes
in a safe, well-defined way.
calc-size
()
calc-size




calc-size
()
any

The

production
matches any sizing keywords allowed in the context.
For example, in
width
it matches
auto
min-content
stretch
, etc.
Why can
calc-size()
be nested?
Allowing
calc-size()
as the basis argument
means that authors can use a variable as the basis
(like
calc-size(var(--foo), size + 20px)
and it will
always work
as long as the variable was originally valid for the property.
Doing the same with just
calc()
doesn’t work -
for example, if you have
--foo: calc-size(min-content, size + 20px)
or even just
--foo: min-content
then
calc( (var(--foo)) + 20px )
fails.
The nesting is simplified away during interpolation,
and at used-value time,
so the basis always ends up as a simple value
by the time interpolation and other effects occur;
see
§ 11.1 Simplifying calc-size()
The first argument given is the
calc-size basis
and the second is the
calc-size calculation
For either argument,
if a

is given,
its
type
must
match

and it must resolve to a

Within the
calc-size calculation
if the
calc-size basis
is not
any
the keyword
size
is allowed.
This keyword is a

and resolves at
used value
time.
calc-size()
represents an
intrinsic size
It is specifically
not

any place that wants to accept a
calc-size()
must explicitly include it in its grammar.
Why not just allow intrinsic keywords in
calc()
In theory, rather than introducing
calc-size()
we could have defined
calc(auto * .5)
to be valid,
allowing interpolation to work as normal.
This has the minor issue that mixing keywords still wouldn’t be allowed,
but it wouldn’t be as obvious
(that is,
calc((min-content + max-content)/2)
looks reasonable,
but would be disallowed).
The larger issue, tho,
is that this wouldn’t allow us to smoothly transition percentages.
calc(50%)
is only half the size of
calc(100%)
when percentages are
definite
in the context;
if they’re not, the two values will usually be the same size
(depending on the context, either
0px
or
auto
-sized).
Using a new function that explicitly separates
the size you’re calculating with
from the calculation itself
lets us get smooth interpolation in
all
cases.
An additional consideration is that there are many effects,
some small and some large,
that depend on whether an element is intrinsically sized
or definite.
Using
calc()
would mean that the answer to the question
"is the element intrinsically-sized"
can have one answer in the middle of a transition
("yes", for
calc(min-content * .2 + 20px * .8))
),
but a different answer at the end of the transition
("no", for
calc(20px)
),
causing the layout to jump at the end of an otherwise-smooth transition.
(This is similar to the stacking-layer changes that can occur
when animating from
opacity:1
to
opacity: 0
any non-
value forces a stacking context.
With
opacity
you can get around this by animating to
.999
which is visually indistinguishable from
but forces a stacking context.
It’s not as reasonable to ask people to animate to
calc(auto * .0001)
to ensure it retains its intrinsic-ness.)
Again, using a new function that identifies itself
as being
inherently
an intrinsic size,
like
calc-size(auto, 20px)
means we can maintain stable layout behaviors the entire time,
even when the actual size is a definite length.
Similar to
math functions
at both
specified value
and
computed value
times
the
calc-size calculation
(and the
calc-size basis
, if it’s a

are simplified to the extent possible,
as defined in
CSS Values 4
§ 10.10.1 Simplification
Why are percentages simplified in this way?
This percentage simplification
ensures that transitions work linearly.
For example, say that 100% is 100px, for simplicity.
If you transitioned from `calc-size(100px, size * 2)`
(resolves to 200px)
to `calc-size(50%, size - 20px)`
(resolves to 30px)
by interpolating both the arguments,
then at the halfway point
you’d have `calc-size(75px, size * 2 * .5 + (size - 20px) * .5)`
(resolves to 102.5px),
which is *not* halfway between 30 and 200
(that would be 115px).
Interpolating one argument,
then substituting it into another calculation
and interpolating that one too,
generally gives
quadratic
interpolation behavior.
Instead, we substitute the basis arg into the calculation arg,
so you get `calc-size(percentage, 100px * 2)`
and `calc-size(percentage, (size * .5) - 20px)`,
and when interpolated,
at the halfway point you get
`calc-size(percentage, 100px * 2 * .5 + ((size * .5) - 20px) * .5)`,
which does indeed resolve to 115px, as expected.
Other points in the transition are similarly linear.
To
de-percentify a calc-size calculation
calc
Replace every instance of a

in
calc
with
(size * N)
where N is the percentage’s value divided by 100.
Return
calc
Note:
For example,
50% + 20px
becomes
(size * .5) + 20px
To
substitute into a calc-size calculation
calc
a value
insertion value
If
calc
doesn’t have the
size
keyword in it,
do nothing.
Otherwise, replace every instance of the
size
keyword
in
calc
with
insertion value
wrapped in parentheses.
If this substitution would produce a value
larger than an UA-defined limit,
return failure.
Note:
This is intentionally identical
to the protection against substitution attacks
defined for variable substitution;
see
CSS Variables 1
§ 3.3 Safely Handling Overly-Long Variables
However, the use-cases for very long
calc-size()
values
are much less than for long custom properties,
so UAs might wish to impose a smaller size limit.
calc-size()
is treated, in all respects,
as if it were its
calc-size basis
(with
any
acting as an unspecified
definite
size).
When actually performing layout calculations, however,
the size represented by its
calc-size basis
is modified to be the value of its
calc-size calculation
with the
size
keyword
evaluating to the
calc-size basis’s
original size.
(If the
calc-size basis
is
any
the
calc-size()
is a
definite
length,
equal to its
calc-size calculation
.)
When evaluating the
calc-size calculation
if percentages are not definite in the given context,
they resolve to
0px
Otherwise, they resolve as normal.
(A percentage in the
calc-size basis
is treated differently;
simplification
moves the percentage into the
calc-size calculation
and replaces it with
size
references.
The
calc-size basis
then becomes
100%
behaving as whatever
100%
would normally do in that context,
including possibly making a property
behave as auto
, etc.)
Percentages in the basis work as normal
so you can always smoothly transition to
any
size,
regardless of its value or behavior.
For example, without
calc-size()
transitioning from
100%
to
0px
only works smoothly if the percentage is
definite
if it’s not, then during the entire transition
the property might
behave as auto
and not actually change size at all.
Percentages in the calculation, on the other hand,
are resolved to 0 when indefinite
to avoid making the
calc-size()
potentially act in two different ways;
there are some cases where a
min-content
size
will cause different layout effects than a
100%
size,
and so a
calc-size()
has to masquerade as one or the other.
Two
calc-size()
functions can be interpolated if
(after being
canonicalized for interpolation
):
Either function returned failure from being
canonicalized for interpolation
The values cannot be interpolated.
Both
calc-size basises
are identical
The result’s
calc-size basis
is the that basis value.
Either
calc-size basis
is
any
The result’s
calc-size basis
is the non-
any
basis.
The result’s
calc-size calculation
is the interpolation of the two input
calc-size calculations
Note:
These interpolation restrictions ensure that a
calc-size()
doesn’t try to act in two different ways at once;
there are some cases where a
min-content
and
max-content
would produce different layout behaviors, for example,
so the
calc-size()
has to masquerade as one or the other.
This, unfortunately, means you can’t transition between keywords,
like going from
auto
to
min-content
Some
calc-size()
values can also be interpolated
with a

or an

To determine whether the values can interpolate
and what the interpolation behavior is,
treat the non-
calc-size()
value
as
calc-size(any,
value
if the value is a

or as
calc-size(
value
, size)
otherwise,
and apply the rules above.
For example,
calc-size()
allows interpolation to/from
height: auto
details
transition
height
details::details-content
display
block
details
open
::details-content
height
auto
details:not
([
open
])
::details-content
height
calc-size
any
px
);
This will implicitly interpolate
between
calc-size(auto, size)
and
calc-size(any, 0px)
Half a second after opening the
details
the ::details-content wrapper’s
height
will be
calc-size(auto, size * .5)
half its open size;
thruout the transition it’ll smoothly animate its height.
Note:
calc-size()
is designed such that
transitioning to/from
calc-size(any,
definite
length)
will
always
work smoothly,
regardless of how the other side of the transition is specified.
Note:
This "upgrade a plain value into a
calc-size()
" behavior
puts

values into the
calc-size calculation
This allows values with percentages
to interpolate with intrinsic size keywords,
but does mean that when a percentage isn’t
definite
it’ll resolve to zero.
If you want to resolve to the actual size the percentage would make the element,
explicitly write a
calc-size()
with the value in its
calc-size basis
like
calc-size(50%, size)
11.4.
Interpolating sizing keywords: the
interpolate-size
property
Note:
If we had a time machine, this property wouldn’t need to exist.
It exists because many existing style sheets assume that
intrinsic sizing keywords
(such as
auto
min-content
, etc.)
cannot animate.
Therefore this property exists to allow style sheets to choose
to get the expected behavior.
Specifying
interpolate-size: allow-keywords
on the root element
chooses the new behavior for the entire page.
We suggest doing this whenever compatibility isn’t an issue.
numeric-only
An

cannot be interpolated.
allow-keywords
Two values can also be interpolated if
one of them is an

and the other is a

This is done by treating
the

keyword
as though it is
calc-size(
keyword
, size)
and applying the rules in
§ 11.3 Interpolating calc-size()
In other cases,
an

still cannot be interpolated.
The value of
interpolate-size
that matters
is the computed value on the element
at the time the animation might start.
For CSS transitions,
this means the value in the
after-change style
An animation is not stopped or started later
because
interpolate-size
changes.
Appendix A: Arbitrary Substitution Functions
An
arbitrary substitution function
is a
functional notation
that will,
when resolved,
substitute itself with other values
that are unknowable at parse time.
Substitution
Each
arbitrary substitution function
must define how to
replace an arbitrary substitution function
for itself,
given a list of arguments
and a
stack
of
substitution contexts
and likely involving further
substitution
It must return a sequence of
component values
(fully resolved,
so no further
arbitrary substitution functions
exist in the sequence),
which it will be replaced by.
A function’s
replacement
algorithm is called
with the results of
early substitution
and parsing with the function’s
argument grammar
but nothing more.
The algorithm will have to further
substitute
each argument it deals with,
before
parsing
it as the appropriate part of its
normal
grammar,
and then performing whatever logic it needs.
It might leave some arguments completely unsubstituted
(for example, the fallback argument of an
attr()
function,
if the attribute value exists and correctly parses),
or only parse some arguments based on the results of evaluating other arguments
(for example, later

values in an
if()
function
are only evaluated if earlier ones evaluated as false,
and only the result in the "successful"

is evaluated).
To
substitute arbitrary substitution functions
in a sequence of
component values
values
given an optional
substitution context
context
Guard
context
for the remainder of this algorithm.
If
context
is marked as a
cyclic substitution context
return the
guaranteed-invalid value
For each
arbitrary substitution function
func
in
values
(ordered via a depth-first pre-order traversal)
that is not nested in the contents of another
arbitrary substitution function
Substitute early-invoked functions
in
func
’s contents,
and let
early result
be the result.
If
early result
contains the
guaranteed-invalid value
replace
func
in
values
with the
guaranteed-invalid value
and
continue
Parse
early result
according to
func
’s
argument grammar
If this returns failure,
replace
func
in values with the
guaranteed-invalid value
and
continue
otherwise,
let
arguments
be the result.
Replace an arbitrary substitution function
for
func
given
arguments
as defined by that function.
Let
result
be the returned list of
component values
If
result
contains the
guaranteed-invalid value
replace
func
in
values
with the
guaranteed-invalid value
Otherwise, replace
func
in
values
with
result
If
context
is marked as a
cyclic substitution context
return the
guaranteed-invalid value
Nested
arbitrary substitution functions
may have marked
context
as
cyclic
in step 2.
Return
values
substitution context
is a
list
consisting of:
the dependency type, as a string;
one or more additional values, specific to the dependency type.
(Usually, just one additional string.)
As
arbitrary substitution functions
are
resolved
they accumulate
substitution contexts
which affect how
nested
arbitrary substitution functions
resolve.
For example, while resolving a
var(--foo)
function,
the value of the
--foo
property is fetched and
substituted
with a
substitution context
of
"property"
"--foo"
preventing any nested
arbitrary substitution functions
from cyclicly depending on
--foo
as well.
While resolving an
attr(foo)
function,
the value of the
foo
attribute on the element is fetched and
substituted
with a
substitution context
of
"attribute"
"foo"
preventing any nested functions
from cyclicly depending on the
foo
attribute as well.
The types of
substitution contexts
are currently:
"property", followed by a property name,
and optionally a
custom function
"attribute", followed by an attribute name.
"function", followed by a
custom function
As
substitution
is recursively invoked
by nested
arbitrary substitution functions
being
replaced
guards
"stack up" the
substitution contexts
passed to each invocation.
When a
substitution context
is
guarded
it means that, for the duration of the guard,
an attempt to guard a matching
substitution context
again
will mark all
substitution contexts
involved in the cycle as
cyclic substitution contexts
For example, given the following style:
.foo
--one
var
--two
);
--two
var
--one
);
Property replacement
for
--one
invokes
substitution
with a
substitution context
of «"property", "--one"».
Substitution
sees the
var(--two)
function
and invokes
replace a var() function
which fetches the values of
--two
and
substitutes
again,
this time with a
substitution context
of «"property", "--two"».
That
substitution
sees the
var(--one)
function
and invokes
replace a var() function
which fetches the value of
--one
and
substitutes
again,
with a
substitution context
of «"property", "--one"».
This, finally, is a
cyclic substitution context
since it matches the
substitution context
from the first
substitution
causing the
substitution
to just produce the
guaranteed-invalid value
This percolates back up the nested invocations,
eventually resulting in
--one
becoming
invalid at computed-value time
The same happens, in opposite order,
when performing
property replacement
on
--two
When a
cycle is detected
all participants in the cycle become invalid.
For example,
all of the following declarations
become
invalid at computed-value time
.foo
--one
var
--two
);
--two
var
--three
baz
);
--three
var
--one
);
The presence of a fallback in
var
--three
baz
does not affect the outcome.
Argument Grammars and Spread Syntax
Each
arbitrary substitution function
in addition to its standard grammar,
must define an
argument grammar
a much less specific and less restrictive version of its normal grammar,
which serves solely to separate the function’s contents
into distinct arguments.
Note:
Typically, an
argument grammar
will only consist of some punctuation
(usually commas)
and the

production.
See the
if()
function for an example.
Each function, in its
replacement
algorithm,
will apply
substitution
to its arguments
and then
parse
them
according to the appropriate parts of its standard grammar.
As it is in control of this process, however,
some arguments can be left unresolved and unparsed.
For example, the
if()
function’s
argument grammar
merely divides its value into alternating "test" and "result" arguments,
separated by `:` and `;`.
It then evaluates tests one by one,
and only evaluates a single argument
matching the first successful test.
This allows
if()
to achieve behavior similar to `if` constructs
in other programming languages,
where later "branches" aren’t evaluated at all
(beyond a basic parse)
and thus can’t cause errors in cases that would be caught by earlier branches.
This means the following is a valid declaration
when the viewport is
600px
or wider,
resulting in the value
blue
Only when the viewport is narrower than
600px
does the
if()
trigger cyclic behavior
and cause
--color
to be
invalid at computed-value time
.foo
--color
if
media
width >=
600
px
: blue
else
var
--color
));
This also means that, ordinarily,
parsing according to a function’s
argument grammar
does not
see the results of any nested
arbitrary substitution functions
the contents are divided into arguments
based only on the values literally present inside the function.
For example, in
random-item(auto, var(--foo), var(--bar))
the
random-item()
function selects between two random values,
either the result of
var(--foo)
or
var(--bar)
This is true even if one of them contains commas, like:
.random-fonts
--foo
Courier
monospace
--bar
Arial
serif
font-family
random-item
auto
var
--foo
),
var
--bar
));
/* equivalent to: */
font-family:
random-item
auto
Courier
monospace
},
Arial
serif
});
/* and thus, randomly, equivalent to either */
font-family: Courier
monospace
/* or */
font-family: Arial
serif
This behavior ensures that authors don’t have to defensively wrap
any arguments containing
arbitrary substitution functions
in
{}
characters;
what you see is what you get.
Note:
This is different from the behavior of
arbitrary substitution functions
substituted into "normal" functions or properties.
For example,
--colors: red, blue, green; background: linear-gradient(var(--colors));
works in the expected fashion,
producing a gradient with three color stops,
because normal functions don’t do this separate
argument grammar
parse.
This behavior can be worked around
by immediately preceding an
arbitrary substitution function
with the
spread syntax
...
indicating that it must be resolved "early",
before division into arguments.
For example, the following
will not work
.invalid-if
--if-clause
media
width >=
600
px
: blue
color
if
var
--if-clause
);
else
green
;);
The
if()
function entirely fails to parse
according to its
argument grammar
there’s no `:` character separating the test from the value in the first branch.
To get the desired behavior of "spreading" the variable
into the function’s arguments,
use
...
':
.valid-if
--if-clause
media
width >=
600
px
: blue
color
if
...
var
--if-clause
);
else
green
;);
The
spread syntax
is three distinct

s with the value `"."`,
all of which must not contain any whitespace between them,
or between the group and the subsequent
arbitrary substitution function
That is,
...var(--foo)
is a valid use of the
spread syntax
but
... var(--foo)
is not,
nor is
. . .var(--foo)
The latter usages will result in the
arbitrary substitution function
being evaluated at the normal time,
after
the
argument grammar
has been applied,
and the period characters being part of the function’s value.
Note:
The
spread syntax
is only used
within
an
arbitrary substitution function’s
value,
as it’s only referenced by the
substitution
algorithm
when parsing an
arbitrary substitution function
Using it outside of that,
such as in
width: ...var(--sidebar-width);
is not recognized as an early invocation;
instead, the periods are just part of the property’s value,
unrelated to the
var()
function,
and would in this case make the
width
property invalid.
(This is similar to JavaScript,
where this syntax was borrowed from,
where `[1, ...arr, 5]` is valid,
but `var x = ...arr;` is not.)
Resolving in Properties
Unless otherwise specified,
arbitrary substitution functions
can be used
in place of any part of any property’s value
(including within other
functional notations
);
and are not valid in any other context.
Should any of these functions be valid in contexts outside of properties?
Tests
For example, the following code incorrectly attempts to use a variable as a property name:
.foo
--side
margin-top
var
--side
20
px
This is
not
equivalent to setting
margin-top: 20px;
Instead, the second declaration is simply thrown away as a syntax error
for having an invalid property name.
If a property value contains one or more
arbitrary substitution functions
and all of those functions are themselves syntactically valid
according to their
argument grammar
s,
the entire value’s grammar must be assumed to be valid at parse time.
Tests
Arbitrary substitution functions
are
substituted
during style
computation
before any other value transformations or introspection can occur.
If a property,
after
property replacement
does not match its declared grammar,
the declaration is
invalid at computed-value time
Note:
Since
arbitrary substitution functions
resolve at
computed value
time,
if the resulting value after substitution is invalid,
the property falls back (essentially) to
unset
behavior,
rather than falling back to an earlier value in the
cascade
the way declarations invalid at parse time do.
See
Invalid Substitution
Tests
If a property value,
after
property replacement
contains only a single
CSS-wide keyword
(and possibly whitespace/comments),
its value is determined as if that keyword were its
specified value
all along.
Tests
For example,
the following usage is fine from a syntax standpoint,
but results in nonsense when the variable is substituted in:
:root
--looks-valid
20
px
background-color
var
--looks-valid
);
Since
20px
is an invalid value for
background-color
the property becomes
invalid at computed-value time
and instead resolves to
transparent
(the
initial value
for
background-color
).
If the property was one that’s inherited by default,
such as
color
it would compute to the inherited value
rather than the initial value.
While a
var()
function can’t get a
CSS-wide keyword
from the
custom property
itself—​if you tried to specify that,
like
--foo: initial;
it would just trigger
explicit defaulting
for the custom property—​it can have a
CSS-wide keyword
in its fallback:
color
var
--does-not-exist
initial
);
In the above code, if the
--does-not-exist
property didn’t exist
or is
invalid at computed-value time
the
var()
will instead substitute in the
initial
keyword,
making the property behave as if it was originally
color: initial
This will make it take on the document’s initial
color
value,
rather than defaulting to inheritance,
as it would if there were no fallback.
Tests
CSSOM
Note that
substitution
takes place at the level of CSS tokens
[css-syntax-3]
not at a textual level;
you can’t build up a single token where part of it is provided by a variable:
.foo
--gap
20
margin-top
var
--gap
px
This is
not
equivalent to setting
margin-top: 20px;
(a length).
Instead, it’s equivalent to
margin-top: 20 px;
(a number followed by an ident),
which is simply an invalid value for the
margin-top
property.
Note, though, that
calc()
can be used to validly achieve the same thing, like so:
.foo
--gap
20
margin-top
calc
var
--gap
px
);
This also implies that the post-substitution value
might not be directly serializable as-is.
Here’s a similar example to the preceding:
.foo
--gap
20
--not-px-length
var
--gap
px
The serialization of the computed (post-substitution)
value of
--not-px-length
is
not
20px
because that would parse back as the single combined dimension;
instead, it will serialize with a comment between the two tokens,
like
px
to enforce that they are separate tokens even when re-parsing.
Tests
Invalid Substitution
When
property replacement
results in a property’s value
containing the
guaranteed-invalid value
this makes the declaration
invalid at computed-value time
When this happens,
the computed value is one of the following
depending on the property’s type:
The property is a non-registered
custom property
The property is a
registered custom property
with
universal syntax
The computed value is the
guaranteed-invalid value
Otherwise
Either the property’s inherited value
or its initial value
depending on whether the property is inherited or not, respectively,
as if the property’s value had been specified as the
unset
keyword.
Tests
For example, in the following code:
:root
--not-a-color
20
px
background-color
red
background-color
var
--not-a-color
);
the

elements will have transparent backgrounds
(the initial value for
background-color
),
rather than red backgrounds.
The same would happen if the
custom property
itself was unset,
or contained an invalid
var()
function.
Note the difference between this
and what happens if the author had just written
background-color: 20px
directly in their stylesheet -
that would be a normal syntax error,
which would cause the rule to be discarded,
so the
background-color: red
rule would be used instead.
Note:
The
invalid at computed-value time
concept exists
because
arbitrary substitution functions
can’t "fail early" like other syntax errors can,
so by the time the user agent realizes a property value is invalid,
it’s already thrown away the other cascaded values.
Substitution in Shorthand Properties
Arbitrary substitution functions
produce some complications
when parsing
shorthand properties
into their component longhands,
and when serializing
shorthand properties
from
their component longhands.
If a
shorthand property
contains an
arbitrary substitution function
in its value,
the
longhand properties
it’s associated with must instead be filled in
with a special, unobservable-to-authors
pending-substitution value
that indicates the shorthand contains an
arbitrary substitution function
and thus the longhand’s value can’t be determined until after
substituted
This value must then be cascaded as normal,
and at computed-value time,
after
substitution
the shorthand must be parsed
and the longhands must be given their appropriate values at that point.
Tests
Note:
When a shorthand is written without an
arbitrary substitution function
it is parsed and separated out into its component
longhand properties
at parse time;
the longhands then participate in the
cascade
with the
shorthand property
more or less discarded.
When the shorthand contains a
var()
, however,
this can’t be done,
as the
var()
could be substituted with anything.
Pending-substitution values
must be serialized as the empty string,
if an API allows them to be observed.
Tests
Shorthand properties
are serialized
by gathering the values of their component
longhand properties
and synthesizing a value
that will parse into the same set of values.
If all of the component
longhand properties
for a given
shorthand
are
pending-substitution values
from the same original shorthand value,
the
shorthand property
must serialize to that original
arbitrary substitution function
-containing)
value.
Otherwise,
if any of the component
longhand properties
for a given
shorthand
are
pending-substitution values
or contain
arbitrary substitution functions
of their own that have not yet been
substituted
the
shorthand property
must serialize to the empty string.
Safely Handling Overly-Long Substitution
Naively implemented,
some
arbitrary substitution functions
(such as
var()
can be used in a variation of the "billion laughs attack":
.foo
--prop1
lol
--prop2
var
--prop1
var
--prop1
);
--prop3
var
--prop2
var
--prop2
);
--prop4
var
--prop3
var
--prop3
);
/* etc */
In this short example,
--prop4
’s computed value is
lol lol lol lol lol lol lol lol
containing 8 copies of the original
lol
Every additional level added to this doubles the number of identifiers;
extending it to a mere 30 levels,
the work of a few minutes by hand,
would make
--prop30
contain
nearly a billion instances
of the identifier.
To avoid this sort of attack,
UAs must impose a UA-defined limit on the allowed length of the token stream
that an
arbitrary substitution function
expands into.
If an
arbitrary substitution function
would expand into a longer token stream than this limit,
it instead is replaced with the
guaranteed-invalid value
Tests
This specification does not define what size limit should be imposed.
However, since there are valid use-cases for custom properties that contain a kilobyte or more of text,
it’s recommended that the limit be set relatively high.
Note:
The general principle that UAs are allowed to violate standards due to resource constraints
is still generally true here;
a UA might, separately, have limits on how long of a custom property they can support,
or how large of an identifier they can support.
This section calls out this attack specifically
because of its long history,
and the fact that it can be done without any of the pieces
seeming
to be too large on first inspection.
Appendix B: Boolean Logic
In order to accommodate future extensions of CSS,

productions generally interpret their

grammar branch as unknown,
and their boolean logic is resolved using 3-value Kleene logic.
In some cases (such as
@supports
),

is instead defined as false;
in which case the logic devolves to standard boolean algebra.
3-value boolean logic is applied recursively
to a boolean condition
test
as follows:
A leaf-level
test
resolves to
true, false, or unknown,
as defined by the relevant specification.
not
test
evaluates to
true if its contained
test
is false,
false if it’s true,
and unknown if it’s unknown.
Multiple
test
s connected with
and
evaluate to
true if
all
of those
test
s are true,
false if
any
of them are false,
and unknown otherwise (i.e. if at least one unknown, but no false).
Multiple
test
s connected with
or
evaluate to
true if
any
of those
test
s are true,
false if
all
of them are false,
and unknown otherwise (i.e. at least one unknown, but no true).
If a “top-level”

is unknown,
and the containing context doesn’t otherwise define
how to handle unknown conditions,
it evaluates to false.
Note:
That is, unknown doesn’t “escape” a 3-value boolean expression
unless explicitly handled,
similar to how
NaN
doesn’t “escape” a
top-level calculation
).
Acknowledgments
Firstly, the editors would like to thank
all of the contributors to the
previous level
of this module.
Secondly, we would like to acknowledge
Guillaume Lebas,
L. David Baron,
Mike Bremford,
Sebastian Zartner,
and
especially Scott Kellum
for their ideas, comments, and suggestions for Level 5;
Changes
Recent Changes
Changes since the
11 November 2024 Working Draft
See also
earlier changes
Additions Since Level 4
Additions since
CSS Values and Units Level 4
Security Considerations
This specification allows CSS

values to have various aspects of their request modified.
Although this is new to CSS,
every ability is already present in
img
or
link
, as well as via JavaScript.
The
attr()
function allows HTML attribute values
to be used in CSS values,
potentially exposing sensitive information
that was previously not accessible via CSS.
See
§ 8.7.2 Security
Privacy Considerations
This specification defines units that expose the user’s screen size
and default font size,
but both are trivially observable from JS,
so they do not constitute a new privacy risk.
Similarly the
media-progress()
notation exposes
information about the user’s environment and preferences
that are already observiable via
media queries
The
attr()
function allows HTML attribute values
to be used in CSS values,
potentially exposing sensitive information
that was previously not accessible via CSS.
See
§ 8.7.2 Security
Conformance requirements are expressed with a combination of
descriptive assertions and RFC 2119 terminology. The key words “MUST”,
“MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”,
“RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this
document are to be interpreted as described in RFC 2119.
However, for readability, these words do not appear in all uppercase
letters in this specification.
All of the text of this specification is normative except sections
explicitly marked as non-normative, examples, and notes.
[RFC2119]
Examples in this specification are introduced with the words “for example”
or are set apart from the normative text with
class=
"example"
like this:
Informative notes begin with the word “Note” and are set apart from the
normative text with
class=
"note"
, like this:
Note, this is an informative note.
Advisements are normative sections styled to evoke special attention and are
set apart from other normative text with
"advisement"
, like
this:
UAs MUST provide an accessible alternative.
Conformance to this specification
is defined for three conformance classes:
A style sheet is conformant to this specification
if all of its statements that use syntax defined in this module are valid
according to the generic CSS grammar and the individual grammars of each
feature defined in this module.
A renderer is conformant to this specification
if, in addition to interpreting the style sheet as defined by the
appropriate specifications, it supports all the features defined
by this specification by parsing them correctly
and rendering the document accordingly. However, the inability of a
UA to correctly render a document due to limitations of the device
does not make the UA non-conformant. (For example, a UA is not
required to render color on a monochrome monitor.)
An authoring tool is conformant to this specification
if it writes style sheets that are syntactically correct according to the
generic CSS grammar and the individual grammars of each feature in
this module, and meet all other conformance requirements of style sheets
as described in this module.
So that authors can exploit the forward-compatible parsing rules to
assign fallback values, CSS renderers
must
treat as invalid (and
ignore
as appropriate
) any at-rules, properties, property values, keywords,
and other syntactic constructs for which they have no usable level of
support. In particular, user agents
must not
selectively
ignore unsupported component values and honor supported values in a single
multi-value property declaration: if any value is considered invalid
(as unsupported values must be), CSS requires that the entire declaration
be ignored.
Once a specification reaches the Candidate Recommendation stage,
non-experimental implementations are possible, and implementors should
release an unprefixed implementation of any CR-level feature they
can demonstrate to be correctly implemented according to spec.
To establish and maintain the interoperability of CSS across
implementations, the CSS Working Group requests that non-experimental
CSS renderers submit an implementation report (and, if necessary, the
testcases used for that implementation report) to the W3C before
releasing an unprefixed implementation of any CSS features. Testcases
submitted to W3C are subject to review and correction by the CSS
Working Group.