CSS Typed OM Level 1

Editor’s Draft,

This version:
https://drafts.css-houdini.org/css-typed-om-1/
Latest published version:
https://www.w3.org/TR/css-typed-om-1/
Previous Versions:
Feedback:
public-houdini@w3.org with subject line “[css-typed-om] … message topic …” (archives)
Issue Tracking:
Inline In Spec
GitHub Issues
Editors:
Tab Atkins-Bittner (Google)
François Remy (Microsoft)
Former Editors:
(Google)

Abstract

Converting CSSOM value strings into meaningfully typed JavaScript representations and back can incur a significant performance overhead. This specification exposes CSS values as typed JavaScript objects to facilitate their performant manipulation.

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.

GitHub Issues are preferred for discussion of this specification. When filing an issue, please put the text “css-typed-om” in the title, preferably like this: “[css-typed-om] …summary of comment…”. All issues and comments are archived.

This document was produced by the CSS Working Group (part of the Style Activity).

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 1 February 2018 W3C Process Document.

1. Introduction

CSS stylesheets are parsed into abstract UA-internal data structures, the underlying values of CSS, which various specification algorithms manipulate.

Underlying values can’t be directly manipulated, as they are implementation-dependent; UAs have to agree on how to interpret the underlying values, but the values themselves are purposely left undefined so that UAs can store and manipulate CSS in whatever way is most efficient for them.

Previously, the only way to read or write to the underlying values was via strings—stylesheets or the CSSOM allowed authors to send strings to the UA, which were parsed into underlying values, and the CSSOM allowed authors to request that the UA serialize their underlying values back into strings.

This specification introduces a new way to interact with underlying values, by representing them with specialized JS objects that can be manipulated and understood more easily and more reliably than string parsing/concatenation. This new approach is both easier for authors (for example, numeric values are reflected with actual JS numbers, and have unit-aware mathematical operations defined for them) and in many cases are more performant, as values can be directly manipulated and then cheaply translated back into underlying values without having to build and then parse strings of CSS.

2. CSSStyleValue objects

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSStyleValue {
    stringifier;
    [Exposed=Window] static CSSStyleValue parse(USVString property, USVString cssText);
    [Exposed=Window] static sequence<CSSStyleValue> parseAll(USVString property, USVString cssText);
};

CSSStyleValue objects are the base class of all CSS values accessible via the Typed OM API.

The stringification behavior of CSSStyleValue objects is defined in §6 CSSStyleValue Serialization.

The parse(property, cssText) method, when invoked, must parse a CSSStyleValue with property property, cssText cssText, and parseMultiple set to false, and return the result.

The parseAll(property, cssText), when invoked, must parse a CSSStyleValue with property property, cssText cssText, and parseMultiple set to true, and return the result.

To parse a CSSStyleValue given a string property, a string cssText, and a parseMultiple flag, run these steps:
  1. If property is not a custom property name string, set property to property ASCII lowercased.

  2. If property is not a valid CSS property, throw a TypeError.

  3. Attempt to parse cssText according to property’s grammar. If this fails, throw a TypeError. Otherwise, let whole value be the parsed result.

    The behavior of custom properties are different when modified via JavaScript than when defined in style sheets.

    When a custom property is defined with an invalid syntax in a style sheet, then the value is recorded as "unset", to avoid having to reparse every style sheet when a custom property is registered.

    Conversely, when a custom property is modified via the JavaScript API, any parse errors are propagated to the progamming environment via a TypeError. This allows more immediate feedback of errors to developers.

  4. Subdivide into iterations whole value, according to property, and let values be the result.

  5. For each value in values, replace it with the result of reifying value for property.

    Define the global.

  6. If parseMultiple is false, return values[0]. Otherwise, return values.

To subdivide into iterations a CSS value whole value for a property property, execute the following steps:
  1. If property is a single-valued property, return a list containing whole value.

  2. Otherwise, divide whole value into individual iterations, as appropriate for property, and return a list containing the iterations in order.

