The CSS Typed Object Model, part of Houdini, replaces stringly-typed style access with real typed objects. This reference covers the value types and the style-map APIs.
How it works
Instead of strings, styles are objects with numeric fields:
// old CSSOM — strings
el.style.width; // "100px" → must parse
// Typed OM — objects
el.computedStyleMap().get("width"); // CSSUnitValue { value: 100, unit: "px" }
el.attributeStyleMap.set("width", CSS.px(100));
The base class is CSSStyleValue. Concrete subtypes include CSSUnitValue (a number + unit),
CSSKeywordValue (an identifier like auto), CSSMathSum/CSSMathProduct (calc expressions),
CSSTransformValue and CSSColorValue. The CSS.px(...), CSS.percent(...), CSS.deg(...) and
CSS.number(...) factories build unit values directly.
Arithmetic and conversion
CSSNumericValue supports add, sub, mul, div, min, max:
- Compatible units reduce to a single
CSSUnitValue—CSS.px(10).add(CSS.px(5))→15px. - Incompatible units stay symbolic —
CSS.px(10).add(CSS.percent(50))→ aCSSMathSum(acalc()), because the result cannot be resolved without layout. .to(unit)converts within a unit family (e.g.CSS.cm(1).to("mm")→10mm).
Read computed values with the read-only element.computedStyleMap(); read/write inline values with
element.attributeStyleMap. Feature-detect with "computedStyleMap" in Element.prototype.
Type reference at a glance
| Type | What it represents | Example factory |
|---|---|---|
CSSUnitValue | A number with a CSS unit | CSS.px(100), CSS.percent(50) |
CSSKeywordValue | A CSS keyword identifier | new CSSKeywordValue('auto') |
CSSMathSum | A calc(a + b) expression | CSS.px(10).add(CSS.percent(5)) |
CSSMathProduct | A calc(a * b) expression | CSS.px(10).mul(2) |
CSSMathNegate | A negated value | CSS.px(10).negate() |
CSSTransformValue | A list of transform functions | new CSSTransformValue([...]) |
CSSStyleValue | The root type for all above | (abstract — use subtypes) |
Reading and writing styles with style maps
Two maps are available on elements, serving different purposes:
// attributeStyleMap — equivalent to the inline style attribute
// Read/write; reflects only explicitly set inline styles
el.attributeStyleMap.set('opacity', CSS.number(0.5));
el.attributeStyleMap.get('opacity'); // CSSUnitValue { value: 0.5, unit: 'number' }
el.attributeStyleMap.delete('opacity');
// computedStyleMap() — equivalent to getComputedStyle()
// Read-only snapshot; reflects the final resolved value after cascade + inheritance
const map = el.computedStyleMap();
map.get('font-size'); // e.g. CSSUnitValue { value: 16, unit: 'px' }
map.get('display'); // CSSKeywordValue { value: 'block' }
A key difference from getComputedStyle: computedStyleMap() returns typed objects, so you can immediately do arithmetic on the .value property rather than parsing a string with parseFloat.
Practical example — animated width from current computed value
A common animation pattern: read the current computed width, then increment it without string parsing:
const map = el.computedStyleMap();
const currentWidth = map.get('width'); // CSSUnitValue, e.g. { value: 200, unit: 'px' }
const newWidth = currentWidth.add(CSS.px(50)); // CSSUnitValue { value: 250, unit: 'px' }
el.attributeStyleMap.set('width', newWidth);
Compare this to the old approach: parseFloat(getComputedStyle(el).width) + 50 + 'px' — three string operations that can produce NaN silently if the value is unexpectedly not a plain pixel length.
When to use CSS Typed OM
Typed OM is most useful in JavaScript-driven animation loops, canvas-synchronized layouts, and CSS Houdini Paint/Layout worklets where you are reading and writing many style values in rapid succession. For simple one-shot style changes, the string API is often adequate and more broadly supported. Always feature-detect before relying on computedStyleMap in production code.