CSS Typed OM Level 1

W3C Working Draft,

This version:
https://www.w3.org/TR/2017/WD-css-typed-om-1-20170801/
Latest published version:
https://www.w3.org/TR/css-typed-om-1/
Editor's Draft:
https://drafts.css-houdini.org/css-typed-om-1/
Previous Versions:
Feedback:
public-houdini@w3.org with subject line “[css-typed-om] … message topic …” (archives)
Issue Tracking:
GitHub
Inline In Spec
Editors:
Tab Atkins-Bittner (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 section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than 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 published by the CSS Working Group and the Technical Architecture Group.

This document was produced by groups operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures (CSS) and a public list of any patent disclosures (Technical Architecture Group) made in connection with the deliverables of each group; these pages also include 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 March 2017 W3C Process Document.

1. Introduction

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.

The API exposed by this specification is designed for performance rather than ergonomics. Some particular considerations:

2. CSSStyleValue objects

interface CSSStyleValue {
    stringifier;
    static CSSStyleValue? parse(DOMString property, DOMString cssText);
    static sequence<CSSStyleValue>? parseAll(DOMString property, DOMString cssText);
};

CSSStyleValue objects are the base class of all CSS Values accessible via the Typed OM API. Values that can’t yet be directly supported by a CSSStyleValue subclass are also represented as CSSStyleValue objects.

The stringification behavior of CSSStyleValue objects is to return a normalized representation (see §5 CSSStyleValue normalization) of the value the CSSStyleValue object represents.

The parse(DOMString property, DOMString cssText), when invoked, must parse a CSSStyleValue with property property, cssText cssText, and parseMultiple set to false.

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

To parse a CSSStyleValue given a property, cssText, and a parseMultiple flag, run these steps:
  1. Attempt to parse property as an <ident>. If this fails, throw a SyntaxError and exit this algorithm. Otherwise, let property be the parsed result. If property does not start with two dashes (U+002D HYPHEN), let property be property ASCII lowercased.

  2. If property is not a supported property name, throw a TypeError and exit this algorithm.

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

  4. If property is a list-valued property, and parseMultiple is true, subdivide value into a list of CSSStyleValue objects, each representing one list-valued property iteration, and let value be the result. Return value as a sequence of CSSStyleValue objects.

  5. If property is a list-valued property, and parseMultiple is false subdivide value into a list of CSSStyleValue objects, each representing one list-valued property iteration, and let value be the first entry in this list.

  6. return a CSSStyleValue representing value.

w3c/css-houdini-drafts/305When doing CSSStyleValue.parse(), what should throw vs return null?

3. The StylePropertyMap

interface StylePropertyMapReadOnly {
    CSSStyleValue? get(DOMString property);
    sequence<CSSStyleValue> getAll(DOMString property);
    boolean has(DOMString property);
    iterable<DOMString, (CSSStyleValue or sequence<CSSStyleValue>)>;
    sequence<DOMString> getProperties();
    stringifier;
};

callback UpdateFunction = CSSStyleValue (CSSStyleValue oldValue);

interface StylePropertyMap : StylePropertyMapReadOnly {
    void append(DOMString property, (CSSStyleValue or DOMString)... values);
    void delete(DOMString property);
    void set(DOMString property, (CSSStyleValue or DOMString)... values);
    void update(DOMString property, UpdateFunction updateFunction);
};

A StylePropertyMapReadOnly object has an associated property model, which is a list of property - sequence<CSSStyleValue> pairs. This list is initialized differently depending on where the CSSStyleValue is used (see §3.1 Computed StylePropertyMapReadOnly objects, §3.2 Declared StylePropertyMap objects, and §3.3 Inline StylePropertyMap objects).

The sequence of CSSStyleValues associated with a property do not represent multiple successive definitions of that property’s value. Instead, sequences represent values associated with list-valued properties.

This approach allows single-valued properties to become list-valued in the future without breaking code that relies on calling get() and/or set() for those properties.

The append(DOMString property, (CSSStyleValue or DOMString)... values) method, when invoked, must append to a StylePropertyMap with property property and values values.

To append to a StylePropertyMap given a property and a list of values, run these steps:
  1. If property does not start with two dashes (U+002D HYPHEN), let property be property ASCII lowercased.

  2. If property is not a supported property name, throw a TypeError and exit this algorithm.

  3. If property is not a list-valued property, throw a TypeError and exit this algorithm.

  4. If StylePropertyMap’s property model contains an entry for property, let entry be that entry. Otherwise, create a new entry for property with an empty list, add it to the property model, and let entry be the newly-created entry.

  5. Let values to append be the empty list.

  6. For each value in values if the algorithm that coerces value into an appropriate type for a given property does not throw an error, append the returned object to values to append.

  7. Append values to append to the end of entry’s list.

This section describes the algorithm that coerces value into an appropriate type for a given property, or fails and throws a TypeError:
If value is a CSSStyleValue,

If value does not match the grammar of a list-valued property iteration of property, throw a TypeError and exit this algorithm. Otherwise, return the value.

If value is a DOMString,

Parse a CSSStyleValue with property property and value value and return the resulting value. If the result is null, throw a TypeError and exit this algorithm. Otherwise, append each list-valued property iteration in the result to a values to append object and return values to append.

To get a value from a StylePropertyMap, run these steps:
To set a value on a StylePropertyMap, run these steps:

The update(DOMString property, UpdateFunction updateFunction) method, when invoked, must update a value in a StylePropertyMap with property name property, update function updateFunction, and property map set to the object this method was invoked on .

To update a value in a StylePropertyMap given a property name, update function, and property map, run these steps:
  1. Let old value be the result of running the algorithm to get a value from a StylePropertyMap with property name property name and property map property map.

  2. Let new value be the return value given by invoking the callback update function with a single input of old value.

  3. Run the algorithm to set a value on a StylePropertyMap with property name property name, value new value, and property map property map.

The getProperties() method returns all of the properties listed in the property model. This list of properties is sorted in the following manner:

computed StylePropertyMap, declared StylePropertyMap and inline StylePropertyMap are all live objects: the attributes and methods on these objects must operate on the actual underlying data, not a snapshot of the data.

w3c/css-houdini-drafts/148[css-typed-om] add detailed descriptions of the rest of the methods on StylePropertyMap
w3c/css-houdini-drafts/268[css-typed-om] Stringification behavior
w3c/css-houdini-drafts/276[css-typed-om] Describe how the StylePropertyMaps should interact with custom properties
w3c/css-houdini-drafts/309[css-typed-om] Should StyleMap be case sensitive?
w3c/css-houdini-drafts/310[css-typed-om] Consider using properties in addition to .get/.set

3.1. Computed StylePropertyMapReadOnly objects

partial interface Window {
    StylePropertyMapReadOnly getComputedStyleMap(Element element, optional DOMString? pseudoElt);
};
w3c/css-houdini-drafts/350[all] Move Houdini APIs to `window.CSS`

Computed StylePropertyMap objects represent the computed style of an Element or pseudo element, and are accessed by calling the getComputedStyleMap() method.

When constructed, the property model for computed StylePropertyMap objects is initialized to contain an entry for every valid CSS property supported by the User Agent.

Note: The StylePropertyMap returned by getComputedStyleMap represents computed style, not resolved style. In this regard it provides different values than those in objects returned by getComputedStyle.

3.2. Declared StylePropertyMap objects

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

Declared StylePropertyMap objects represent style property-value pairs embedded in a style rule, and are accessed via the styleMap attribute of CSSStyleRule objects.

When constructed, the property model for declared StylePropertyMap objects is initialized to contain an entry for each property that is paired with at least one valid value inside the CSSStyleRule that the object represents. The value for a given property is the last valid value provided by the CSSStyleRule object.

3.3. Inline StylePropertyMap objects

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

Inline StylePropertyMap objects represent inline style declarations attached directly to Elements. They are accessed via the styleMap attribute of Element objects.

When constructed, the property model for inline StylePropertyMap objects is initialized to contain an entry for each property that is paired with at least one valid value in the string representing the style attribute for the Element that the object is associated with. The value for a given property is the last valid value provided in the string.

4. CSSStyleValue subclasses

4.1. CSSUnparsedValue objects

[Constructor((DOMString or CSSVariableReferenceValue)... members)]
interface CSSUnparsedValue : CSSStyleValue {
    iterable<(DOMString or CSSVariableReferenceValue)>;
    readonly attribute unsigned long length;
    getter (DOMString or CSSVariableReferenceValue) (unsigned long index);
};

interface CSSVariableReferenceValue {
    attribute DOMString variable;
    attribute CSSUnparsedValue? fallback;
};
w3c/css-houdini-drafts/239[css-typed-om]: There's no nice way to represent CSSUnparsedValue as a "list-plus" style object.

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

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

The length attribute indicates how many string fragments and variable references are contained within the CSSUnparsedValue.

The indexed getter retrieves the string fragment or variable reference at the provided index.

4.2. CSSKeywordValue objects

CSSKeywordValue objects represent CSS keywords and other identifiers.

[Constructor(DOMString value)]
interface CSSKeywordValue : CSSStyleValue {
    attribute DOMString value;
    stringifier;
};
The CSSKeywordValue(value) constructor must, when called, perform the following steps:
  1. Return a new CSSKeywordValue with its value internal slot set to value.

The stringification behavior of a CSSKeywordValue this is:
  1. Let token be an <ident-token> with its value set to this’s value internal slot.

  2. Return the serialization of token.

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

typedef (DOMString or CSSKeywordValue) CSSKeywordish;
To rectify a keywordish value val, perform the following steps:
  1. If val is a CSSKeywordValue, return val.

  2. If val is a DOMString, return a new CSSKeywordValue with its value internal slot set to val.

4.3. Numeric Values:

CSSNumericValue objects represent CSS values that are numeric in nature (<number>s, <percentage>s, <dimension>s).

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 or inline StylePropertyMap. Instead, clamping and/or rounding will occur during computation of style.

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

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

var computedStyle = getComputedStyleMap(myElement);
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.

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);

    CSSNumericValue to(DOMString unit);
    CSSMathSum toSum(DOMString... units);
    // ??? type();

    static CSSNumericValue parse(DOMString cssText);
};