How to divide a list-valued property into iterations is intentionally undefined and hand-wavey at the moment. Generally, you just split it on top-level commas (corresponding to a top-level <foo># term in the grammar), but some legacy properties (such as counter-reset) don’t separate their iterations with commas.

It’s expected to be rigorously defined in the future, but at the moment is explicitly a "you know what we mean" thing.

2.1. Direct CSSStyleValue Objects

Values that can’t yet be directly supported by a more specialized CSSStyleValue subclass are instead represented as CSSStyleValue objects.

Each CSSStyleValue object is associated with a particular CSS property, via its [[associatedProperty]] internal slot, and a particular, immutable, underlying value. These objects are said to "represent" the particular underlying value they were reified from, such that if they are set back into a stylesheet for the same property, they reproduce an equivalent underlying value.

These CSSStyleValue objects are only considered valid for the property that they were parsed for. This is enforced by CSSStyleValue objects having a [[associatedProperty]] internal slot, which is either null (the default) or a string specifying a property name.

Note: This slot is checked by StylePropertyMap.set()/append()

3. The StylePropertyMap

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface StylePropertyMapReadOnly {
    iterable<USVString, sequence<CSSStyleValue>>;
    any get(USVString property);
    /* 'any' means (undefined or CSSStyleValue) here,
       see https://github.com/heycam/webidl/issues/60 */
    sequence<CSSStyleValue> getAll(USVString property);
    boolean has(USVString property);
    readonly attribute unsigned long size;
};

[Exposed=Window]
interface StylePropertyMap : StylePropertyMapReadOnly {
    void set(USVString property, (CSSStyleValue or USVString)... values);
    void append(USVString property, (CSSStyleValue or USVString)... values);
    void delete(USVString property);
    void clear();
};

StylePropertyMap is an alternate way to represent a CSS declaration block as an object (when fetched via the [cssom], CSS declaration blocks are instead represented as CSSStyleDeclaration objects.)

A StylePropertyMapReadOnly object has a [[declarations]] internal slot, which is a map reflecting the CSS declaration block's declarations.

Note: The declarations are not yet defined using [infra] terminology, but for the purpose of this spec it’s assumed to be a map whose keys are strings (representing property names) and whose values are underlying values for those properties.

Unless otherwise stated, the initial ordering of the [[declarations]] internal slot is based on the key of each entry:

  1. Standardized properties (not custom properties or vendor-prefixed properties), ASCII lowercased and then sorted in increasing code-point order.

  2. Vendor-prefixed/experimental properties (those whose name starts with a single dash), ASCII lowercased and then sorted in increasing code-point order.

  3. Custom properties, sorted in increasing code-point order. (These are never lower-cased; they are preserved exactly as written.)

The value pairs to iterate over for a StylePropertyMapReadOnly object this are obtained as follows:
  1. Let declarations be this’s [[declarations]] slot.

  2. Let value pairs be an empty list.

  3. For each propvalue in declarations:

    1. Let iterations be the result of dividing into iterations value.

    2. Reify each item of iterations, and let objects be the result.

    3. Append prop/objects to value pairs.

  4. Return value pairs.

Some CSS properties are list-valued properties, such as background-image or animation; their value is a list of parallel grammar terms, almost always comma-separated (the only exceptions are certain legacy properties like counter-reset), indicating multiple distinct "values" interpreted in the same way. Other properties, such as color, are single-valued properties; they take only a single (possibly complex) value.

w3c/css-houdini-drafts/644[css-typed-om]Define precisely which properties are list-valued and which aren't, probably in an appendix.
There are multiple examples of CSS properties that have transitioned from being single-valued to list-valued. To ensure that code written at a time when a property was single-valued does not break when it becomes list-valued in the future, the StylePropertyMap is a multi-map; it stores list of values for each key, but allows you to interact with it as if there was only a single value for each key as well.

This means that multiple values for a single property in a StylePropertyMap do not represent multiple successive definition of that property’s value; instead, they represent multiple comma-separated sub-values in a single property value, like each "layer" in a background-image property.

The get(property) method, when called on a StylePropertyMap this, must perform the following steps:
  1. If property is not a custom property name string, set property to property ASCII lowercased.

  2. If property is not a valid CSS property, throw a TypeError.

  3. Let props be the value of this’s [[declarations]] internal slot.

  4. If props[property] exists, subdivide into iterations props[property], then reify the first item of the result and return it.

    Otherwise, return undefined.

    Define the global.

The getAll(property) method, when called on a StylePropertyMap this, must perform the following steps:
  1. If property is not a custom property name string, set property to property ASCII lowercased.

  2. If property is not a valid CSS property, throw a TypeError.

  3. Let props be the value of this’s [[declarations]] internal slot.

  4. If props[property] exists, subdivide into iterations props[property], then reify each item of the result, and return the list.

    Otherwise, return an empty list.

    Define the global.

The has(property) method, when called on a StylePropertyMap this, must perform the following steps:
  1. If property is not a custom property name string, set property to property ASCII lowercased.

  2. If property is not a valid CSS property, throw a TypeError.

  3. Let props be the value of this’s [[declarations]] internal slot.

  4. If props[property] exists, return true. Otherwise, return false.

The size attribute, on getting from a StylePropertyMap this, must perform the following steps:
  1. Return the size of the value of this’s [[declarations]] internal slot.

The set(property, ...values) method, when called on a StylePropertyMap this, must perform the following steps:
  1. If property is not a custom property name string, set property to property ASCII lowercased.

  2. If property is not a valid CSS property, throw a TypeError.

  3. If property is a single-valued property and values has more than one item, throw a TypeError.

  4. If any of the items in values have a non-null [[associatedProperty]] internal slot, and that slot’s value is anything other than property, throw a TypeError.

  5. If the size of values is two or more, and one or more of the items are a CSSUnparsedValue or CSSVariableReferenceValue object, throw a TypeError.

    Note: Having 2+ values implies that you’re setting multiple items of a list-valued property, but the presence of a var() function in the string-based OM disables all syntax parsing, including splitting into individual iterations (because there might be more commas inside of the var() value, so you can’t tell how many items are actually going to show up). This step’s restriction preserves the same semantics in the Typed OM.

  6. Let props be the value of this’s [[declarations]] internal slot.

  7. If props[property] exists, remove it.

  8. Let values to set be an empty list.

  9. For each value in values, create an underlying value for property and value, and append the result to values to set.

  10. Set props[property] to values to set.

Note: The property is deleted then added back so that it gets put at the end of the ordered map, which gives the expected behavior in the face of shorthand properties.

The append(property, ...values) method, when called on a StylePropertyMap this, must perform the following steps:
  1. If property is not a custom property name string, set property to property ASCII lowercased.

  2. If property is not a valid CSS property, throw a TypeError.

  3. If property is not a list-valued property, throw a TypeError.

  4. If any of the items in values have a non-null [[associatedProperty]] internal slot, and that slot’s value is anything other than property, throw a TypeError.

  5. If any of the items in values are a CSSUnparsedValue or CSSVariableReferenceValue object, throw a TypeError.

    Note: When a property is set via string-based APIs, the presence of var() in a property prevents the entire thing from being interpreted. In other words, everything besides the var() is a plain component value, not a meaningful type. This step’s restriction preserves the same semantics in the Typed OM.

  6. Let props be the value of this’s [[declarations]] internal slot.

  7. If props[property] does not exist, set props[property] to an empty list.

  8. If props[property] contains a var() reference, throw a TypeError.

  9. Let temp values be an empty list.

  10. For each value in values, create an underlying value with property and value, and append the returned value to temp values.

  11. Append the entries of temp values to props[property].

The delete(property) method, when called on a StylePropertyMap this, must perform the following steps:
  1. If property is not a custom property name string, set property to property ASCII lowercased.

  2. If property is not a valid CSS property, throw a TypeError.

  3. If this’s [[declarations]] internal slot contains property, remove it.

The clear() method, when called on a StylePropertyMap this, must perform the following steps:
  1. Remove all of the declarations in this’s [[declarations]] internal slot.

To create an underlying value, given a string property and a string or CSSStyleValue value:
If value is a direct CSSStyleValue,

Return value’s associated value.

If value is a CSSStyleValue subclass,

If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.

If any component of property’s CSS grammar has a limited numeric range, and the corresponding part of value is a CSSUnitValue that is outside of that range, replace that value with the result of wrapping it in a fresh CSSMathSum whose values internal slot contains only that part of value.

Return the value.

If value is a USVString,

Parse a CSSStyleValue with property property, cssText value, and parseMultiple set to false, and return the result.

Note: This can throw a TypeError instead.

CSS properties express their valid inputs with grammars, which are written with the assumption of being matched against strings parsed into CSS tokens, as defined in CSS Syntax 3 §4 Tokenization. CSSStyleValue objects can also be matched against these grammars, however.

A CSSStyleValue is said to match a grammar based on the following rules:

Note: As the ability to create more complex values in Typed OM increases, this section will become more complex.

A string is a custom property name string if it starts with two dashes (U+002D HYPHEN-MINUS), like --foo. (This corresponds to the <custom-property-name> production, but applies to strings, rather than identifiers; it can be used without invoking the CSS parser.)

A string is a valid CSS property if it is a custom property name string, or is a CSS property name recognized by the user agent.

3.1. Computed StylePropertyMapReadOnly objects

partial interface Element {
    [SameObject] StylePropertyMapReadOnly computedStyleMap();
};

Computed StylePropertyMap objects represent the computed values of an Element, and are accessed by calling the computedStyleMap() method.

Every Element has a [[computedStyleMapCache]] internal slot, initially set to null, which caches the result of the computedStyleMap() method when it is first called.

The computedStyleMap() method must, when called on an Element this, perform the following steps:
  1. If this’s [[computedStyleMapCache]] internal slot is set to null, set its value to a new StylePropertyMapReadOnly object, whose [[declarations]] internal slot are the name and computed value of every longhand CSS property supported by the User Agent, every registered custom property, and every non-registered custom property which is not set to its initial value on this, in the standard order.

    The computed values in the [[declarations]] of this object must remain up-to-date, changing as style resolution changes the properties on this and how they’re computed.

    Note: In practice, since the values are "hidden" behind a .get() method call, UAs can delay computing anything until a given property is actually requested.

  2. Return this’s [[computedStyleMapCache]] internal slot.

Note: like Window.getComputedStyle(), this method can expose information from stylesheets with the origin-clean flag unset.

Note: The StylePropertyMapReadOnly returned by this method represents the actual computed values, not the resolved value concept used by Window.getComputedStyle(). It can thus return different values than Window.getComputedStyle() for some properties (such as width).

Note: Per WG resolution, pseudo-element styles are intended to be obtainable by adding this method to the new PseudoElement interface (rather than using a pseudoElt argument like Window.getComputedStyle() does).

3.2. Declared & Inline StylePropertyMap objects

partial interface CSSStyleRule {
    [SameObject] readonly attribute StylePropertyMap styleMap;
};

partial interface ElementCSSInlineStyle {
    [SameObject] readonly attribute StylePropertyMap attributeStyleMap;
};

Declared StylePropertyMap objects represent style property-value pairs embedded in a style rule or inline style, and are accessed via the styleMap attribute of CSSStyleRule objects, or the attributeStyleMap attribute of objects implementing the ElementCSSInlineStyle interface (such as Elements).

When constructed, the [[declarations]] internal slot for declared StylePropertyMap objects is initialized to contain an entry for each property with a valid value inside the CSSStyleRule or inline style that the object represents, in the same order as the CSSStyleRule or inline style.

4. CSSStyleValue subclasses

4.1. CSSUnparsedValue objects

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(sequence<CSSUnparsedSegment> members)]
interface CSSUnparsedValue : CSSStyleValue {
    iterable<CSSUnparsedSegment>;
    readonly attribute unsigned long length;
    getter CSSUnparsedSegment (unsigned long index);
    setter CSSUnparsedSegment (unsigned long index, CSSUnparsedSegment val);
};

