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)
Elika J. Etemad / fantasai (Apple)
Miriam E. Suzanne (Invited Expert)
Suggest an Edit for this Spec:
GitHub Editor
Test Suite:
https://wpt.fyi/results/css/css-values/

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 18 August 2025 W3C Process Document.

This spec is in the early exploration phase. Feedback is welcome, and and major breaking changes are expected.

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,

  1. Boolean combinations of a conditional notation. These are written using the <boolean-expr[]> notation, and represent recursive expressions of boolean logic using keywords and parentheses, applied to the grammar specified in brackets, e.g. <boolean-expr[ ( &lt;media-feature&gt; ) ]> 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 <whole-value>, <declaration-value>, and <any-value>) 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. A 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 <declaration-value> not matching <semicolon-token>s, apply regardless of whether it’s {}-wrapped or not.

For example, the grammar of the random-item() function is:
random-item( <random-key>, [<declaration-value>?]# )

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 <declaration-value> 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 <boolean-expr[]>

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 <boolean-expr> production represents this pattern generically.

The <boolean-expr[]> notation wraps another value type in the square brackets within it, e.g. <boolean[ <test> ]>, 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:

<boolean-expr[ <test> ]> = not <boolean-expr-group> | <boolean-expr-group>
                                            [ [ and <boolean-expr-group> ]*
                                            | [ or <boolean-expr-group> ]* ]

<boolean-expr-group> = <test> | ( <boolean-expr[ <test> ]> ) | <general-enclosed>

The <boolean-expr[]> 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 <boolean-expr[]>) 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 <boolean-expr[]>, the grammar for an @container query could be written as:
<container-query> = <boolean-expr[ <cq-test> ]>
<cq-test> = (<size-query>) | style( <style-query> ) | scroll-state( <scroll-state-query> )
<size-query> = <boolean-expr[ ( <size-feature> ) ]> | <size-feature>
<style-query> = <boolean-expr[ ( <style-feature> ) ]> | <style-feature>
<scroll-state-query> = <boolean-expr[ ( <scroll-state-feature> ) ]> | <scroll-state-feature>

The <general-enclosed> 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 <test> term in a <boolean-expr[]> should be defined to match <general-enclosed>.

3.3. Specifying CSS Syntax in CSS: the <syntax> 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 <syntax> 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:

<syntax> = '*' | <syntax-component> [ <syntax-combinator> <syntax-component> ]* | <syntax-string>
<syntax-component> = <syntax-single-component> <syntax-multiplier>?
                   | '<' transform-list '>'
<syntax-single-component> = '<' <syntax-type-name> '>' | <ident>
<syntax-type-name> = angle | color | custom-ident | image | integer
                   | length | length-percentage | number
                   | percentage | resolution | string | time
                   | url | transform-function
<syntax-combinator> = '|'
<syntax-multiplier> = [ '#' | '+' ]

<syntax-string> = <string>

A <syntax-component> consists of either a <syntax-type-name> between <> (angle brackets), which maps to one of the supported syntax component names, or an <ident>, which represents any keyword. Additionally, a <syntax-component> may contain a multiplier, which indicates a list of values.

Note: This means that <length> and length are two different types: the former describes a <length>, whereas the latter describes a keyword length.

Multiple <syntax-component>s may be combined with a | <delim-token>, causing the syntax components to be matched against a value in the specified order.

<percentage> | <number> | auto

The above, when parsed as a <syntax>, would accept <percentage> values, <number> values, as well as the keyword auto.

red | <color>

The syntax definition resulting from the above <syntax>, when used as a grammar for parsing, would match an input red as an identifier, but would match an input blue as a <color>.

The * <delim-token> represents the universal syntax definition.

The <transform-list> production is a convenience form equivalent to <transform-function>+. Note that <transform-list> may not be followed by a <syntax-multiplier>.

Whitespace is not allowed between the angle bracket <delim-token>s (< >) and the <syntax-type-name> they enclose, nor is whitespace allowed to precede a <syntax-multiplier>.

Note: The whitespace restrictions also apply to <transform-list>.

A <syntax-string> is a <string> whose value successfully parses as a <syntax>, and represents the same value as that <syntax> would.

Note: <syntax-string> mostly exists for historical purposes; before <syntax> was defined, the @property rule used a <string> for this purpose.

3.3.1. Parsing as <syntax>

The purpose of a <syntax> 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 <syntax>:

