v154 · css · typed om
Expose the CSSStyleValue hierarchy to workers
The CSS Typed OM has always been specified as [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]. Blink exposed it to windows and worklets and forgot workers, so CSSUnitValue was undefined in the one place you would most want to do bulk CSS value work off the main thread — and defined in Firefox and Safari, which made it a portability trap as well as a gap.
concepts
-
What exists where
The same five constructors probed in three contexts — this window, a dedicated worker, and a module worker — with the results compared side by side. On a browser before Chrome 154 the worker columns are empty, which is the bug in one table.
-
Parsing off the main thread
CSSStyleValue.parse()in a worker, against the string handling you had to fall back to. Feed it a real stylesheet's worth of values and compare what each approach gets right — units, calc(), and the values that should be rejected. -
A token pipeline
The practical shape: normalise a design token sheet — convert units, scale a ramp, reject nonsense — in a worker, and post typed results back to the page. Includes what survives
postMessageand what does not.
why it shipped
Typed OM exists so CSS values can be manipulated as numbers with units instead of strings that must be re-parsed on every read. That is exactly the kind of work you want off the main thread: normalising a token file, converting a theme between unit systems, validating author input in bulk. A worker was the natural home and the one context where the constructors were missing.
The fallback was string manipulation with regular expressions, which gets units wrong at the edges, cannot evaluate calc(), and silently accepts values the CSS parser would reject. This is a compliance fix: no new API, just the interfaces appearing where the specification always said they were.
the API
// Inside a worker, from Chrome 154:
const gap = CSSStyleValue.parse("margin", "2.5rem"); // CSSUnitValue
const doubled = new CSSUnitValue(gap.value * 2, gap.unit);
postMessage({ value: doubled.value, unit: doubled.unit });
// Feature-detect on the global, because there is no CSS.supports here.
const typed = typeof CSSUnitValue === "function";
Typed OM objects are not structured-cloneable, so what crosses postMessage is the value and unit, not the object. That is a detail worth knowing before you design the message shape, and the token pipeline demo shows it.