typedef (USVString or CSSVariableReferenceValue) CSSUnparsedSegment;

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(USVString variable, optional CSSUnparsedValue? fallback = null)]
interface CSSVariableReferenceValue {
    attribute USVString variable;
    readonly attribute CSSUnparsedValue? fallback;
};

CSSUnparsedValue objects represent property values that reference custom properties. They are comprised of a list of string fragments and variable references.

They have a [[tokens]] internal slot, which is a list of USVStrings and CSSVariableReferenceValue objects. This list is the object’s values to iterate over.

The length attribute returns the size of the [[tokens]] internal slot.

The supported property indexes of a CSSUnparsedValue this are the integers greater than or equal to 0, and less than the size of this’s [[tokens]] internal slot.

To determine the value of an indexed property of a CSSUnparsedValue this and an index n, let tokens be this’s [[tokens]] internal slot, and return tokens[n].

To set the value of an existing indexed property of a CSSUnparsedValue this, an index n, and a value new value, let tokens be this’s [[tokens]] internal slot, and set tokens[n] to new value.

To set the value of a new indexed property of a CSSUnparsedValue this, an index n, and a value new value, let tokens be this’s [[tokens]] internal slot. If n is not equal to the size of tokens, throw a RangeError. Otherwise, append new value to tokens.

The getter for the variable attribute of a CSSVariableReferenceValue this must return its variable internal slot.

The variable attribute of a CSSVariableReferenceValue this must, on setting a variable variable, perform the following steps:

  1. If variable is not a custom property name string, throw a TypeError.

  2. Otherwise, set this’s variable internal slot to variable.

The CSSVariableReferenceValue(variable, fallback) constructor must, when called, perform the following steps:
  1. If variable is not a custom property name string, throw a TypeError.

  2. Return a new CSSVariableReferenceValue with its variable internal slot set to variable and its fallback internal slot set to fallback.

4.2. CSSKeywordValue objects

CSSKeywordValue objects represent CSS keywords and other identifiers.

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(USVString value)]
interface CSSKeywordValue : CSSStyleValue {
    attribute USVString value;
};
The CSSKeywordValue(value) constructor must, when called, perform the following steps:
  1. If value is an empty string, throw a TypeError.

  2. Otherwise, return a new CSSKeywordValue with its value internal slot set to value.

Any place that accepts a CSSKeywordValue also accepts a raw USVString, by using the following typedef and algorithm:

The value attribute of a CSSKeywordValue this must, on setting a value value, perform the following steps:
  1. If value is an empty string, throw a TypeError.

  2. Otherwise, set this’s value internal slot, to value.

4.3. Numeric Values:

CSSNumericValue objects represent CSS values that are numeric in nature (<number>s, <percentage>s, <dimension>s). There are two interfaces that inherit from CSSNumericValue:

CSSNumericValue objects are not range-restricted. Any valid numeric value can be represented by a CSSNumericValue, and that value will not be clamped, rounded, or rejected when set on a declared StylePropertyMap. Instead, clamping and/or rounding will occur during computation of style.

The following code is valid
myElement.attributeStyleMap.set("opacity", CSS.number(3));
myElement.attributeStyleMap.set("z-index", CSS.number(15.4));