To parse with a <syntax> given a string or list or component values values, a <syntax> value syntax, and optionally an element el for context, perform the following steps. It returns either CSS values, or the guaranteed-invalid value.
  1. Parse a list of component values from values, and let raw parse be the result.

  2. If el was given, substitute arbitrary substitution functions in raw parse, and set raw parse to that result.

  3. parse values according to syntax, with a * value treated as <declaration-value>?, and let parsed result be the result. If syntax used a | combinator, let parsed result be the parse result from the first matching clause.

  4. If parsed result is failure, return the guaranteed-invalid value.

  5. Assert: parsed result is now a well-defined list of one or more CSS values, since each branch of a <syntax> defines an unambiguous parse result (or the * syntax is unambiguous on its own).

  6. 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 <url> type

See CSS Values 4 § 4.5 Resource Locators: the <url> type.

Tests

4.1.1. Request URL Modifiers

<request-url-modifier>s are <url-modifier>s that affect the <url>’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 <request-url-modifier>s:

<request-url-modifier> = <cross-origin-modifier> | <integrity-modifier> | <referrer-policy-modifier>
<cross-origin-modifier> = cross-origin(anonymous | use-credentials)
<integrity-modifier> = integrity(<string>)
<referrer-policy-modifier> = 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-modifier> = cross-origin(anonymous | use-credentials)
The URL request modifier steps for this modifier given request req are:
  1. Set req’s mode to "cors".

  2. If the given value is use-credentials, set req’s credentials mode to "include".

  3. Otherwise, set req’s credentials mode to "same-origin".

<integrity-modifier> = integrity(<string>)
The URL request modifier steps for this modifier given request req are to set request’s integrity metadata to the given <string>.
<referrer-policy-modifier> = 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.
To apply request modifiers from URL value given a request req and a <url> url, call the URL request modifier steps for url’s <request-url-modifier>s in sequence given req.
Tests

4.2. 2D Positioning: the <position> type

The <position> 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:

<position> = <position-one> | <position-two> | <position-four>
<position-one> = [
  left | center | right | top | bottom |
  x-start | x-end | y-start | y-end |
  block-start | block-end | inline-start | inline-end |
  <length-percentage>
]
<position-two> = [
  [ left | center | right | x-start | x-end ] &&
  [ top | center | bottom | y-start | y-end ]
|
  [ left | center | right | x-start | x-end | <length-percentage> ]
  [ top | center | bottom | y-start | y-end | <length-percentage> ]
|
  [ block-start | center | block-end ] &&
  [ inline-start | center | inline-end ]
|
  [ start | center | end ]{2}
]
<position-four> = [
  [ [ left | right | x-start | x-end ] <length-percentage> ] &&
  [ [ top | bottom | y-start | y-end ] <length-percentage> ]
|
  [ [ block-start | block-end ] <length-percentage> ] &&
  [ [ inline-start | inline-end ] <length-percentage> ]
|
  [ [ start | end ] <length-percentage> ]{2}
]

If only one value is specified (<position-one>), the second value is assumed to be center.

If two values are given (<position-two>), a <length-percentage> as the first value represents the horizontal position as the offset between the left edges of the alignment subject and alignment container, and a <length-percentage> 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 (<position-four>) then each <length-percentage> 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 10px top 15px;   /* 10px, 15px */
background-position: left      top     ;   /*  0px,  0px */
background-position:      10px     15px;   /* 10px, 15px */
background-position: left          15px;   /*  0px, 15px */
background-position:      10px top     ;   /* 10px,  0px */
<position>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 3em bottom 10px

The computed value of a <position> is a pair of offsets (horizontal and vertical), each given as a computed <length-percentage> value, representing the distance between the left edges and top edges (respectively) of the alignment subject and alignment container.

<length-percentage>
A <length-percentage> 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.

A <percentage> for the horizontal offset is relative to (width of alignment container - width of alignment subject). A <percentage> 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 image position within element
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 y/x 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]

4.2.1. Parsing <position>

When specified in a grammar alongside other keywords, <length>s, or <percentage>s, <position> is greedily parsed; it consumes as many components as possible.

For example, transform-origin defines a 3D position as (effectively) <position> <length>?. A value such as left 50px will be parsed as a 2-value <position>, with an omitted z-component; on the other hand, a value such as top 50px will be parsed as a single-value <position> followed by a <length>.

4.2.2. Serializing <position>