Figure out how we want to represent the type of an expression in JS, and define the type() method accordingly.

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.

  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. Let type be the result of multiplying the types of every item in values. If type is failure, throw a TypeError.

  4. 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. 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. Let type be the result of adding the types of every item in values. If type is failure, throw a TypeError.

  4. 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. Let type be the result of adding the types of every item in values. If type is failure, throw a TypeError.

  4. 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. Let thisAndValues be this prepended to values.

  3. If all items of thisAndValues do not belong to the same class return false. Example: all items in thisAndValues must be CSSUnitValue.

  4. If all items in thisAndValues are {{CSSUnitValue}s and have the same unit and value return true.

  5. If all items in thisAndValues are CSSMathValues :

    1. If thisAndValues are all either CSSMathSum, CSSMathProduct, CSSMathMin or CSSMathMax, iterate over the CSSNumericArrays on thisAndValues,

      1. For each CSSNumericValue in each of the CSSNumericArrays go back to step 1 to check that the CSSUnitValues on all items in thisAndValue are equal.

    2. If this and value are both either CSSMathNegate or CSSMathInvert:

      1. Go back to step 1 to check that the CSSUnitValue on all items in thisAndValue are equal.

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.

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

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

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 * 1deg) 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 union 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 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 <calc()>, throw a SyntaxError and abort this algorithm.

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

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.

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

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

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

The unit attribute of a CSSUnitValue this must, on setting a value unit, perform the following steps:
  1. If creating a type from unit returns failure,, throw a TypeError.

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

On reading, it must return the value of this’s unit internal slot.

The type of a CSSUnitValue is the result of creating a type from its unit internal slot.
To create a CSSUnitValue from a string str, return a new CSSUnitValue object with its value internal slot set to the numeric portion of str parsed into a number, and its unit internal slot set to the unit portion of str, or "number" or "percent" if str is a plain number or percent.
For example, creating a CSSUnitValue from 5px creates an object equivalent to new CSSUnitValue(5, "px").
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)).

interface CSSMathValue : CSSNumericValue {
    readonly attribute CSSMathOperator operator;
    readonly attribute DOMString type;
};

[Constructor(CSSNumberish... args)]
interface CSSMathSum : CSSMathValue {
    attribute CSSNumericArray values;
};

[Constructor(CSSNumberish... args)]
interface CSSMathProduct : CSSMathValue {
    attribute CSSNumericArray values;
};