console.log(myElement.attributeStyleMap.get("opacity").value); // 3
console.log(myElement.attributeStyleMap.get("z-index").value); // 15.4

var computedStyle = myElement.computedStyleMap();
var opacity = computedStyle.get("opacity");
var zIndex = computedStyle.get("z-index");

After execution, the value of opacity is 1 (opacity is range-restricted), and the value of zIndex is 15 (z-index is rounded to an integer value).

Note: "Numeric values" which incorporate variable references will instead be represented as CSSUnparsedValue objects, and keywords as CSSKeywordValue objects.

Any place that accepts a CSSNumericValue also accepts a raw double, by using the following typedef and algorithm:

typedef (double or CSSNumericValue) CSSNumberish;
To rectify a numberish value num, perform the following steps:
  1. If num is a CSSNumericValue, return num.

  2. If num is a double, return a new CSSUnitValue with its value internal slot set to num and its unit internal slot set to "number".

4.3.1. Common Numeric Operations, and the CSSNumericValue Superclass

All numeric CSS values (<number>s, <percentage>s, and <dimension>s) are represented by subclasses of the CSSNumericValue interface.

enum CSSNumericBaseType {
    "length",
    "angle",
    "time",
    "frequency",
    "resolution",
    "flex",
    "percent",
};

dictionary CSSNumericType {
    long length;
    long angle;
    long time;
    long frequency;
    long resolution;
    long flex;
    long percent;
    CSSNumericBaseType percentHint;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSNumericValue : CSSStyleValue {
    CSSNumericValue add(CSSNumberish... values);
    CSSNumericValue sub(CSSNumberish... values);
    CSSNumericValue mul(CSSNumberish... values);
    CSSNumericValue div(CSSNumberish... values);
    CSSNumericValue min(CSSNumberish... values);
    CSSNumericValue max(CSSNumberish... values);

    boolean equals(CSSNumberish... value);

    CSSUnitValue to(USVString unit);
    CSSMathSum toSum(USVString... units);
    CSSNumericType type();

    [Exposed=Window] static CSSNumericValue parse(USVString cssText);
};

The methods on the CSSNumericValue superclass represent operations that all numeric values can perform.

The following are the arithmetic operations you can perform on dimensions:

The add(...values) method, when called on a CSSNumericValue this, must perform the following steps:
  1. Replace each item of values with the result of rectifying a numberish value for the item.

  2. If this is a CSSMathSum object, prepend the items in this’s values internal slot to values. Otherwise, prepend this to values.

  3. If all of the items in values are CSSUnitValues and have the same unit, return a new CSSUnitValue whose unit internal slot is set to this’s unit internal slot, and value internal slot is set to the sum of the value internal slots of the items in values. This addition must be done "left to right" - if values is « 1, 2, 3, 4 », the result must be (((1 + 2) + 3) + 4). (This detail is necessary to ensure interoperability in the presence of floating-point arithmetic.)

  4. Let type be the result of adding the types of every item in values. If type is failure, throw a TypeError.

  5. Return a new CSSMathSum object whose values internal slot is set to values.

The sub(...values) method, when called on a CSSNumericValue this, must perform the following steps:
  1. Replace each item of values with the result of rectifying a numberish value for the item, then negating the value.

  2. Return the result of calling the add() internal algorithm with this and values.

To negate a CSSNumericValue this:
  1. If this is a CSSMathNegate object, return this’s value internal slot.

  2. If this is a CSSUnitValue object, return a new CSSUnitValue with the same unit internal slot as this, and a value internal slot set to the negation of this’s.

  3. Otherwise, return a new CSSMathNegate object whose value internal slot is set to this.

The mul(...values) method, when called on a CSSNumericValue this, must perform the following steps:
  1. Replace each item of values with the result of rectifying a numberish value for the item.

  2. If this is a CSSMathProduct object, prepend the items in this’s values internal slot to values. Otherwise, prepend this to values.

  3. If all of the items in values are CSSUnitValues with unit internal slot set to "number", return a new CSSUnitValue whose unit internal slot is set to "number", and value internal slot is set to the product of the value internal slots of the items in values.

    This multiplication must be done "left to right" - if values is « 1, 2, 3, 4 », the result must be (((1 × 2) × 3) × 4). (This detail is necessary to ensure interoperability in the presence of floating-point arithmetic.)

  4. If all of the items in values are CSSUnitValues with unit internal slot set to "number" except one which is set to unit, return a new CSSUnitValue whose unit internal slot is set to unit, and value internal slot is set to the product of the value internal slots of the items in values.

    This multiplication must be done "left to right" - if values is « 1, 2, 3, 4 », the result must be (((1 × 2) × 3) × 4).

  5. Let type be the result of multiplying the types of every item in values. If type is failure, throw a TypeError.

  6. Return a new CSSMathProduct object whose values internal slot is set to values.

The div(...values) method, when called on a CSSNumericValue this, must perform the following steps:
  1. Replace each item of values with the result of rectifying a numberish value for the item, then inverting the value.

  2. Return the result of calling the mul() internal algorithm with this and values.

To invert a CSSNumericValue this:
  1. If this is a CSSMathInvert object, return this’s value internal slot.

  2. If this is a CSSUnitValue object with unit internal slot set to "number":

    1. If this’s value internal slot is set to 0 or -0, throw a RangeError.

    2. Else return a new CSSUnitValue with the unit internal slot set to "number", and a value internal slot set to 1 divided by this’s {CSSUnitValue/value}} internal slot.

  3. Otherwise, return a new CSSMathInvert object whose value internal slot is set to this.

The min(...values) method, when called on a CSSNumericValue this, must perform the following steps:
  1. Replace each item of values with the result of rectifying a numberish value for the item.

  2. If this is a CSSMathMin object, prepend the items in this’s values internal slot to values. Otherwise, prepend this to values.

  3. If all of the items in values are CSSUnitValues and have the same unit, return a new CSSUnitValue whose unit internal slot is set to this’s unit internal slot, and value internal slot is set to the minimum of the value internal slots of the items in values.

  4. Let type be the result of adding the types of every item in values. If type is failure, throw a TypeError.

  5. Return a new CSSMathMin object whose values internal slot is set to values.