When serializing the specified value of a <position>:

If only one component is specified:
  • The implied center keyword is added, and a 2-component value is serialized.

If two components are specified:
  • Keywords are serialized as keywords.

  • <length-percentage>s are serialized as <length-percentage>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: <position> 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 <position> is placed next to a <length>, such as transform-origin.

The computed value of a <position> is serialized as a pair of <length-percentage>s representing offsets from the left and top edges, in that order.

4.2.3. Combination of <position>

Interpolation of <position> is defined as the independent interpolation of each component (x, y) normalized as an offset from the top left corner as a <length-percentage>.

Addition of <position> is likewise defined as the independent addition each component (x, y) normalized as an offset from the top left corner as a <length-percentage>.

5. Interpolation Progress Calculations: the progress() notation

Tests

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:

<progress()> = progress(no-clamp? <calc-sum>, <calc-sum>, <calc-sum>)

where the first, second, and third <calc-sum> values represent the progress value, progress start value, and progress end value, respectively.

If no-clamp is present, the progress() function can potentially resolve to any number; if omitted (the default), the return value is clamped to the range [0, 1].

The argument calculations can resolve to any <number>, <dimension>, or <percentage>, but must have a consistent type or else the function is invalid.

The result of progress() is a <number> made consistent with the consistent type of its arguments, resolved by calculating a progress function as follows:
If the progress start value and progress end value are different values

(progress value - progress start value) / (progress end value - progress start value), clamped to the [0,1] range if no-clamp is not specified.

If the progress start value and progress end value are the same value

0 if no-clamp is not specified.

Otherwise, 0, -∞, or +∞, depending on whether progress value is equal to, less than, or greater than the shared value.

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 && <percentage>? ]# )

where the options can provide type-specific mixing options, and each value and optional <percentage> 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 <percentage> is omitted, or else assigned to a “none” type value as defined by the specific mix notation.

To normalize mix percentages given