[Constructor(CSSNumberish arg)]
interface CSSMathNegate : CSSMathValue {
    attribute CSSNumericValue value;
};

[Constructor(CSSNumberish arg)]
interface CSSMathInvert : CSSMathValue {
    attribute CSSNumericValue value;
};

[Constructor(CSSNumberish... args)]
interface CSSMathMin : CSSMathValue {
    attribute CSSNumericArray values;
};

[Constructor(CSSNumberish... args)]
interface CSSMathMax : CSSMathValue {
    attribute CSSNumericArray values;
};

interface CSSNumericArray {}; // See issue below

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

CSSNumericArray will be an Array-like restricted to containing CSSNumericValue objects. This is dependent on WebIDL#345 getting resolved properly.

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"

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

CSSMathProduct

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

CSSMathNegate
CSSMathInvert

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

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.

For example, rather than creating a new CSSPositionValue with code like:
let pos = new CSSPositionValue(
    new CSSUnitValue(5, "px"),
    new CSSUnitValue(10, "px"));

One can instead write:

let pos = new CSSPositionValue(CSS.px(5), CSS.px(10));

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.

[Constructor(optional sequence<CSSTransformComponent> transforms)]
interface CSSTransformValue : CSSStyleValue {
    /*arraylike<CSSTransformComponent>;*/
    readonly attribute boolean is2D;
    DOMMatrix toMatrix();
};

Assuming the resolution of WebIDL#345 produces an "arraylike" declaration.

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

The CSSTransformValue(transforms) constructor must, when called, perform the following steps:
  1. 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 4x4 matrix, initially set to the identity matrix. Let is2D be a boolean initially set to true.

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

    1. Let funcMatrix be func’s equivalent 4x4 transform matrix, as defined in CSS Transforms 1 §21 Mathematical Description of Transform Functions.

      As the entries of such a matrix are defined relative to the px unit, if any <length>s in func involved in generating the matrix are not compatible units with px, throw a TypeError.

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

    3. If func’s is2D internal slot is false, set is2D to false.

  3. Return a new DOMMatrix representing matrix, and with its internal is 2D flag set to is2D.

interface CSSTransformComponent {
    stringifier;
    attribute boolean is2D;
};

[Constructor(CSSNumericValue x, CSSNumericValue y, optional CSSNumericValue z)]
interface CSSTranslation : CSSTransformComponent {
    attribute CSSNumericValue x;
    attribute CSSNumericValue y;
    attribute CSSNumericValue z;
};

[Constructor(CSSNumericValue angle),
 Constructor(CSSNumberish x, CSSNumberish y, CSSNumberish z, CSSNumericValue angle)]
interface CSSRotation : CSSTransformComponent {
    attribute CSSNumberish x;
    attribute CSSNumberish y;
    attribute CSSNumberish z;
    attribute CSSNumericValue angle;
};

[Constructor(CSSNumberish x, CSSNumberish y, optional CSSNumberish z)]
interface CSSScale : CSSTransformComponent {
    attribute CSSNumberish x;
    attribute CSSNumberish y;
    attribute CSSNumberish z;
};

[Constructor(CSSNumericValue ax, CSSNumericValue ay)]
interface CSSSkew : CSSTransformComponent {
    attribute CSSNumericValue ax;
    attribute CSSNumericValue ay;
};

[Constructor(CSSNumericValue length)]
interface CSSPerspective : CSSTransformComponent {
    attribute CSSNumericValue length;
};

[Constructor(DOMMatrixReadOnly matrix, optional CSSMatrixComponentOptions options)]
interface CSSMatrixComponent : CSSTransformComponent {
    attribute DOMMatrix matrix;
};

dictionary CSSMatrixComponentOptions {
    boolean is2D;
};
The is2D attribute indicates whether the transform is 2D or 3D. When it’s true, the attributes of the transform that are relevant to 3D transforms (such as the CSSTranslation.z attribute) simply have no effect on the transform they represent.

Note: This affects the serialization of the object, and concepts such as the object’s "equivalent 4x4 matrix".

is2D Design Considerations

For legacy reasons, 2D and 3D transforms are distinct, even if they have identical effects; a translateZ(0px) has observable effects on a page, even tho it’s defined to be an identity transform, as the UA activates some 3D-based optimizations for the element.

There were several possible ways to reflect this—nullable 3D-related attributes, separate 2D and 3D interfaces, etc—but we chose the current design (an author-flippable switch that dictates the behavior) because it allows authors to, in most circumstances, operate on transforms without having to care whether they’re 2D or 3D, but also prevents "accidentally" flipping a 2D transform into becoming 3D.

The CSSTranslation(x, y, z) constructor must, when invoked, perform the following steps:
  1. If x, y, or z (if passed) don’t match <length-percentage>, throw a TypeError.

  2. Let this be a new CSSTranslation object, with its x and y internal slots set to x and y.

  3. If z was passed, set this’s z internal slot to z, and set this’s is2D internal slot to false.

  4. If z was not passed, set this’s z internal slot to new unit value of 0px, and set this’s is2D internal slot to true.

  5. Return this.

The CSSRotation(angle) constructor must, when invoked, perform the following steps:
  1. If angle doesn’t match <angle>, throw a TypeError.

  2. Return a new CSSRotation with its angle internal slot set to angle, its x and y internal slots set to new unit values of 0, its z internal slot set to a new unit value of 1, and its is2D internal slot set to true.

The CSSRotation(x, y, z, angle) constructor must, when invoked, perform the following steps:
  1. If angle doesn’t match <angle>, throw a TypeError.

  2. Let x, y, and z be replaced by the result of rectifying a numberish value.

  3. If x, y, or z don’t match <number>, throw a TypeError.

  4. Return a new CSSRotation with its angle internal slot set to angle, its x, y, z internal slots set to x, y, and z, and its is2D internal slot set to false.

The CSSScale(x, y, z) constructor must, when invoked, perform the following steps:
  1. Let x, y, and z (if passed) be replaced by the result of rectifying a numberish value.

  2. If x, y, or z (if passed) don’t match <number>, throw a TypeError.

  3. Let this be a new CSSScale object, with its x and y internal slots set to x and y.

  4. If z was passed, set this’s z internal slot to z, and set this’s is2D internal slot to false.

  5. If z was not passed, set this’s z internal slot to a new unit value of 1, and set this’s is2D internal slot to true.

  6. Return this.

The CSSSkew(ax, ay) constructor must, when invoked, perform the following steps:
  1. If ax or ay do not match <angle>, throw a TypeError.

  2. Return a new CSSSkew object with its ax and ay internal slots set to ax and ay, and its is2D internal slot set to true.

The is2D attribute of a CSSSkew object must, on setting, do nothing.

Note: skew() functions always represent 2D transforms.

The CSSPerspective(length) constructor must, when invoked, perform the following steps:
  1. If length does not match <length>, throw a TypeError.

  2. Return a new CSSPerspective object with its length internal slot set to length, and its is2D internal slot set to false.

The is2D attribute of a CSSPerspective object must, on setting, do nothing.

Note: perspective() functions always represent 3D transforms.

The CSSMatrixComponent(matrix, options) constructor must, when invoked, perform the following steps:
  1. Let this be a new CSSMatrixComponent object with its matrix internal slot set to matrix.

  2. If options was passed and has a CSSMatrixComponentOptions field, set this’s is2D internal slot to the value of that field.

  3. Otherwise, set this’s is2D internal slot to the value of matrix’s is2D internal slot.

  4. Return this.

Each CSSTransformComponent can correspond to one of a number of underlying transform functions. For example, a CSSTranslation with an x value of 10px and y & z values of 0px could represent any of the following:

When stringified, however, it will always print out either translate(10px, 0px) or translate3d(10px, 0px, 0px), depending on whether its is2D internal slot is true or false, respectively.

4.5. CSSPositionValue objects

CSSPositionValue objects represent <position> values, used by properties such as object-position.

[Constructor(CSSNumericValue x, CSSNumericValue y)]
interface CSSPositionValue : CSSStyleValue {
    attribute CSSNumericValue x;
    attribute CSSNumericValue y;
};

The x attribute expresses the offset from the left edge of the container. y expressions the offset from the top edge of the container.

The CSSPositionValue(x, y) constructor must, when called, perform the following steps:
  1. If x or y doesn’t match <length-percentage>, throw a TypeError.

  2. Otherwise, return a new CSSPositionValue whose x internal slot is set to x, and whose y internal slot is set to y.

The x and y attribute of a CSSPositionValue this must, on setting a value value, perform the following steps:
  1. If value doesn’t match <length-percentage>, throw a TypeError.

  2. Otherwise, set this’s x or y internal slot, as appropriate, to value.

On reading, the attributes must return the value of the x or y internal slot, as appropriate.

<position> values accept a complicated combination of keywords and values, but in the Typed OM are always simplified to just two offsets. For example, the following style sheet:
.example {
    object-position: center bottom 10px;
}

Will produce the following behavior:

let map = document.querySelector('.example').styleMap;

map.get('object-position').x;
// CSS.percent(50)

map.get('object-position').y;
// CSSMathSum(CSS.percent(100), CSS.px(-10))

4.6. CSSResourceValue objects

enum CSSResourceState {"unloaded", "loading", "loaded", "error"};

interface CSSResourceValue : CSSStyleValue {
    readonly attribute CSSResourceState state;
};

CSSResourceValue objects represent CSS values that may require an asynchronous network fetch before being usable.

A CSSResourceValue is in one of the following states, as reflected in the value of the state attribute:

"unloaded"

The resource is not ready and is not actively being fetched

"loading"

The resource is not ready, but is in the process of being fetched

"loaded"

The resource is ready for rendering

"error"

The resource can’t be fetched, or the fetched resource is invalid

For example, images that match the <url> production can be used immediately, but will not result in a visual change until the image data is fetched. CSSResourceValue objects represent this by providing values that track loaded state via the CSSResourceState enum.
w3c/css-houdini-drafts/186[css-worklets] [css-typed-om] CSSResourceValues constructed within Worklets should have state "unloaded".

4.7. CSSImageValue objects

interface CSSImageValue : CSSResourceValue {
    readonly attribute double? intrinsicWidth;
    readonly attribute double? intrinsicHeight;
    readonly attribute double? intrinsicRatio;
};

[Constructor(USVString url)]
interface CSSURLImageValue : CSSImageValue {
    readonly attribute USVString url;
};

CSSImageValue objects represent values for properties that take <image> productions, for example background-image, list-style-image, and border-image-source.

CSSImageValue objects that do not require network data (for example linear and radial gradients) are initialized with state "loaded".

If the CSSImageValue's state is "loaded", and the resource has an intrinsic width, height, or aspect ratio, then intrinsicWidth, intrinsicHeight, and intrinsicRatio must reflect the resource’s corresponding value. In all other cases, the attributes must be null.

Does the loading lifecycle need to be described here?

CSSURLImageValue objects represent CSSImageValues that match the <url> production. For these objects, the url attribute contains the URL that references the image.

4.8. CSSFontFaceValue objects

[Constructor(DOMString fontFamilyName)]
interface CSSFontFaceValue : CSSResourceValue {
    readonly attribute DOMString fontFamilyName;
};

w3c/css-houdini-drafts/293[CSS Typed OM] CSSFontFaceValue objects can't store source value

CSSFontFaceValue objects are opaque representations of the contents of @font-face rules. They are used to pass font information into paint image definitions, via custom properties.

As font data may need to be fetched from a remote source, CSSFontFaceValue is a subclass of CSSResourceValue.

w3c/css-houdini-drafts/159[css-typed-om] Spec up ColorValue

5. CSSStyleValue normalization

This section describes how Typed OM objects are constructed from CSS values.

If a property’s grammar is more complex than one of the types listed here, it produces a raw CSSStyleValue, with a stringification behavior that produces the CSSOM serialization of the property.

Better to define a full table of properties and what types they normalize to.

Per F2F, "CSSOM serialization" isn’t well-defined/interoperable enough. We instead need to strictly define the serialization of every property. This should be done according to CSSOM principlies, tho (generally, shortest possible value).

5.1. Raw CSS tokens: properties with var() references

Regardless of what the property’s grammar is otherwise, a property value with an un-substituted var() reference is represented as a list of component values, which becomes a CSSUnparsedValue in the Typed OM.

To normalize a list of component values from a list:
  1. Replace all var() references in list with CSSVariableReferenceValue objects, as described in §5.2 var() References.

  2. Replace each remaining maximal subsequence of component values in list with a single string of their concatenated serializations.

  3. Return a new CSSUnparsedValue whose [[tokens]] slot is set to list.

The string "calc(42px + var(--foo, 15em) + var(--bar, var(--far) + 15px))" is converted into a CSSUnparsedValue that contains a sequence with:

5.2. var() References

var() references become CSSVariableReferenceValues in the Typed OM.

To normalize a var() reference var:
  1. Let object be a new CSSVariableReferenceValue.

  2. Set object’s variable internal slot to the serialization of the <custom-ident> providing the variable name.

  3. If var has a fallback value, set object’s fallback internal slot to the result of normalizing the fallback’s component values. Otherwise, set it to null.

  4. Return object.

5.3. Identifier Values

CSS identifiers become CSSKeywordValues in the Typed OM.

To normalize an identifier ident:
  1. Return a new CSSKeywordValue with its value internal slot set to the serialization of ident.

5.4. <number>, <percentage>, and <dimension> values

CSS <number>, <percentage>, and <dimension> values become CSSNumericValues in the Typed OM.

To normalize a numeric value num:
  1. If num is a calc(), min(), or max() expression, normalize a math expression from num and return the result.

  2. If num is the unitless value 0 and num is a <dimension>, return a new CSSUnitValue with its value internal slot set to 0, and its unit internal slot set to "px".

  3. Return a new CSSUnitValue with its value internal slot set to the numeric value of num, and its unit internal slot set to "number" if num is a <number>, "percent" if num is a <percentage>, and num’s unit if num is a <dimension>.

To normalize a math expression num:
  1. If num is a min() or max() expression:

    1. Let values be the result of normalizing the arguments to the expression, treating each argument as if it were the contents of a calc() expression.

    2. Return a new CSSMathMin or CSSMathMax object, respectively, with its values internal slot set to values.

  2. Assert: Otherwise, num is a calc().

  3. Turn num’s argument into an expression tree using standard PEMDAS precedence rules, with the following exceptions/clarification:

    • Treat subtraction as instead being addition, with the RHS argument instead wrapped in a special "negate" node.

    • Treat division as instead being multiplication, with the RHS argument instead wrapped in a special "invert" node.

    • Addition and multiplication are N-ary; each node can have any number of arguments.

    • If an expression has only a single value in it, and no operation, treat it as an addition node with the single argument.

  4. Recursively transform the expression tree into objects, as follows:

    addition node

    becomes a new CSSMathSum object, with its values internal slot set to its list of arguments

    multiplication node

    becomes a new CSSMathProduct object, with its values internal slot set to its list of arguments

    negate node

    becomes a new CSSMathNegate object, with its value internal slot set to its argument

    invert node

    becomes a new CSSMathInvert object, with its value internal slot set to its argument

    leaf node

    normalized as appropriate

For example, calc(1px - 2 * 3em) produces the structure:
CSSMathSum(
    CSS.px(1),
    CSSMathNegate(
        CSSMathProduct(
            2,
            CSS.em(3)
        )
    )
)

Note: The value computation process may transform different units into identical ones, simplifying the resulting expression. For example, calc(1px + 2em) as a specified value results in a CSSMathSum(CSS.px(1), CSS.em(2)), but as a computed value will give CSS.px(33) or similar (depending on the value of an em in that context).

5.5. <transform-list> and <transform-function> values

CSS <transform-list> values become CSSTransformValues in the Typed OM, while CSS <transform-function> values become CSSTransformComponents.

To normalize a <transform-list> list:
  1. Return a new CSSTransformValue whose values to iterate over are the result of mapping the normalize a <transform-function> algorithm over list.

To normalize a <transform-function> func, perform the appropriate set of steps below, based on func:
matrix()
matrix3d()
  1. Return a new CSSMatrixComponent object, whose matrix internal slot is set to a 4x4 matrix representing the same information as func, and whose is2D internal slot is true if func is matrix(), and false otherwise.

translate()
translateX()
translateY()
translate3d()
translateZ()
  1. Return a new CSSTranslation object, whose x, y, and z internal slots are set to the normalization of the specified x/y/z offsets, or the normalization of 0px if not specified in func, and whose is2D internal slot is true if func is translate(), translateX(), or translateY(), and false otherwise.

scale()
scaleX()
scaleY()
scale3d()
scaleZ()
  1. Return a new CSSScale object, whose x, y, and z internal slots are set to the specified x/y/z scales, or to 1 if not specified in func and whose is2D internal slot is true if func is scale(), scaleX(), or scaleY(), and false otherwise.

rotate()
rotate3d()
rotateX()
rotateY()
rotateZ()
  1. Return a new CSSRotation object, whose angle internal slot is set to the normalization of the specified angle, and whose x, y, and z internal slots are set to the specified rotation axis coordinates, or the implicit axis coordinates if not specified in func and whose is2D internal slot is true if func is rotate(), and false otherwise.

skew()
skewX()
skewY()
  1. Return a new CSSSkew object, whose ax and ay internal slots are set to the normalization of the specified x and y angles, or the normalization of 0deg if not specified in func, and whose is2D internal slot is true.

perspective()
  1. Return a new CSSPerspective object, whose length internal slot is set to the normalization of the specified length and whose is2D internal slot is false.

5.6. CSSPositionValue normalization

If the provided value matches the <position> production, then a CSSPositionValue is constructed with x and y components determined via the following process. If this process, or any sub-process referenced by this process fails, then normalization as a whole fails.

  1. Initialize both x and y to a CSSNumericValue value representing 50%.

  2. If the provided value is a single keyword, length, percentage, or calc expression, then follow the procedure outlined in §5.6.1 Determining x or y from a single value with value given by the provided value and a horizontal bias.

  3. Otherwise, if the provided value consists of a combination of two keywords, then:

    1. follow the procedure outlined in §5.6.1 Determining x or y from a single value with value given by the first keyword and an auto bias.

    2. if bias is horizontal, set it to vertical. Otherwise, set it to horizontal.

    3. follow the procedure again with value given by the second keyword, using the existing bias.

  4. Otherwise, if the provided value consists of a combination of two keywords, lengths, percentages, and calc expressions, then follow the procedure outlined in §5.6.1 Determining x or y from a single value with value given by the first part of the provided value and a horizontal bias, then follow the procedure again with value given by the second part of the provided value and a vertical bias.

  5. Otherwise:

    1. if the provided value starts with a keyword followed by a length, percentage, or calc expression, then follow the procedure outlined in §5.6.2 Determining x or y from a keyword and a length with keyword set to the keyword, length set to the length, percentage, or calc expression, and auto bias.

    2. otherwise, follow the procedure outlined in §5.6.1 Determining x or y from a single value with value set to the first component of the provided value and an auto bias.

    3. if bias is horizontal, set it to vertical. Otherwise, set it to horizontal.

    4. if the remainder of the provided value is a single keyword, length, percentage or calc expression, follow the procedure outlined in §5.6.1 Determining x or y from a single value with value set to the keyword and the existing bias.

    5. otherwise, if the remainder of the provided value consists of a keyword followed by a length, percentage or calc expression, follow the procedure outlined in §5.6.2 Determining x or y from a keyword and a length with keyword set to the keyword, length set to the length, percentage, or calc expression, and the existing bias.

    6. Otherwise, the process fails.

5.6.1. Determining x or y from a single value

The following process sets a value for either x or y, depending on an input value and bias. The process also updates bias based on the value.

  1. If value is the keyword "left" and bias is not vertical, then set x to a CSSNumericValue value representing 0% and bias to horizontal and exit this process.

  2. If value is the keyword "right" and bias is not vertical, then set x to a CSSNumericValue value representing 100% and bias to horizontal and exit this process.

  3. If value is the keyword "top" and bias is not horizontal, then set y to a CSSNumericValue value representing 0% and bias to vertical and exit this process.

  4. If value is the keyword "bottom" and bias is not horizontal, then set y to a CSSNumericValue value representing 100% and bias to vertical and exit this process.

  5. If value matches the <length-percentage> production, then set norm to the result of normalizing |value| as a numeric value. If bias is vertical, set y to norm, otherwise set x to norm and bias to horizontal. Exit this process.

  6. If value is not the keyword "center", then this process fails.

5.6.2. Determining x or y from a keyword and a length

The following process sets a value for either x ory, depending on an input keyword, length, and bias. The process also updates bias based on the keyword and length.

  1. follow the procedure outlined in §5.6.1 Determining x or y from a single value with value given by keyword, using the provided bias

  2. let adjustment be the result of normalizing |length| as a numeric value.

  3. If the keyword is "right" or "bottom", let adjustment be the result of subtracting adjustment from a zero length.

  4. amend x (if bias is horizontal) or y (if bias is vertical) by adding adjustment to it.

5.7. CSSResourceValue normalization

Resource references are normalized by determining whether the reference is invalid (in which case state is set to error) or requires network data (in which case state is set to loading). If data is not required and the reference is valid then state is set to loaded.

If state is set to loading then the image reference is reevaluated once the pending data becomes available, according to the same rules referenced above.

Normalization does not fail for CSSResourceValue objects.

The string 'url(bike.png)' is converted into a CSSURLImageValue with state set to unloaded and the url set to bike.png. The intrinsicWidth, intrinsicHeight and intrinsicRatio are all set to null.

6. CSSStyleValue Serialization

The way that a CSSStyleValue serializes is dependent on how the value was constructed.

if the value was constructed from a DOMString

the serialization is the DOMString from which the value was constructed.

otherwise, if the value was constructed using an IDL constructor

the serialization is specified in the sections below.

otherwise, if the value was extracted from the CSSOM

the serialization matches the CSSOM serialization of the corresponding value.

For example:

var length1 = CSSNumericValue.from("42.0px");
length1.toString(); // "42.0px"

var length2 = CSSNumericValue.from(42.0, "px");
length2.toString(); // "42px";

element.style.width = "42.0px";
var length3 = element.styleMap.get('width');
length3.toString(); // "42px";

6.1. CSSUnparsedValue Serialization

CSSUnparsedValue objects are serialized by first serializing each CSSVariableReferenceValue, then concatenating the contained DOMStrings and CSSVariableReferenceValue serializations in order.

CSSVariableReferenceValue objects are serialized by the following process:

  1. the fallback CSSUnparsedValue is serialized

  2. if the fallback serialization is the empty string, then the CSSVariableReferenceValue serializes as "var(" + variable + ")"

  3. otherwise, the CSSVariableReferenceValue serializes as "var(" + variable + "," + fallback serialization + ")"

6.2. CSSKeywordValue Serialization

CSSKeywordValue objects are serialized to their contained value attribute.

6.3. CSSUnitValue Serialization

If their unit is "number", CSSUnitValue objects are serialized to the string representation of their value.

Otherwise, if their unit is "percent", CSSUnitValue objects are serialized to the string representation of their value followed by the character U+0025 PERCENTAGE SIGN (%).

Otherwise, CSSUnitValue objects are serialized to the string representation of their value followed by their unit.

6.4. CSSMathValue Serialization

CSSMathValue objects are serialized into a calc() expression

TODO

6.5. CSSTransformValue Serialization

CSSTransformValue objects are serialized by generating a space-separated list of serializations of the contained CSSTransformComponent objects.

CSSTransformComponent objects are serialized according to the following rules:

6.6. CSSPositionValue Serialization

CSSPositionValue objects are serialized by:

6.7. CSSURLImageValue Serialization

CSSURLImageValue objects are serialized to the string given by "url(" + url + ")".

6.8. CSSFontFaceValue Serialization

CSSFontFaceValue objects are serialized to the value of their contained fontFamilyName.

7. Security Considerations

There are no known security issues introduced by these features.

8. Privacy Considerations

There are no known privacy issues introduced by these features.

Appendix A: Computed CSSStyleValue objects

This appendix describes the restrictions on CSSStyleValue objects that appear as computed values (i.e. as a value stored on computed StylePropertyMapReadOnly objects).

Computed CSSUnparsedValue objects

A property with a declared CSSUnparsedValue value will not compute to a CSSUnparsedValue. Instead, after custom property references are resolved, the CSSStyleValue subclass appropriate to the property will be used.

For example, a style rule containing:
width: calc(var(--foo) + 10%);

Will represent a declared width as an CSSUnparsedValue, but if this value is the winning value during computation for a given element then that element’s computed width will be represented by a CSSNumericValue object (assuming that --foo resolves to a valid substitution).

Often there will be no CSSStyleValue subclass appropriate - for example when a custom property contains a reference to another custom property. In these cases, a CSSStyleValue is used directly to represent a value of unknown type.

For example, a style rule containing:

--foo: var(--bar) black;

Will represent a declared value for --foo as a CSSUnparsedValue, and if this value is the winning declaration for --foo during computation for a given element, then that element’s will have a computed value for --foo that is represented by a CSSStyleValue.

Computed CSSKeywordValue objects

During computation, CSSKeywordValue objects are either as specified (e.g. auto values for lengths that participate in layout) or resolved to a relevant value and renormalized (e.g. the color red).

Computed CSSUnitValue objects

During computation, CSSUnitValue objects are range-restricted or rounded as appropriate to the relevant property, but otherwise as specified.

Computed CSSMathValue objects

During computation, CSSMathValue objects are reduced accordingly:

TODO

Computed CSSTransformValue objects

During computation, any CSSNumericValue objects referenced by a CSSTransformComponent (e.g. the x attribute of a CSSTranslation) are computed according to Computed CSSUnitValue objects, but the CSSTransformValue object is otherwise as specified.

Computed CSSPositionValue objects

During computation, both the x and y components of a CSSPositionValue are computed according to Computed CSSUnitValue objects.

Computed CSSImageValue objects

Computed CSSImageValue objects are as specified.

Computed CSSFontFaceValue objects

Computed CSSFontFaceValue objects are as specified.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Advisements are normative sections styled to evoke special attention and are set apart from other normative text with <strong class="advisement">, like this: UAs MUST provide an accessible alternative.

Conformance classes

Conformance to this specification is defined for three conformance classes:

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

A style sheet is conformant to this specification if all of its statements that use syntax defined in this module are valid according to the generic CSS grammar and the individual grammars of each feature defined in this module.

A renderer is conformant to this specification if, in addition to interpreting the style sheet as defined by the appropriate specifications, it supports all the features defined by this specification by parsing them correctly and rendering the document accordingly. However, the inability of a UA to correctly render a document due to limitations of the device does not make the UA non-conformant. (For example, a UA is not required to render color on a monochrome monitor.)

An authoring tool is conformant to this specification if it writes style sheets that are syntactically correct according to the generic CSS grammar and the individual grammars of each feature in this module, and meet all other conformance requirements of style sheets as described in this module.

Partial implementations

So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid (and ignore as appropriate) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support. In particular, user agents must not selectively ignore unsupported component values and honor supported values in a single multi-value property declaration: if any value is considered invalid (as unsupported values must be), CSS requires that the entire declaration be ignored.

Implementations of Unstable and Proprietary Features

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

Non-experimental implementations

Once a specification reaches the Candidate Recommendation stage, non-experimental implementations are possible, and implementors should release an unprefixed implementation of any CR-level feature they can demonstrate to be correctly implemented according to spec.

To establish and maintain the interoperability of CSS across implementations, the CSS Working Group requests that non-experimental CSS renderers submit an implementation report (and, if necessary, the testcases used for that implementation report) to the W3C before releasing an unprefixed implementation of any CSS features. Testcases submitted to W3C are subject to review and correction by the CSS Working Group.

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

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley. CSS Color Module Level 4. URL: https://www.w3.org/TR/css-color-4/
[CSS-GRID-1]
Tab Atkins Jr.; Elika Etemad; Rossen Atanassov. CSS Grid Layout Module Level 1. URL: https://www.w3.org/TR/css-grid-1/
[CSS-LISTS-3]
Tab Atkins Jr.. CSS Lists and Counters Module Level 3. URL: https://www.w3.org/TR/css-lists-3/
[CSS-POSITION-3]
Rossen Atanassov; Arron Eicholz. CSS Positioned Layout Module Level 3. URL: https://www.w3.org/TR/css-position-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-TRANSFORMS-1]
Simon Fraser; et al. CSS Transforms Module Level 1. URL: https://www.w3.org/TR/css-transforms-1/
[CSS-TRANSFORMS-2]
CSS Transforms Module Level 2 URL: https://drafts.csswg.org/css-transforms-2/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. URL: https://www.w3.org/TR/css-values-3/
[CSS-VARIABLES-1]
Tab Atkins Jr.. CSS Custom Properties for Cascading Variables Module Level 1. URL: https://www.w3.org/TR/css-variables-1/
[CSS3-BACKGROUND]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. URL: https://www.w3.org/TR/css3-background/
[CSS3-IMAGES]
Elika Etemad; Tab Atkins Jr.. CSS Image Values and Replaced Content Module Level 3. 17 April 2012. CR. URL: https://www.w3.org/TR/css3-images/
[CSSOM-1]
Simon Pieters; Glenn Adams. CSS Object Model (CSSOM). URL: https://www.w3.org/TR/cssom-1/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[GEOMETRY-1]
Simon Pieters; Dirk Schulze; Rik Cabanier. Geometry Interfaces Module Level 1. URL: https://www.w3.org/TR/geometry-1/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[WebIDL]
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. URL: https://heycam.github.io/webidl/

IDL Index

interface CSSStyleValue {
    stringifier;
    static CSSStyleValue? parse(DOMString property, DOMString cssText);
    static sequence<CSSStyleValue>? parseAll(DOMString property, DOMString cssText);
};

interface StylePropertyMapReadOnly {
    CSSStyleValue? get(DOMString property);
    sequence<CSSStyleValue> getAll(DOMString property);
    boolean has(DOMString property);
    iterable<DOMString, (CSSStyleValue or sequence<CSSStyleValue>)>;
    sequence<DOMString> getProperties();
    stringifier;
};

callback UpdateFunction = CSSStyleValue (CSSStyleValue oldValue);

interface StylePropertyMap : StylePropertyMapReadOnly {
    void append(DOMString property, (CSSStyleValue or DOMString)... values);
    void delete(DOMString property);
    void set(DOMString property, (CSSStyleValue or DOMString)... values);
    void update(DOMString property, UpdateFunction updateFunction);
};

partial interface Window {
    StylePropertyMapReadOnly getComputedStyleMap(Element element, optional DOMString? pseudoElt);
};

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

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

[Constructor((DOMString or CSSVariableReferenceValue)... members)]
interface CSSUnparsedValue : CSSStyleValue {
    iterable<(DOMString or CSSVariableReferenceValue)>;
    readonly attribute unsigned long length;
    getter (DOMString or CSSVariableReferenceValue) (unsigned long index);
};

interface CSSVariableReferenceValue {
    attribute DOMString variable;
    attribute CSSUnparsedValue? fallback;
};

[Constructor(DOMString value)]
interface CSSKeywordValue : CSSStyleValue {
    attribute DOMString value;
    stringifier;
};

typedef (DOMString or CSSKeywordValue) CSSKeywordish;

typedef (double or CSSNumericValue) CSSNumberish;

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);

    CSSNumericValue to(DOMString unit);
    CSSMathSum toSum(DOMString... units);
    // ??? type();

    static CSSNumericValue parse(DOMString cssText);
};

[Constructor(double value, DOMString unit)]
interface CSSUnitValue : CSSNumericValue {
    attribute double value;
    attribute DOMString unit;
    readonly attribute DOMString type;
};

interface CSSMathValue : CSSNumericValue {
    readonly attribute CSSMathOperator operator;
    readonly attribute DOMString type;
};

[Constructor(CSSNumberish... args)]
interface CSSMathSum : CSSMathValue {
    attribute CSSNumericArray values;
};

[Constructor(CSSNumberish... args)]
interface CSSMathProduct : CSSMathValue {
    attribute CSSNumericArray values;
};

[Constructor(CSSNumberish arg)]
interface CSSMathNegate : CSSMathValue {
    attribute CSSNumericValue value;
};

[Constructor(CSSNumberish arg)]
interface CSSMathInvert : CSSMathValue {
    attribute CSSNumericValue value;
};

[Constructor(CSSNumberish... args)]
interface CSSMathMin : CSSMathValue {
    attribute CSSNumericArray values;
};

[Constructor(CSSNumberish... args)]
interface CSSMathMax : CSSMathValue {
    attribute CSSNumericArray values;
};

interface CSSNumericArray {}; // See issue below

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

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);
};

[Constructor(optional sequence<CSSTransformComponent> transforms)]
interface CSSTransformValue : CSSStyleValue {
    /*arraylike<CSSTransformComponent>;*/
    readonly attribute boolean is2D;
    DOMMatrix toMatrix();
};

interface CSSTransformComponent {
    stringifier;
    attribute boolean is2D;
};

[Constructor(CSSNumericValue x, CSSNumericValue y, optional CSSNumericValue z)]
interface CSSTranslation : CSSTransformComponent {
    attribute CSSNumericValue x;
    attribute CSSNumericValue y;
    attribute CSSNumericValue z;
};

[Constructor(CSSNumericValue angle),
 Constructor(CSSNumberish x, CSSNumberish y, CSSNumberish z, CSSNumericValue angle)]
interface CSSRotation : CSSTransformComponent {
    attribute CSSNumberish x;
    attribute CSSNumberish y;
    attribute CSSNumberish z;
    attribute CSSNumericValue angle;
};

[Constructor(CSSNumberish x, CSSNumberish y, optional CSSNumberish z)]
interface CSSScale : CSSTransformComponent {
    attribute CSSNumberish x;
    attribute CSSNumberish y;
    attribute CSSNumberish z;
};

[Constructor(CSSNumericValue ax, CSSNumericValue ay)]
interface CSSSkew : CSSTransformComponent {
    attribute CSSNumericValue ax;
    attribute CSSNumericValue ay;
};

[Constructor(CSSNumericValue length)]
interface CSSPerspective : CSSTransformComponent {
    attribute CSSNumericValue length;
};

[Constructor(DOMMatrixReadOnly matrix, optional CSSMatrixComponentOptions options)]
interface CSSMatrixComponent : CSSTransformComponent {
    attribute DOMMatrix matrix;
};

dictionary CSSMatrixComponentOptions {
    boolean is2D;
};

[Constructor(CSSNumericValue x, CSSNumericValue y)]
interface CSSPositionValue : CSSStyleValue {
    attribute CSSNumericValue x;
    attribute CSSNumericValue y;
};

enum CSSResourceState {"unloaded", "loading", "loaded", "error"};

interface CSSResourceValue : CSSStyleValue {
    readonly attribute CSSResourceState state;
};


interface CSSImageValue : CSSResourceValue {
    readonly attribute double? intrinsicWidth;
    readonly attribute double? intrinsicHeight;
    readonly attribute double? intrinsicRatio;
};

[Constructor(USVString url)]
interface CSSURLImageValue : CSSImageValue {
    readonly attribute USVString url;
};


[Constructor(DOMString fontFamilyName)]
interface CSSFontFaceValue : CSSResourceValue {
    readonly attribute DOMString fontFamilyName;
};


Issues Index

w3c/css-houdini-drafts/305When doing CSSStyleValue.parse(), what should throw vs return null?
w3c/css-houdini-drafts/148[css-typed-om] add detailed descriptions of the rest of the methods on StylePropertyMap
w3c/css-houdini-drafts/148[css-typed-om] add detailed descriptions of the rest of the methods on StylePropertyMap
w3c/css-houdini-drafts/148[css-typed-om] add detailed descriptions of the rest of the methods on StylePropertyMap
w3c/css-houdini-drafts/268[css-typed-om] Stringification behavior
w3c/css-houdini-drafts/276[css-typed-om] Describe how the StylePropertyMaps should interact with custom properties
w3c/css-houdini-drafts/309[css-typed-om] Should StyleMap be case sensitive?
w3c/css-houdini-drafts/310[css-typed-om] Consider using properties in addition to .get/.set
w3c/css-houdini-drafts/350[all] Move Houdini APIs to `window.CSS`
w3c/css-houdini-drafts/239[css-typed-om]: There's no nice way to represent CSSUnparsedValue as a "list-plus" style object.
Figure out how we want to represent the type of an expression in JS, and define the type() method accordingly.
CSSNumericArray will be an Array-like restricted to containing CSSNumericValue objects. This is dependent on WebIDL#345 getting resolved properly.
Assuming the resolution of WebIDL#345 produces an "arraylike" declaration.
w3c/css-houdini-drafts/186[css-worklets] [css-typed-om] CSSResourceValues constructed within Worklets should have state "unloaded".
Does the loading lifecycle need to be described here?
w3c/css-houdini-drafts/293[CSS Typed OM] CSSFontFaceValue objects can't store source value
w3c/css-houdini-drafts/159[css-typed-om] Spec up ColorValue
Better to define a full table of properties and what types they normalize to.
Per F2F, "CSSOM serialization" isn’t well-defined/interoperable enough. We instead need to strictly define the serialization of every property. This should be done according to CSSOM principlies, tho (generally, shortest possible value).
TODO
TODO