The max(...values) method, when called on a CSSNumericValue this, must perform the following steps:
  1. Replace each item of values with the result of rectifying a numberish value for the item.

  2. If this is a CSSMathMax object, prepend the items in this’s values internal slot to values. Otherwise, prepend this to values.

  3. If all of the items in values are CSSUnitValues and have the same unit, return a new CSSUnitValue whose unit internal slot is set to this’s unit internal slot, and value internal slot is set to the maximum of the value internal slots of the items in values.

  4. Let type be the result of adding the types of every item in values. If type is failure, throw a TypeError.

  5. Return a new CSSMathMax object whose values internal slot is set to values.

The equals(...values) method, when called on a CSSNumericValue this, must perform the following steps:
  1. Replace each item of values with the result of rectifying a numberish value for the item.

  2. For each item in values, if the item is not an equal numeric value to this, return false.

  3. Return true.

This notion of equality is purposely fairly exacting; all the values must be the exact same type and value, in the same order. For example, CSSMathSum(CSS.px(1), CSS.px(2)) is not equal to CSSMathSum(CSS.px(2), CSS.px(1)).

This precise notion is used because it allows structural equality to be tested for very quickly; if we were to use a slower and more forgiving notion of equality, such as allowing the arguments to match in any order, we’d probably want to go all the way and perform other simplifications, like considering 96px to be equal to 1in; this looser notion of equality might be added in the future.

To determine whether two CSSNumericValues value1 and value2 are equal numeric values, perform the following steps:
  1. If value1 and value2 are not members of the same interface, return false.

  2. If value1 and value2 are both CSSUnitValues, return true if they have equal unit and value internal slots, or false otherwise.

  3. If value1 and value2 are both CSSMathSums, CSSMathProducts, CSSMathMins, or CSSMathMaxs:

    1. If value1’s values and value2s values internal slots have different sizes, return false.

    2. If any item in value1’s values internal slot is not an equal numeric value to the item in value2’s values internal slot at the same index, return false.

    3. Return true.

  4. Assert: value1 and value2 are both CSSMathNegates or CSSMathInverts.

  5. Return whether value1’s value and value2’s value are equal numeric values.

The to(unit) method converts an existing CSSNumericValue this into another one with the specified unit, if possible. When called, it must perform the following steps:
  1. Let type be the result of creating a type from unit. If type is failure, throw a SyntaxError.

  2. Let sum be the result of creating a sum value from this. If sum is failure, throw a TypeError.

  3. If sum has more than one item, throw a TypeError. Otherwise, let item be the result of creating a CSSUnitValue from the sole item in sum, then converting it to unit. If item is failure, throw a TypeError.

  4. Return item.

When asked to create a CSSUnitValue from a sum value item item, perform the following steps:
  1. If item has more than one entry in its unit map, return failure.

  2. If item has no entries in its unit map, return a new CSSUnitValue whose unit internal slot is set to "number", and whose value internal slot is set to item’s value.

  3. Otherwise, item has a single entry in its unit map. If that entry’s value is anything other than 1, return failure.

  4. Otherwise, return a new CSSUnitValue whose unit internal slot is set to that entry’s key, and whose value internal slot is set to item’s value.

The toSum(...units) method converts an existing CSSNumericValue this into a CSSMathSum of only CSSUnitValues with the specified units, if possible. (It’s like to(), but allows the result to have multiple units in it.) If called without any units, it just simplifies this into a minimal sum of CSSUnitValues.

When called, it must perform the following steps:

  1. For each unit in units, if the result of creating a type from unit is failure, throw a SyntaxError.

  2. Let sum be the result of creating a sum value from this. If sum is failure, throw a TypeError.

  3. Let values be the result of creating a CSSUnitValue for each item in sum. If any item of values is failure, throw a TypeError.

  4. If units is empty, sort values in code point order according to the unit internal slot of its items, then return a new CSSMathSum object whose values internal slot is set to values.

  5. Otherwise, let result initially be an empty list. For each unit in units:

    1. Let temp initially be a new CSSUnitValue whose unit internal slot is set to unit and whose value internal slot is set to 0.

    2. For each value in values:

      1. Let value unit be value’s unit internal slot.

      2. If value unit is a compatible unit with unit, then:

        1. Convert value to unit.

        2. Increment temp’s value internal slot by the value of value’s value internal slot.

        3. Remove value from values.

    3. Append temp to result.

  6. If values is not empty, throw a TypeError. this had units that you didn’t ask for.

  7. Return a new CSSMathSum object whose values internal slot is set to result.

The type() method returns a representation of the type of this.

When called, it must perform the following steps:

  1. Let result be a new CSSNumericType.

  2. For each baseTypepower in the type of this,

    1. If power is not 0, set result[baseType] to power.

  3. If the percent hint of this is not null,

    1. Set percentHint to the percent hint of this.

  4. Return result.

A sum value is an abstract representation of a CSSNumericValue as a sum of numbers with (possibly complex) units. Not all CSSNumericValues can be expressed as a sum value.

A sum value is a list. Each entry in the list is a tuple of a value, which is a number, and a unit map, which is a map of units (strings) to powers (integers).

Here are a few examples of CSS values, and their equivalent sum values:
  • 1px becomes «(1, «["px" → 1]»)»

  • calc(1px + 1in) becomes «(97, «["px" → 1]»)» (because in and px are compatible units, and px is the canonical unit for them)

  • calc(1px + 2em) becomes «(1, «["px" → 1]»), (2, «["em" → 1]»)»

  • calc(1px + 2%) becomes «(1, «["px" → 1]»), (2, «["percent" → 1]»)» (percentages are allowed to add to other units, but aren’t resolved into another unit, like they are in a type)

  • calc(1px * 2em) becomes «(2, «["em" → 1, "px" → 1]»)»

  • calc(1px + 1deg) can’t be represented as a sum value because it’s an invalid computation

  • calc(1px * 2deg) becomes «(2, «["deg" → 1, "px" → 1]»)»

To create a sum value from a CSSNumericValue this, the steps differ based on this’s class:

CSSUnitValue
  1. Let unit be the value of this’s unit internal slot, and value be the value of this’s value internal slot.

  2. If unit is a member of a set of compatible units, and is not the set’s canonical unit, multiply value by the conversion ratio between unit and the canonical unit, and change unit to the canonical unit.

  3. If unit is "number", return «(value, «[ ]»)».

  4. Otherwise, return «(value, «[unit → 1]»)».