returning a list of mix items with normalized percentages and a “leftover” percentage:

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

  2. For each omitted percentage in items, set it to (100% - specified sum) / (number of omitted percentages).

  3. Let total be the sum of the percentages of all the items.

  4. 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).

  5. If total is less than 100%, let leftover be (100% - total). Otherwise, let leftover be 0%.

  6. 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( [ <calc-sum> <percentage [0,100]>? ]# )

The <calc-sum> arguments can resolve to any <number>, <dimension>, or <percentage>, 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 <calc-sum> values.

The used value of a valid calc-mix() is the result of producing a weighted average of its <calc-sum> values, with the weight of each item given by its corresponding <percentage> 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 <calc-sum> and <percentage> values in it can be resolved, and is a calc-mix() notation with each of its <calc-sum> and <percentage> 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 <transform-list>, with the following syntactic form:

<transform-mix()> = transform-mix( [ <transform-list> && <percentage [0,100]> ]# )

The used value of a valid transform-mix() is the result of producing a weighted average of its <transform-list> values, with the weight of each item given by its corresponding <percentage> 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 <transform-list> using its <percentage> weight as the interpolation progress from the identity transform towards the <transform-list>. See CSS Transforms 1 § 9 Interpolation of Transforms.

If every <percentage> weight can be fully resolved, and the <transform-list>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 <transform-function>.

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(100vw in lch,
  200px: palegoldenrod,
  800px: palegreen,
  2000px: 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(100vw, 200px, 2000px) ease-in-out;
  font-size: calc-interpolate(var(--font-scale),
    0%: 16px,
    70%: 20px,
    100%: 24px);
}
h1 {
  font-size: calc-interpolate(var(--font-scale),
    0%: 1.2rem,
    40%: 2rem,
    100%: 3rem);
}

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(
  [
    <progress-source> && [ by <easing-function> ]?
    && <easing-function>? && <segment-options>?
  ] ,
  <input-position>{1,2} : <output-value> ,
  [
    [ <easing-function> || <segment-options> ]? ,
    <input-position>{1,2} : <output-value>
  ]#?
)

These represent the interpolation progress and interpolation map as described below.

7.1.1. Specifying the Interpolation Progress

The <progress-source> value type represents the interpolation progress in an interpolation notation. Its syntax is:

<progress-source> = <percentage> | <number> | <dimension> | <'animation-timeline'>

where:

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

Note: This allows the use of the progress() notations and other math functions that output a <number>.

<dimension>
Represents the interpolation progress as a dimension, which is converted to a number as specified in § 7.2 Validation and Normalization.

Note: Using a <dimension> value as <progress-source> requires at least some of the stops in the interpolation map to also use <dimension> 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/0% and above 1/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 <easing-function> argument

The interpolation progress given by <progress-source> may be optionally modified by the <easing-function> 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 <segment-options> and <easing-function> arguments applying between those keyframes.

The interpolation map values are defined as follows:

<input-position>{1,2} : <output-value>
Represents an interpolation stop associating the specified input position(s) with the specified output values. As with the gradient functions, if two <input-position>s are specified, it is treated the same as two stops with the same <output-value>.
<input-position> = <percentage> | <number> | <dimension>

Note: <output-value> is not given a grammar here, as the specific functions specify what their outputs are. <input-position>, however, is linked to <progress-source>, see § 7.2.1 Type Checking.

<easing-function>
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 <easing-function> 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.

For example, in color-interpolate(20%, 0%: red, ease-in-out, 80%: green, 100%: blue), the 20% interpolation progress becomes a 25% segment interpolation progress, as 20% is 25% of the way between 0% and 80%, the surrounding input positions.
<segment-options>
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 <color-interpolation-method>.) 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 <segment-options> argument.

All positions before the first interpolation stop map to the first stop’s <output-value>, and all positions after the last interpolation stop map to the last stop’s <output-value>. 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 <progress-source> and <input-position> has a type, which can be proportional (<number>, <percentage>, 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 <progress-source> and <input-position> values that are not proportional must have a consistent type for the notation to be valid. Additionally, an interpolation notation whose <progress-source> is not proportional must have at least one absolute <input-position> (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 (<time> or <length>).

7.2.2. Calculating the Absolute Interpolation Range

The interpolation range specifies the <dimension> values corresponding to 0% and 100% interpolation progress, and thereby defines the mapping between proportional and absolute values.

In an absolutely typed interpolation map, the first and last absolute stops in the list are assigned to 0% and 100%, respectively. (If there is only one absolutely typed interpolation stop, it is assigned to both 0% and 100%.)

A proportionally typed interpolation map does not define an interpolation range.

7.2.3. Normalizing Absolute Positions and Progress

If a <progress-source> or <input-position> is specified as a <dimension>, it is normalized to a number by linearly interpolating it between the start and end of the interpolation range. (This can produce values less than 0 or greater than 1.)

For example, given a function like:
background-color: color-interpolate(300px in hsl, 200px: red, 500px: green, 600px: blue);

The interpolation range is (200px, 600px), so the <progress-source> is converted to the number 0.25, and the three stops are converted to the numbers 0, .75, and 1.

Thus, that function is equivalent to:

background-color: color-interpolate(25% in hsl, 0%: red, 75%: green, 100%: blue);

In either case, the result will be a color 1/3 of the way between red and green using HSL interpolation, giving hsl(40deg, 100%, 50%), a light orange.

7.2.4. Interpolation Stop Fixup

Once all interpolation stops have been normalized, they are additionally fixed up so that they progress in order from the first stop to the last. If any stop has an input position that is less than the input position of the preceding stop, it is set to the input position of the previous stop.

7.3. Interpolation Resolution

The used value of a valid interpolation notation is the result of mapping its interpolation progress through the specified interpolation map.

The computed value is the used value if it is possible to calculate. Otherwise, if its interpolation progress, input positions, and interpolation options can all be resolved, then it is simplified to a two-stop interpolation notation with <number> values for the interpolation progress and input positions, and all other values computed according to their type. Otherwise, it is the interpolation notation itself, with its arguments computed individually otherwise.

7.4. Interpolated Numeric and Dimensional Values: the calc-interpolate() notation

The calc-interpolate() interpolation notation represents an interpolated numeric or dimensional value. Like calc(), it is a math function, with the following syntax:

<calc-interpolate()> = calc-interpolate(
  [
    <progress-source> && [ by <easing-function> ]?
    && <easing-function>?
  ] ,
  <input-position>{1,2} : <calc-sum> ,
  [ <easing-function>? , <input-position>{1,2} : <calc-sum> ]#? )

The <calc-sum> arguments can resolve to any <number>, <dimension>, or <percentage>, but must have a consistent type or else the function is invalid. The result’s type will be that consistent type.

The <progress-source> and <input-position> values do not use the inherited calculation context; they resolve percentages as specified in § 7.2 Validation and Normalization.

7.5. Interpolated Color Values: the color-interpolate() notation

The color-interpolate() interpolation notation represents an interpolated <color> value, with the following syntax:

<color-interpolate()> = color-interpolate(
  [
    <progress-source> && [ by <easing-function> ]?
    && <easing-function>? && <color-interpolation-method>?
  ] ,
  <input-position>{1,2} : <color>,
  [
    [ <easing-function> || <color-interpolation-method> ]?,
    <input-position>{1,2} : <color>
  ]#? )

7.6. Interpolated Transform Values: the transform-interpolate() notation

The transform-interpolate() interpolation notation represents an interpolated <transform-list>, with the following syntax:

<transform-interpolate()> = transform-interpolate(
  [
    <progress-source> && [ by <easing-function> ]?
    && <easing-function>?
  ],
  <input-position>{1,2} : <transform-list>,
  [ <easing-function>?, <input-position>{1,2} : <transform-list> ]#? )

transform-interpolate() is, itself, a <transform-function>.

7.7. Interpolated Property Values: the interpolate() notation

The interpolate() interpolation notation represents the interpolation of entire property values, which supports two alternative syntax patterns:

<interpolate()> = interpolate(
  [
    <progress-source> && [ by <easing-function> ]?
    && <easing-function>?
  ] ,
  <input-position>{1,2} : <whole-value>,
  [ <easing-function>?, <input-position>{1,2} : <whole-value> ]#? )
|
  interpolate( <progress-source> && [ by <easing-function> ]?
    && <easing-function>? of <keyframes-name> )

The first syntax alternative, like other interpolation notations, assembles an interpolation map from a list of interpolation stops. The second uses the corresponding property declarations from a set of keyframes, incorporating those values into the normal cascade for this property.

Each <whole-value> argument is computed as if it were the value of this property; and all other arguments computed according to their type.

For example, most uses of interpolate() will resolve at computed-value time:
color: interpolate(90%, 0: red, 1: blue);
/* via simple interpolation,
   computes to: */
color: rgb(10% 0 90%);

color: interpolate(90%, 0: currentcolor, 1: black);
/* can't be fully resolved at computed-value time,
   but still has a defined representation: */
color: color-mix(currentcolor 90%, black 10%);

float: interpolate(90%, 0: left, 1: right);
/* discretely animatable */
float: right;

The interpolate() notation is a <whole-value>. Additionally, if any of its <whole-value> arguments are not animatable, the notation is invalid.

For example, the following declarations are invalid, and will be ignored:
/* Invalid start value */
color: interpolate(90%, 0: #invalid, 1: #F00);

/* Function is mixed with other values */
background: url(ocean) interpolate(10%, 0: blue, 1: yellow);

/* 'animation-*' is not animatable */
animation-delay: interpolate(0%, 0: 0s, 1: 2s);

8. Miscellaneous Value Substituting Functions

8.1. Representing An Entire Property Value: the <whole-value> type

Several functions defined in this specification can only be used as the "whole value" of a property. For example, background-position: toggle(50px 50px, center); is valid, but background-position: toggle(50px, center) 50px; is not. The <whole-value> production represents these values.

All properties implicitly accept a <whole-value> as their entire value, just as they accept the CSS-wide keywords as their entire value.

When used as a component value of a function, <whole-value> also represents any CSS value normally valid as the whole value of the property in which it is used (including additional <whole-value> functions). However, some functions may restrict what a <whole-value> argument can include.

8.2. Selecting the First Supported Value: the first-valid() notation

CSS supports progressive enhancement with its forward-compatible parsing: authors can declare the same property multiple times in a style rule, using different values each time, and a CSS UA will automatically use the last one that it understands and throw out the rest. This principle, together with the @supports rule, allows authors to write stylesheets that work well in old and new UAs simultaneously.

However, using var() (or similar substitution functions that resolve after parsing) thwarts this functionality; CSS UAs must assume any such property is valid at parse-time.

The first-valid() functional notation inlines the fallback behavior intrinsic to parsing declarations. Unlike most notations, it can accept any valid or invalid syntax in its arguments, and represents the first value among its arguments that is supported (parsed as valid) by the UA as the whole value of the property it’s used in.

<first-valid()> = first-valid( <declaration-value># )

If none of the arguments represent a valid value for the property, the property is invalid at computed-value time.

first-valid() is a <whole-value>.

Should this have a different name? We didn’t quite decide on it during the resolution to add this.

Note: Despite effectively taking <whole-value>s as its argument, first-valid() is instead defined to take <declaration-value>s because, by definition, it’s intended to be used in cases where its values might be invalid for the declaration it’s in. <declaration-value> imposes no contextual validity constraints on what it matches, unlike <whole-value>.

8.3. Conditional Value Selection: the if() notation

Tests

The if() function is an arbitrary substitution function that represents conditional values. Its argument consists of an ordered semi-colon–separated list of statements, each consisting of a condition followed by a colon followed by a value. An if() function represents the value corresponding to the first condition in its argument list to be true; if no condition matches, then the if() function represents an empty token stream.

The if() function’s syntax is defined as follows:

<if()> = if( [ <if-branch> ; ]* <if-branch> ;? )
<if-branch> = <if-condition> : <declaration-value>?
<if-condition> = <boolean-expr[ <if-test> ]> | else
<if-test> =
  supports( [ <ident> : <declaration-value> ] | <supports-condition> ) |
  media( <media-feature> | <media-condition> ) |
  style( <style-query> )

The else keyword represents a condition that is always true.

The if() function’s argument grammar is:

<if-args> = if( [ <if-args-branch> ; ]* <if-args-branch> ;? )
<if-args-branch> = <declaration-value> : <declaration-value>?

In the above <if-args-branch> production, the first <declaration-value> excludes top-level <colon-token>s as part of its free-form production restrictions (alongside commas and curly braces).

To replace an if() function, given a list of arguments:
  1. For each <if-args-branch> branch in arguments:

    1. Substitute arbitrary substitution functions in the first <declaration-value> of branch, then parse the result as an <if-condition>. If parsing returns failure, continue; otherwise, let the result be condition.

    2. Evaluate condition.

      If a <style-query> in condition tests the value of a property, and guarding a substitution context «"property", referenced-property-name» would mark it as a cyclic substitution context, that query evaluates to false.

      For example, in --foo: if(style(--foo: bar): baz); the style() query is automatically false, since property replacement has already established a «"property", "--foo"» substitution context.

      If the result of condition is false, continue.

    3. Substitute arbitrary substitution functions in the second <declaration-value> of branch, and return the result.

  2. Return nothing (an empty sequence of component values).

Note: Unlike using @media/@supports/@container rules, which just ignore their contents when they’re false and let the cascade determine what values otherwise apply, declarations with if() do not roll back the cascade if the conditions are false; any fallback values must be provided inline. However, see the revert-rule CSS-wide keyword.

8.4. Toggling Between Values: the toggle() notation

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( <whole-value># )

The toggle() notation is a <whole-value>. However, it 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: 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.

8.5. Custom Property References: the var() notation

The var() notation substitutes the value of a custom property, see the CSS Custom Properties for Cascading Variables Module. [CSS-VARIABLES]

8.6. Inherited Value References: the inherit() notation

Tests

Like the inherit keyword, the inherit() functional notation resolves to the computed value of a property on the parent. Rather than resolving to the value of the same property, however, it resolves to a sequence of component values representing the computed value of the property specified as its first argument. Its second argument, if present, is used as a fallback in case the first argument resolves to the guaranteed-invalid value.

inherit() is an arbitrary substitution function whose syntax is defined as:

<inherit()> = inherit( <custom-property-name>, <declaration-value>? )

The inherit() function’s argument grammar is:

<inherit-args> = inherit( <declaration-value>, <declaration-value>? )

Like var(), a bare comma can be used with nothing following it, indicating that the second <declaration-value> was passed, just as an empty sequence.

Note: That is, inherit(--foo) does not pass a fallback value, but inherit(--foo,) does (the fallback is just empty).

To replace an inherit() function, given a list of arguments:
  1. Substitute arbitrary substitution functions in the first <declaration-value> of arguments, then parse it as a <custom-property-name>.

  2. If parsing returned a <custom-property-name>, and the inherited value of that custom property on the element does not contain the guaranteed-invalid value, return that inherited value.

  3. Otherwise, if a second <declaration-value>? was passed in arguments, substitute arbitrary substitution functions in that argument, and return the result.

  4. Otherwise, return the guaranteed-invalid value.

Note: Future levels of CSS may allow specifying standard CSS properties in inherit(); however because the tokenization of computed values is not fully standardized for all CSS properties, this feature is deferred from Level 5. Note that the computed value differs from the used value, and is not always the resolved value returned by getComputedStyle(); thus even if inherit(width) were allowed, it would frequently return the keyword auto, not the used <length>.

8.7. Attribute References: the attr() notation

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( <attr-name> <attr-type>? , <declaration-value>?)

<attr-name> = [ <ident-token>? '|' ]? <ident-token>
<attr-type> = type( <syntax> ) | raw-string | number | <attr-unit>
<attr-unit> = <custom-ident>

The attr() function’s argument grammar is:

<attr-args> = attr( <declaration-value>, <declaration-value>? )
Tests

Like var(), a bare comma can be used with nothing following it, indicating that the second <declaration-value> was passed, just as an empty sequence.

Note: That is, attr(foo) does not pass a fallback value, but attr(foo,) does (the fallback is just empty).

<attr-unit> is intended to represents an identifier that is an ASCII case-insensitive match for the name of a CSS dimension unit, such as px, or the <delim-token> %. As this set expands regularly, it is actually specified as a <custom-ident>, and triggers fallback behavior when it doesn’t match a known unit (rather than making the function invalid).

The arguments of attr() are:

<attr-name>

Gives the name of the attribute being referenced, similar to <wq-name> (from [SELECTORS-3]) but without the possibility of a wildcard prefix.

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.) Unless otherwise specified, the case-sensitivity/matching rules of <attr-name> are identical to attribute selectors (and thus might depend on the host language).

Whitespace is not allowed between any of the components of <attr-name>.

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 how the attribute value is parsed into a CSS value.

If given as a type() function, the value is parsed according to the <syntax> argument, and substitutes as the resulting tokens. For this purpose, <url> is invalid as a <syntax-single-component>. Values that fail to parse according to the syntax trigger fallback.

Note: <url> is banned because, for now, attr() values can’t be used in URLs at all due to security concerns.

If given as the number keyword, it causes the attribute’s literal value, after stripping leading and trailing whitespace, to be parsed as a <number-token>. Values that fail to parse trigger fallback.

If given as an <attr-unit> value, the value is first parsed as if number keyword was specified; if this fails to parse, it triggers fallback. Then, if the <attr-unit> value matches a known CSS unit name or is %, the number is turned into a dimension or percentage with that value and the given unit. If the <attr-unit> does not match a known CSS unit, it triggers fallback.

If given as the raw-string keyword, or omitted entirely, it causes the attribute’s literal value to be treated as the value of a CSS string, with no CSS parsing performed at all (including CSS escapes, whitespace removal, comments, etc). No value triggers fallback; only the lack of the attribute entirely does.

Note: This is different from specifying a syntax of type(*), which still triggers CSS parsing (but with no requirements placed on it beyond that it parse validly), and which substitutes the result of that parsing directly as tokens, rather than as a <string> value.

<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 omitted, the fallback defaults to the empty string if omitted; otherwise, it 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 <length>, the default could still be auto, like in width: attr(size <length>, auto);.
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);
}

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:
  1. Let el be the element that the style containing the attr() function is being applied to. Let first arg be the first <declaration-value> in arguments. Let second arg be the <declaration-value>? passed after the comma, or null if there was no comma.

  2. Substitute arbitrary substitution functions in first arg, then parse it as <attr-name> <attr-type>?. 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 <attr-type> was omitted), processed as specified in the definition of those arguments.

  3. If attr name exists as an attribute on el, let attr value be its value; otherwise jump to the last step (labeled FAILURE).

  4. If syntax is the keyword number or an <attr-unit> value, parse attr value against <attr-type>. 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.

  5. If syntax is null or the keyword raw-string, return a CSS <string> whose value is attr value.

    Note: No parsing or modification of any kind is performed on the value.

  6. Substitute arbitrary substitution functions in attr value, with «"attribute", attr name» as the substitution context, then parse with a <syntax> attr value, with syntax and el. If that succeeds, return the result; otherwise, jump to the last step (labeled FAILURE).

  7. FAILURE:

    1. If second arg is null, and syntax was omitted, return an empty CSS <string>.

    2. If second arg is null, return the guaranteed-invalid value.

    3. Substitute arbitrary substitution functions in second arg, and return the result.

8.7.2. Security

Tests

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 <url> makes a declaration invalid at computed-value time.

For example, all of the following are invalid at computed-value time:

However, using attr() for other purposes is fine, even if the usage is near a url: