CSS Values and Units Module Level 5

Editor’s Draft,

More details about this document
This version:
https://drafts.csswg.org/css-values-5/
Latest published version:
https://www.w3.org/TR/css-values-5/
Feedback:
CSSWG Issues Repository
Inline In Spec
Editors:
Tab Atkins (Google)
fantasai
Suggest an Edit for this Spec:
GitHub Editor

Abstract

This CSS module describes the common values and units that CSS properties accept and the syntax used for describing them in CSS property definitions.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-values” in the title, like this: “[css-values] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list www-style@w3.org.

This document is governed by the 2 November 2021 W3C Process Document.

1. Introduction

The value definition field of each CSS property can contain keywords, data types (which appear between < and >), and information on how they can be combined. Generic data types (<length> being the most widely used) that can be used by many properties are described in this specification, while more specific data types (e.g., <spacing-limit>) are described in the corresponding modules.

1.1. Module Interactions

This module supersedes [CSS-VALUES-4] which replaces and extends the data type definitions in [CSS21] sections 1.4.2.1, 4.3, and A.2.

1.2. Toggling Between Values: toggle()

The toggle() expression allows descendant elements to cycle over a list of values instead of inheriting the same value.

The following example makes <em> elements italic in general, but makes them normal if they’re inside something that’s italic:
em { font-style: toggle(italic; normal); }
The following example cycles markers for nested lists, so that a top level list has disc-shaped markers, but nested lists use circle, then square, then box, and then repeat through the list of marker shapes, starting again (for the 5th list deep) with disc.
ul { list-style-type: toggle(disc; circle; square; box); }

The syntax of the toggle() expression is:

toggle( <toggle-value> [ ';' <toggle-value> ]+ )

where <toggle-value> is any CSS value that is valid where the expression is placed. If any of the values inside are not valid, then the entire toggle() expression is invalid. The toggle() expression may be used as the value of any property, but must be the only component in that property’s value.

Note: This functional notation uses semicolons to separate arguments rather than the more typical comma because the values themselves can contain commas.

The toggle() notation is not allowed to be nested; nor may it contain attr() or calc() notations. Declarations containing such constructs are invalid.

The following toggle() examples are all invalid:
background-position: 10px toggle(50px, 100px);
/* toggle() must be the sole value of the property */

list-style-type: toggle(disc, 50px);
/* 50px isn’t a valid value of 'list-style-type' */

To determine the computed value of toggle(), first evaluate each argument as if it were the sole value of the property in which toggle() is placed to determine the computed value that each represents, called Cn for the n-th argument to toggle(). Then, compare the property’s inherited value with each Cn. For the earliest Cn that matches the inherited value, the computed value of toggle() is Cn+1. If the match was the last argument in the list, or there was no match, the computed value of toggle() is the computed value that the first argument represents.

Note: This means that repeating values in a toggle() short-circuits the list. For example toggle(1em; 2em; 1em; 4em) will be equivalent to toggle(1em; 2em).

Note: That toggle() explicitly looks at the computed value of the parent, so it works even on non-inherited properties. This is similar to the inherit keyword, which works even on non-inherited properties.

Note: That the computed value of a property is an abstract set of values, not a particular serialization [CSS21], so comparison between computed values should always be unambiguous and have the expected result. For example, a Level 2 background-position computed value is just two offsets, each represented as an absolute length or a percentage, so the declarations background-position: top center and background-position: 50% 0% produce identical computed values. If the "Computed Value" line of a property definition seems to define something ambiguous or overly strict, please provide feedback so we can fix it.

If toggle() is used on a shorthand property, it sets each of its longhands to a toggle() value with arguments corresponding to what the longhand would have received had each of the original toggle() arguments been the sole value of the shorthand.

For example, the following shorthand declaration:
margin: toggle(1px 2px, 4px, 1px 5px 4px);

is equivalent to the following longhand declarations:

margin-top:    toggle(1px; 4px; 1px);
margin-right:  toggle(2px; 4px; 5px);
margin-bottom: toggle(1px; 4px; 4px);
margin-left:   toggle(2px; 4px; 5px);

Note that, since 1px appears twice in the top margin and 4px appears twice in bottom margin, they will cycle between only two values while the left and right margins cycle through three. In other words, the declarations above will yield the same computed values as the longhand declarations below:

margin-top:    toggle(1px; 4px);
margin-right:  toggle(2px; 4px; 5px);
margin-bottom: toggle(1px; 4px);
margin-left:   toggle(2px; 4px; 5px);

which may not be what was intended.

2. Attribute References: the attr() function

attr

In all current engines.

Firefox1+Safari3.1+Chrome2+
Opera9+Edge79+
Edge (Legacy)12+IE8+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile10.1+

The attr() function substitutes the value of an attribute on an element into a property, similar to how the var() function substitutes a custom property value into a function.

attr() = attr( <q-name> <attr-type>? , <declaration-value>?)

<attr-type> = string | url | ident | color | number | percentage |
              length | angle | time | frequency | flex | <dimension-unit>

The <dimension-unit> production matches a literal "%" character (that is, a <delim-token> with a value of "%") or an ident whose value is any of the CSS units for <length>, <angle>, <time>, <frequency>, or <flex> values (such as px or ms).

The arguments of attr() are:

<q-name>

Gives the name of the attribute being referenced.

If no namespace is specified (just an identifier is given, like attr(foo)), the null namespace is implied. (This is usually what’s desired, as namespaced attributes are rare. In particular, HTML and SVG do not contain namespaced attributes.) As with attribute selectors, the case-sensitivity of <q-name> depends on the document language.

If attr() is used in a property applied to an element, it references the attribute of the given name on that element; if applied to a pseudo-element, the attribute is looked up on the pseudo-element’s originating element.

<attr-type>

Specifies what kind of CSS value the attribute’s value will be interpreted into (the attr()’s substitution value) and what, if any, special parsing will be done to the value.

The possible values and their behavior are defined in § 2.1 attr() Types.

Defaults to string if omitted.

<declaration-value>

Specifies a fallback value for the attr(), which will be substituted instead of the attribute’s value if the attribute is missing or fails to parse as the specified type.

If the <attr-type> argument is string, defaults to the empty string if omitted; otherwise, defaults to the guaranteed-invalid value if omitted.

If a property contains one or more attr() functions, and those functions are syntactically valid, the entire property’s grammar must be assumed to be valid at parse time. It is only syntax-checked at computed-value time, after attr() functions have been substituted.

Note that the default value need not be of the type given. For instance, if the type required of the attribute by the author is px, the default could still be auto, like in width: attr(size px, auto);.

2.1. attr() Types

The behavior of the attr() function depends partially on the value of the <attr-type> argument:

string

The substitution value is a CSS string, whose value is the literal value of the attribute. (No CSS parsing or "cleanup" of the value is performed.)

No value triggers fallback.

url

The substitution value is a CSS <url> value, whose url is the literal value of the attribute. (No CSS parsing or "cleanup" of the value is performed.)

Note: If url() was syntactically capable of containing functions, attr(foo url) would be identical to url(attr(foo string)).

No value triggers fallback.

ident

The substitution value is a CSS <custom-ident>, whose value is the literal value of the attribute, with leading and trailing ASCII whitespace stripped. (No CSS parsing of the value is performed.)

If the attribute value, after trimming, is the empty string, there is instead no substitution value.

If the <custom-ident>’s value is a CSS-wide keyword or default, there is instead no substitution value.

color

Parse a component value from the attribute’s value. If the result is a <hex-color> or a named color ident, the substitution value is that result as a <color>.

Otherwise there is no substitution value.

number

Parse a component value from the attribute’s value. If the result is a <number-token>, the result is the substitution value.

Otherwise, there is no substitution value.

percentage

Parse a component value from the attribute’s value. If the result is a <percentage-token>, the result is the substitution value.

Otherwise, there is no substitution value.

length
angle
time
frequency
flex

Parse a component value from the attribute’s value. If the result is a <dimension-token> whose unit matches the given type, the result is the substitution value.

Otherwise, there is no substitution value.

<dimension-unit>

Parse a component value from the attribute’s value. If the result is a <number-token>, the substitution value is a dimension with the result’s value, and the given unit.

Otherwise, there is no substitution value.

Do we want to allow math functions as attr values for all the numeric types? And color functions for "color"? I think we do, but I’d have to check the contents to make sure they don’t contain further reference functions; foo="rgb(var(--red), 0, 0)" needs to be illegal for attr(foo color).

This example shows the use of attr() to visually illustrate data in an XML file:
<stock>
  <wood length="12"/>
  <wood length="5"/>
  <metal length="19"/>
  <wood length="4"/>
</stock>

stock::before {
  display: block;
  content: "To scale, the lengths of materials in stock are:";
}
stock > * {
  display: block;
  width: attr(length em, 0px);
  height: 1em;
  border: solid thin;
  margin: 0.5em;
}
wood {
  background: orange url(wood.png);
}
metal {
  background: silver url(metal.png);
}

2.2. attr() Substitution

attr() and var() substitute at the same time, so I should probably rewrite substitute a var() to be more generally about "substitute a reference" and just use that for both of these functions.

attr() functions are substituted at computed-value time. If a declaration, once all attr() functions are substituted in, does not match its declared grammar, the declaration is invalid at computed-value time.

To substitute an attr():

  1. If the attr() function has a substitution value, replace the attr() function by the substitution value.

  2. Otherwise, if the attr() function has a fallback value as its last argument, replace the attr() function by the fallback value. If there are any var() or attr() references in the fallback, substitute them as well.

  3. Otherwise, the property containing the attr() function is invalid at computed-value time.

3. 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.

3.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( <random-caching-options>? , <calc-sum>, <calc-sum>, [by <calc-sum>]? );

<random-caching-options> = <dashed-ident> || per-element

Its arguments are:

<random-caching-options>

The optional <random-caching-options> provides some control over whether a given random() function resolves similarly or differently to other random()s on the page. See § 3.3 Generating/Caching Random Values: the <random-caching-options> value for details.

By default, random() resolves to a single value, shared by all elements using that style, and two random() functions with identical arguments will resolve to the same random value.

Providing a <dashed-ident> does nothing, but can make the argument lists distinct between two or more otherwise-identical random() functions, so they’ll generate distinct values.

The per-element keyword causes the random() function to generate a different value on each element the function is applied to, rather than resolving to a single value per usage in the stylesheet.

<calc-sum>, <calc-sum>

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 <length> between 100px and 300px: it might be 100px, 300px, or any value between them like 234.5px.
''by <calc-sum>''

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, by 50px) can only resolve to 100px, 150px, 200px, 250px, or 300px; it will never return a value like 120px.

While the minimum value is always a possible result, the maximum value isn’t always, if it’s not also a multiple of the step from the minimum. For example, in random(100px, 300px, by 30px), the largest possible value it can resolve to is 280px, 6 steps from the minimum value.

Note that rounding issues might have an effect here: in random(100px, 200px, by 100px / 3) you’ll definitely get three possible values (100px, and approximately 133.33px and 166.67px), but whether 200px is possible depends on rounding precision. To be safe, you can put the maximum value slightly above where you expect the final step to land, like random(100px, 201px, by 100px / 3).

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, by 1px) restricts it to resolving only to whole px values; random(1, 10, by 1) is restricted to resolving only to integers; etc.

Note: The definition of the step does not allow for naively generating a random value in the range and then rounding it to the nearest step value, as that can result in the values not appearing with the same weights. For example, random(100px, 200px, by 50px) has to generate the three possible values each with a 1/3 chance; a naive rounding-based method will instead incorrectly generate 150px twice as often as the boundary values.

All of the calculation arguments can resolve to any <number>, <dimension>, or <percentage>, but must have the same type, or else the function is invalid; the result will have the same type as the arguments.

For example, random(50px, 100%, by 1em) is valid (assuming percentages are valid in the context this is used, and resolve to a <length>), as all three arguments resolve to a length.

However, random(50px, 180deg) is invalid, as lengths and angles are not the same type.

If per-element is not present in the <random-caching-options>, a random() function can be simplified as soon as its argument calculations can be simplified to numeric values. If per-element is specified in the <random-caching-options>, the function cannot be simplified until used value time.

At least in theory it should be fine to use random() in non-property contexts, so long as per-element isn’t specified; it’s well-defined what happens with @media (max-width: random(100px, 500px)) {...}, for example. I suspect we want to disallow it, tho?

3.1.1. Argument Ranges

In random(A, B, by C), if A or B is infinite, the result is NaN. If C is infinite, the result is A.

(If C is zero or negative, the result is A, but that falls out of the standard definition.)

Note: As usual for math functions, if any argument calculation is NaN, the result is NaN.

3.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( <random-caching-options> ';' <any-value> [ ';' <any-value> ]* )

The required <random-caching-options> is interpreted identically to random(). (See § 3.3 Generating/Caching Random Values: the <random-caching-options> value for details.)

Like random(), the <dashed-ident> can be used to force similar random-item() functions to generate distinct random values, and per-element causes it to resolve to a distinct value on each element.

Aside from these, the grouping of random-item() functions as "identical" is much simpler: all that matters is the number of arguments.

That is, random-item(--x; red; blue; green) and random-item(--x; 1; 2; 3) will always resolve to the same argument index: either red and 1, or blue and 2, or green and 3. This allows coordination between groups of properties that all want to use a random set of values.

On the other hand, random-item(--x; red; blue; green) and random-item(--x; 1; 2; 3; 4) will have no connection to each other; any of the 12 possible combinations can occur.

Note: The <random-caching-options> argument is required in random-item(), but optional in random(), both for parsing reasons (it’s impossible to tell whether random-item(--foo; --bar; --baz) has three <any-value> arguments or two and a <random-caching-options> argument), and because accidentally associating the random generation of random-item() functions together is much easier to do accidentally, since only the number of arguments is used to distinguish instances.

The remaining arguments are arbitrary sequences of CSS values, separated by semicolons. The random-item() function resolves to one of these sequences, chosen uniformly at random.

Note: Unlike most functions in CSS, random-item() separates its arguments with semicolons, rather than commas, because its arguments can contain commas themselves.

The random-item() function is an arbitrary substitution function, like var().

That is, if you use random-item():

Define arbitrary substitution function, probably over in Variables, since we have several upcoming functions leaning on this functionality.

Since random-item() is var()-like, we probably want to restrict it to only be usable in properties. (This is likely something we want to apply to all such functions.) Tho random() is a fundamentally different kind of value, we probably want to restrict it as well, for thematic consistency.

3.3. Generating/Caching Random Values: the <random-caching-options> value

In a programming language like JavaScript, there’s a clear temporal ordering to code, so you can tell exactly when something like a call to Math.random() is evaluated. You can also store the results in a variable, making it clear when you’re reusing a single random value in multiple places, versus using a distinct random value in each location.

CSS, on the other hand, is a declarative language (code is not "executed" in any particular order, nor is there any control over how many times something is "executed"); it makes it very easy to apply identical styles to multiple elements but difficult to specify distinct values for each of them (making it unclear whether a property using random() is meant to resolve to the same value on each element it’s applied to or to distinct values on each); and it has very limited "variable" functionality (making it difficult to intentionally reuse a particular randomly-generated value in several places).

To resolve these issues, the random() and random-item() functions are defined to generate random values under the following caching semantics:

The "unique value per element or pseudo-element" must have the same lifetime as a JavaScript reference to the element (or to the originating element + sufficient additional info to uniquely identify the pseudo-element). Elements in separate documents (including across refreshes of the same page, which produces distinct documents with distinct elements) should have distinct unique values. (This is not strictly required, to allow for pseudo-random generation of these values, but uniqueness should be likely enough that authors cannot depend on elements having the same values across documents.)

For example, in the following stylesheet:
.random-square {
  width: random(100px, 500px);
  height: random(100px, 500px);
}

The random-caching keys for both functions are identical: (100px, 500px, null, null, null). This means that both will resolve to the exact same value, guaranteeing a square element with a size somewhere between 100px and 500px. Additionally, every .random-square element will have the same size.

On other hand, in this stylesheet:

.random-rect {
  width: random(100px, 500px);
  height: random(--x, 100px, 500px);
}

The random-caching keys are distinct between the two functions: the function in width has (100px, 500px, null, null, null), while the function in height has (100px, 500px, null, --x, null).

This means the two functions will resolve to distinct random values, making it very unlikely for the element to be square. However, every element matching .random-rect will still have the same random size.

Changing any aspect of the function also alters this key. The following two declarations are similarly distinct, resulting in the width and height having no connection to each other:

.random-rect-2 {
  width: random(100px, 500px);
  height: random(100px, 500px, by 50px);
}

But so long as the used-values end up identical, two functions that look distinct might end up identical. For example, in the following code:

.random-square-2 {
  font-size: 16px;
  width: random(160px, 320px);
  height: random(10em, 20em);
}

The two functions superficially look different, but after the lengths are fully resolved they end up with identical random-caching keys; each is (160px, 320px, null, null, null), so actually the widths and heights will end up always identical.

By default, each instance of a random() function in a stylesheet essentially resolves to a static value, which is then shared by every element that property applies to. This behavior can be changed with the per-element keyword.

For example, in:

.foo { width: random(100px, 500px); }

Multiple elements matching .foo will end up with the same random width.

But in:

.foo { width: random(per-element, 100px, 500px); }

Every element matching .foo will get its own unique width.

Note that this causes the value to be unique per element, not per value necessarily. For example, in:

.random-squares {
  width: random(per-element, 100px, 500px);
  height: random(per-element, 100px, 500px);
}

Every element matching .random-squares will get a distinct random value, but that value will be the same for width and height on a given element, making the element square. This is because in both properties the random-caching key is (100px, 500px, null, null, [unique value for the element]), so both functions will resolve to the same length on a single element.

This makes random values in custom properties act more predictably. The preceding code could also be written as:

.foo {
  --size: random(per-element, 100px, 500px);
  width: var(--size);
  height: var(--size);
}

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 L. David Baron and Mike Bremford for their comments and suggestions, which have improved Level 5.

Changes

Recent Changes

(This is a subset of Additions Since Level 4.)

Additions Since Level 4

Additions since CSS Values and Units Level 4:

Security and Privacy Considerations

This specification mostly just defines units that are common to CSS specifications, and which present no security concerns.

Note: Does URL handling have a security concern? Probably.

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.

Conformance

Document conventions

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:

This is an example of an informative example.

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 <strong class="advisement">, like this: UAs MUST provide an accessible alternative.

Tests

Tests relating to the content of this specification may be documented in “Tests” blocks like this one. Any such block is non-normative.


Conformance classes

Conformance to this specification is defined for three conformance classes:

style sheet
A CSS style sheet.
renderer
A UA that interprets the semantics of a style sheet and renders documents that use them.
authoring tool
A UA that writes a style sheet.

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.

Partial implementations

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.

Implementations of Unstable and Proprietary Features

To avoid clashes with future stable CSS features, the CSSWG recommends following best practices for the implementation of unstable features and proprietary extensions to CSS.

Non-experimental implementations

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.

Further information on submitting testcases and implementation reports can be found from on the CSS Working Group’s website at http://www.w3.org/Style/CSS/Test/. Questions should be directed to the public-css-testsuite@w3.org mailing list.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. URL: https://drafts.csswg.org/css-cascade-5/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley; Lea Verou. CSS Color Module Level 4. URL: https://drafts.csswg.org/css-color/
[CSS-GRID-2]
Tab Atkins Jr.; Elika Etemad; Rossen Atanassov. CSS Grid Layout Module Level 2. URL: https://drafts.csswg.org/css-grid-2/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. URL: https://drafts.csswg.org/css-syntax/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. URL: https://drafts.csswg.org/css-values-4/
[CSS-VARIABLES-2]
CSS Custom Properties for Cascading Variables Module Level 2 URL: https://drafts.csswg.org/css-variables-2/
[CSS21]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. URL: https://drafts.csswg.org/css2/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. URL: https://drafts.csswg.org/selectors/

Informative References

[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. URL: https://drafts.csswg.org/css-backgrounds/
[CSS-SIZING-3]
Tab Atkins Jr.; Elika Etemad. CSS Box Sizing Module Level 3. URL: https://drafts.csswg.org/css-sizing-3/
[CSS22]
Bert Bos. Cascading Style Sheets Level 2 Revision 2 (CSS 2.2) Specification. URL: https://drafts.csswg.org/css2/

Issues Index

Do we want to allow math functions as attr values for all the numeric types? And color functions for "color"? I think we do, but I’d have to check the contents to make sure they don’t contain further reference functions; foo="rgb(var(--red), 0, 0)" needs to be illegal for attr(foo color).
attr() and var() substitute at the same time, so I should probably rewrite substitute a var() to be more generally about "substitute a reference" and just use that for both of these functions.
At least in theory it should be fine to use random() in non-property contexts, so long as per-element isn’t specified; it’s well-defined what happens with @media (max-width: random(100px, 500px)) {...}, for example. I suspect we want to disallow it, tho?
Define arbitrary substitution function, probably over in Variables, since we have several upcoming functions leaning on this functionality.
Since random-item() is var()-like, we probably want to restrict it to only be usable in properties. (This is likely something we want to apply to all such functions.) Tho random() is a fundamentally different kind of value, we probably want to restrict it as well, for thematic consistency.