CSSMathSum
  1. Let values initially be an empty list.

  2. For each item in this’s values internal slot:

    1. Let value be the result of creating a sum value from item. If value is failure, return failure.

    2. For each subvalue of value:

      1. If values already contains an item with the same unit map as subvalue, increment that item’s value by the value of subvalue.

      2. Otherwise, append subvalue to values.

  3. Create a type from the unit map of each item of values, and add all the types together. If the result is failure, return failure.

  4. Return values.

CSSMathNegate
  1. Let values be the result of creating a sum value from this’s value internal slot.

  2. If values is failure, return failure.

  3. Negate the value of each item of values.

  4. Return values.

CSSMathProduct
  1. Let values initially be the sum value «(1, «[ ]»)». (I.e. what you’d get from 1.)

  2. For each item in this’s values internal slot:

    1. Let new values be the result of creating a sum value from item. Let temp initially be an empty list.

    2. If new values is failure, return failure.

    3. For each item1 in values:

      1. For each item2 in new values:

        1. Let item be a tuple with its value set to the product of the values of item1 and item2, and its unit map set to the product of the unit maps of item1 and item2, with all entries with a zero value removed.

        2. Append item to temp.

    4. Set values to temp.

  3. Return values.

CSSMathInvert
  1. Let values be the result of creating a sum value from this’s value internal slot.

  2. If values is failure, return failure.

  3. If the length of values is more than one, return failure.

  4. Invert (find the reciprocal of) the value of the item in values, and negate the value of each entry in its unit map.

  5. Return values.

CSSMathMin
  1. Let args be the result of creating a sum value for each item in this’s values internal slot.

  2. If any item of args is failure, or has a length greater than one, return failure.

  3. If not all of the unit maps among the items of args are identical, return failure.

  4. Return the item of args whose sole item has the smallest value.

CSSMathMax
  1. Let args be the result of creating a sum value for each item in this’s values internal slot.

  2. If any item of args is failure, or has a length greater than one, return failure.

  3. If not all of the unit maps among the items of args are identical, return failure.

  4. Return the item of args whose sole item has the largest value.

To create a type from a unit map unit map:
  1. Let types be an initially empty list.

  2. For each unitpower in unit map:

    1. Let type be the result of creating a type from unit.

    2. Set type’s sole value to power.

    3. Append type to types.

  3. Return the result of multiplying all the items of types.

The product of two unit maps units1 and units2 is the result given by the following steps:
  1. Let result be a copy of units1.

  2. For each unitpower in units2:

    1. If result[unit] exists, increment result[unit] by power.

    2. Otherwise, set result[unit] to power.

  3. Return result.

The parse() method allows a CSSNumericValue to be constructed directly from a string containing CSS. Note that this is a static method, existing directly on the CSSNumericValue interface object, rather than on CSSNumericValue instances.

The parse(cssText) method, when called, must perform the following steps:
  1. Parse a component value from cssText and let result be the result. If result is a syntax error, throw a SyntaxError and abort this algorithm.

  2. If result is not a <number-token>, <percentage-token>, <dimension-token>, or a math function, throw a SyntaxError and abort this algorithm.

  3. Reify a numeric value result, and return the result.

4.3.2. Numeric Value Typing

Each CSSNumericValue has an associated type, which is a map of base types to integers, and an associated percent hint. The base types are "length", "angle", "time", "frequency", "resolution", "flex", and "percent". The ordering of a type’s entries always matches this base type ordering. The percent hint is either null or a base type other than "percent".

Note: As new unit types are added to CSS, they’ll be added to this list of base types, and to the CSS math functions.

To create a type from a string unit, follow the appropriate branch of the following:
unit is "number"

Return «[ ]» (empty map)

unit is "percent"

Return «[ "percent" → 1 ]»

unit is a <length> unit

Return «[ "length" → 1 ]»

unit is an <angle> unit

Return «[ "angle" → 1 ]»

unit is a <time> unit

Return «[ "time" → 1 ]»

unit is a <frequency> unit

Return «[ "frequency" → 1 ]»

unit is a <resolution> unit

Return «[ "resolution" → 1 ]»

unit is a <flex> unit

Return «[ "flex" → 1 ]»

anything else

Return failure.

In all cases, the associated percent hint is null.

To add two types type1 and type2, perform the following steps:
  1. Replace type1 with a fresh copy of type1, and type2 with a fresh copy of type2. Let finalType be a new type with an initially empty ordered map and an initially null percent hint.

  2. If both type1 and type2 have non-null percent hints with different values

    The types can’t be added. Return failure.

    If type1 has a non-null percent hint hint and type2 doesn’t

    Apply the percent hint hint to type2.

    Vice versa if type2 has a non-null percent hint and type1 doesn’t.

    Otherwise

    Continue to the next step.

  3. If all the entries of type1 with non-zero values are contained in type2 with the same value, and vice-versa

    Copy all of type1’s entries to finalType, and then copy all of type2’s entries to finalType that finalType doesn’t already contain. Set finalType’s percent hint to type1’s percent hint. Return finalType.

    If type1 and/or type2 contain "percent" with a non-zero value, and type1 and/or type2 contain a key other than "percent" with a non-zero value

    For each base type other than "percent" hint:

    1. Provisionally apply the percent hint hint to both type1 and type2.

    2. If, afterwards, all the entries of type1 with non-zero values are contained in type2 with the same value, and vice versa, then copy all of type1’s entries to finalType, and then copy all of type2’s entries to finalType that finalType doesn’t already contain. Set finalType’s percent hint to hint. Return finalType.

    3. Otherwise, revert type1 and type2 to their state at the start of this loop.

    If the loop finishes without returning finalType, then the types can’t be added. Return failure.

    Note: You can shortcut this in some cases by just checking the sum of all the values of type1 vs type2. If the sums are different, the types can’t be added.

    Otherwise

    The types can’t be added. Return failure.

To apply the percent hint hint to a type, perform the following steps:
  1. If type doesn’t contain hint, set type[hint] to 0.

  2. If type contains "percent", add type["percent"] to type[hint], then set type["percent"] to 0.

  3. Set type’s percent hint to hint.

To multiply two types type1 and type2, perform the following steps:
  1. Replace type1 with a fresh copy of type1, and type2 with a fresh copy of type2. Let finalType be a new type with an initially empty ordered map and an initially null percent hint.

  2. If both type1 and type2 have non-null percent hints with different values, the types can’t be multiplied. Return failure.

  3. If type1 has a non-null percent hint hint and type2 doesn’t, apply the percent hint hint to type2.

    Vice versa if type2 has a non-null percent hint and type1 doesn’t.

  4. Copy all of type1’s entries to finalType, then for each baseTypepower of type2:

    1. If finalType[baseType] exists, increment its value by power.

    2. Otherwise, set finalType[baseType] to power.

    Set finalType’s percent hint to type1’s percent hint.

  5. Return finalType.

To invert a type type, perform the following steps:
  1. Let result be a new type with an initially empty ordered map and an initially null percent hint

  2. For each unitexponent of type, set result[unit] to (-1 * exponent).

  3. Return result.

A type is said to match a CSS production in some circumstances:

Many specifications use ''[ <length> | <percentage> ]'' instead of ''<length-percentage>'' in their grammar, and specify in prose that the <length> and <percentage> can be combined. For the purposes of matching, these cases should be treated as <length-percentage>. Similarly for <angle-percentage>, etc.

Note: Types form a semi-group under both addition and a monoid under multiplication (with the multiplicative identity being «[ ]» with a null percent hint), meaning that they’re associative and commutative. Thus the spec can, for example, add an unbounded number of types together unambiguously, rather than having to manually add them pair-wise.

4.3.3. Value + Unit: CSSUnitValue objects

Numeric values that can be expressed as a single unit (or a naked number or percentage) are represented as CSSUnitValues.

For example, the value 5px in a stylesheet will be represented by a CSSUnitValue with its value attribute set to 5 and its unit attribute set to "px".

Similarly, the value 10 in a stylesheet will be represented by a CSSUnitValue with its value attribute set to 10 and its unit attribute set to "number".

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(double value, USVString unit)]
interface CSSUnitValue : CSSNumericValue {
    attribute double value;
    readonly attribute USVString unit;
};
The CSSUnitValue(value, unit) constructor must, when called, perform the following steps:
  1. If creating a type from unit returns failure, throw a TypeError and abort this algorithm.

  2. Return a new CSSUnitValue with its value internal slot set to value and its unit set to unit.

The type of a CSSUnitValue is the result of creating a type from its unit internal slot.
To create a CSSUnitValue from a pair (num, unit), return a new CSSUnitValue object with its value internal slot set to num, and its unit internal slot set to unit.
For example, creating a CSSUnitValue from (5, "px") creates an object equivalent to new CSSUnitValue(5, "px").

Note: This is a spec-internal algorithm, meant simply to make it easier to create unit values in algorithms when needed.

To convert a CSSUnitValue this to a unit unit, perform the following steps:
  1. Let old unit be the value of this’s unit internal slot, and old value be the value of this’s value internal slot.

  2. If old unit and unit are not compatible units, return failure.

  3. Return a new CSSUnitValue whose unit internal slot is set to unit, and whose value internal slot is set to old value multiplied by the conversation ratio between old unit and unit.

4.3.4. Complex Numeric Values: CSSMathValue objects

Numeric values that are more complicated than a single value+unit are represented by a tree of CSSMathValue subclasses, eventually terminating in CSSUnitValue objects at the leaf nodes. The calc(), min(), and max() functions in CSS are represented in this way.

For example, the CSS value calc(1em + 5px) will be represented by a CSSMathSum like CSSMathSum(CSS.em(1), CSS.px(5)).

A more complex expression, like calc(1em + 5px * 2), will be represented by a nested structure like CSSMathSum(CSS.em(1), CSSMathProduct(CSS.px(5), 2)).

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathValue : CSSNumericValue {
    readonly attribute CSSMathOperator operator;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish... args)]
interface CSSMathSum : CSSMathValue {
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish... args)]
interface CSSMathProduct : CSSMathValue {
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish arg)]
interface CSSMathNegate : CSSMathValue {
    readonly attribute CSSNumericValue value;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish arg)]
interface CSSMathInvert : CSSMathValue {
    readonly attribute CSSNumericValue value;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish... args)]
interface CSSMathMin : CSSMathValue {
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish... args)]
interface CSSMathMax : CSSMathValue {
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish min, CSSNumberish val, CSSNumberish max)]
interface CSSMathClamp : CSSMathValue {
    readonly attribute CSSNumericValue min;
    readonly attribute CSSNumericValue val;
    readonly attribute CSSNumericValue max;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSNumericArray {
    iterable<CSSNumericValue>;
    readonly attribute unsigned long length;
    getter CSSNumericValue (unsigned long index);
};

enum CSSMathOperator {
    "sum",
    "product",
    "negate",
    "invert",
    "min",
    "max",
    "clamp",
};

Note: CSSMathValue, being a pure superclass, cannot be directly constructed. It exists solely to host the common attributes of all the "math" operations.

The operator attribute of a CSSMathValue this must, on getting, return the following string, depending on the interface of this:
CSSMathSum

"sum"

CSSMathProduct

"product"

CSSMathMin

"min"

CSSMathMax

"max"

CSSMathClamp

"clamp"

CSSMathNegate

"negate"

CSSMathInvert

"invert"

Note: These are all instances of the CSSMathOperator enum.

The CSSMathSum(...args) constructor must, when called, perform the following steps:
  1. Replace each item of args with the result of rectifying a numberish value for the item.

  2. If args is empty, throw a SyntaxError.

  3. Let type be the result of adding the types of all the items of args. If type is failure, throw a TypeError.

  4. Return a new CSSMathSum whose values internal slot is set to args.

The CSSMathMin(...args) and CSSMathMax(...args) constructors are defined identically to the above, except that in the last step they return a new CSSMathMin or CSSMathMax object, respectively.

The CSSMathProduct(...args) constructor is defined identically to the above, except that in step 3 it multiplies the types instead of adding, and in the last step it returns a CSSMathProduct.

The CSSMathClamp(min, val, max) constructor must, when called, perform the following steps:
  1. Replace min, val, and max with the result of rectifying a numberish value for each.

  2. Let type be the result of adding the types of min, val, and max. If type is failure, throw a TypeError.

  3. Return a new CSSMathClamp whose min, val, and max internal slots are set to min, val, and max, respectively.

The CSSMathNegate(arg) constructor must, when called, perform the following steps:
  1. Replace arg with the result of rectifying a numberish value for arg.

  2. Return a new CSSMathNegate whose value internal slot is set to arg.

The CSSMathInvert(arg) constructor is defined identically to the above, except that in the last step it returns a new CSSMathInvert object.

The type of a CSSMathValue depends on its class:
CSSMathSum
CSSMathMin
CSSMathMax

The type is the result of adding the types of each of the items in its values internal slot.

CSSMathClamp

The type is the result of adding the types of the min, val, and max internal slots.

CSSMathProduct

The type is the result of multiplying the types of each of the items in its values internal slot.

CSSMathNegate

The type is the same as the type of its value internal slot.

CSSMathInvert

The type is the same as the type of its value internal slot, but with all values negated.

The length attribute of CSSNumericArray indicates how many CSSNumericValues are contained within the CSSNumericArray.

The indexed getter of CSSNumericArray retrieves the CSSNumericValue at the provided index.

4.3.5. Numeric Factory Functions

The following factory functions can be used to create new numeric values much less verbosely than using the constructors directly.

partial namespace CSS {
    CSSUnitValue number(double value);
    CSSUnitValue percent(double value);

    // <length>
    CSSUnitValue em(double value);
    CSSUnitValue ex(double value);
    CSSUnitValue ch(double value);
    CSSUnitValue ic(double value);
    CSSUnitValue rem(double value);
    CSSUnitValue lh(double value);
    CSSUnitValue rlh(double value);
    CSSUnitValue vw(double value);
    CSSUnitValue vh(double value);
    CSSUnitValue vi(double value);
    CSSUnitValue vb(double value);
    CSSUnitValue vmin(double value);
    CSSUnitValue vmax(double value);
    CSSUnitValue cm(double value);
    CSSUnitValue mm(double value);
    CSSUnitValue Q(double value);
    CSSUnitValue in(double value);
    CSSUnitValue pt(double value);
    CSSUnitValue pc(double value);
    CSSUnitValue px(double value);

    // <angle>
    CSSUnitValue deg(double value);
    CSSUnitValue grad(double value);
    CSSUnitValue rad(double value);
    CSSUnitValue turn(double value);

    // <time>
    CSSUnitValue s(double value);
    CSSUnitValue ms(double value);

    // <frequency>
    CSSUnitValue Hz(double value);
    CSSUnitValue kHz(double value);

    // <resolution>
    CSSUnitValue dpi(double value);
    CSSUnitValue dpcm(double value);
    CSSUnitValue dppx(double value);

    // <flex>
    CSSUnitValue fr(double value);
};
All of the above methods must, when called with a double value, return a new CSSUnitValue whose value internal slot is set to value and whose unit internal slot is set to the name of the method as defined here.

Note: The unit used does not depend on the current name of the function, if it’s stored in another variable; let foo = CSS.px; let val = foo(5); does not return a {value: 5, unit: "foo"} CSSUnitValue. The above talk about names is just a shorthand to avoid defining the unit individually for all ~20 functions.

4.4. CSSTransformValue objects

CSSTransformValue objects represent <transform-list> values, used by the transform property. They "contain" one or more CSSTransformComponents, which represent individual <transform-function> values.

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(sequence<CSSTransformComponent> transforms)]
interface CSSTransformValue : CSSStyleValue {
    iterable<CSSTransformComponent>;
    readonly attribute unsigned long length;
    getter CSSTransformComponent (unsigned long index);
    setter CSSTransformComponent (unsigned long index, CSSTransformComponent val);

    readonly attribute boolean is2D;
    DOMMatrix toMatrix();
};

A CSSTransformValue’s values to iterate over is a list of CSSTransformComponents.

The CSSTransformValue(transforms) constructor must, when called, perform the following steps:
  1. If transforms is empty, throw a TypeError.

  2. Return a new CSSTransformValue whose values to iterate over is transforms.

The is2D attribute of a CSSTransformValue this must, on getting, return true if, for each func in this’s values to iterate over, the func’s is2D attribute would return true; otherwise, the attribute returns false.
The toMatrix() method of a CSSTransformValue this must, when called, perform the following steps:
  1. Let matrix be a new DOMMatrix, initialized to the identity matrix, with its is2D internal slot set to true.

  2. For each func in this’s values to iterate over:

    1. Let funcMatrix be the DOMMatrix returned by calling toMatrix() on func.

    2. Set matrix to the result of multiplying matrix and the matrix represented by funcMatrix.

  3. Return matrix.

The length attribute indicates how many transform components are contained within the CSSTransformValue.

They have a [[values]] internal slot, which is a list of CSSTransformComponent objects. This list is the object’s values to iterate over.

The supported property indexes of a CSSTransformValue this are the integers greater than or equal to 0, and less than the size of this’s [[values]] internal slot.

To determine the value of an indexed property of a CSSTransformValue this and an index n, let values be this’s [[values]] internal slot, and return values[n].

To set the value of an existing indexed property of a CSSTransformValue this, an index n, and a value new value, let values be this’s [[values]] internal slot, and set values[n] to new value.

To set the value of a new indexed property of a CSSTransformValue this, an index n, and a value new value, let values be this’s [[values]] internal slot. If n is not equal to the size of values, throw a RangeError. Otherwise, append new value to values.

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSTransformComponent {
    stringifier;
    attribute boolean is2D;
    DOMMatrix toMatrix();
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumericValue x, CSSNumericValue y, optional CSSNumericValue z)]
interface CSSTranslate : CSSTransformComponent {
    attribute CSSNumericValue x;
    attribute CSSNumericValue y;
    attribute CSSNumericValue z;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumericValue angle),
 Constructor(CSSNumberish x, CSSNumberish y, CSSNumberish z, CSSNumericValue angle)]
interface CSSRotate : CSSTransformComponent {
    attribute CSSNumberish x;
    attribute CSSNumberish y;
    attribute CSSNumberish z;
    attribute CSSNumericValue angle;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumberish x, CSSNumberish y, optional CSSNumberish z)]
interface CSSScale : CSSTransformComponent {
    attribute CSSNumberish x;
    attribute CSSNumberish y;
    attribute CSSNumberish z;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumericValue ax, CSSNumericValue ay)]
interface CSSSkew : CSSTransformComponent {
    attribute CSSNumericValue ax;
    attribute CSSNumericValue ay;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumericValue ax)]
interface CSSSkewX : CSSTransformComponent {
    attribute CSSNumericValue ax;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumericValue ay)]
interface CSSSkewY : CSSTransformComponent {
    attribute CSSNumericValue ay;
};

/* Note that skew(x,y) is *not* the same as skewX(x) skewY(y),
   thus the separate interfaces for all three. */

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
 Constructor(CSSNumericValue length)]
interface CSSPerspective : CSSTransformComponent {
    attribute CSSNumericValue