HomeJournalThis post

CSS Typed OM vs getComputedStyle()

Choose CSS Typed OM or getComputedStyle property by property, with real value classes, conversions, readback, and explicit fallback semantics.

JP
JP Casabianca
AI Engineer and Product Designer · full-stack delivery · Bogotá

CSS Typed OM vs getComputedStyle is a choice between structured values and broadly supported serialized strings. This guide tests authored maps, computed maps, conversions, transforms, custom properties, and an honest fallback.

CSS Typed OM vs getComputedStyle is a value question

getComputedStyle returns a CSSStyleDeclaration whose properties are serialized strings. CSS Typed OM exposes style maps and structured CSS values such as CSSUnitValue. The choice is not simply old API versus new API. CSS Typed OM vs getComputedStyle asks which value semantics, coverage, and fallback contract a product tool actually needs.

Strings are interoperable, inspectable, and broadly available. They are also easy to parse incorrectly: a length might be pixels, a percentage, a calculation, or a keyword. Typed values can support unit-aware operations and structured transforms, but Level 1 coverage is incomplete and some properties remain unparsed. The teaching fixture preserves unsupported states rather than inventing types.

Begin with the operation. If the tool only displays the browser’s resolved string, getComputedStyle is enough. If it performs numeric conversion or structured mutation on supported values, Typed OM can remove parsing code. If it needs authored tokens and computed output, read both layers because neither API alone reconstructs the cascade or design-system intent.

String-to-typed-value microscopeThe same width, transform, and custom property appear as serialized legacy strings and property-specific typed or unparsed objects.RESOLVED STRINGSTYPED OBJECTSwidth: “192px”transform: “matrix(…)”--raw: “calc(…)”CSSUnitValueCSSTransformValueCSSUnparsedValueoperationdecides
String-to-typed-value microscope
The same width, transform, and custom property appear as serialized legacy strings and property-specific typed or unparsed objects.
Same declarations, different interfaces
PropertygetComputedStylePossible Typed OM shape
widthSerialized resolved stringCSSUnitValue when supported
transformSerialized transform or matrixStructured transform sequence when supported
custom propertyToken stringOften CSSUnparsedValue
Figure 1: Typed structure helps only when the property and operation are actually supported.

Inspect typed values and resolved strings

The CSS Typed OM specification defines StylePropertyMap, computedStyleMap(), value classes, and conversion rules. Calling element.computedStyleMap().get("width") may produce a CSSUnitValue; calling getComputedStyle(element).width produces a string. The typed result exposes value and unit without a regular expression.

The CSS Typed OM vs getComputedStyle microscope shows the same declaration entering both interfaces. It does not claim every browser or property returns the illustrated class. The lab records the constructor name in the executing browser, plus serialization and fallback path, so an unsupported case remains visible.

Resolved legacy values have historical behavior. The CSSOM definition of getComputedStyle describes a live, read-only declaration and resolved values, which may differ from a specification’s computed-value concept for some properties. Name that distinction in tooltips and exports. CSS value parsing remains an application responsibility on the legacy route, with a declared grammar and rejection behavior. “Computed” in the method name is not permission to collapse authored, cascaded, computed, used, and resolved stages.

Read authored and computed maps separately

An element’s attributeStyleMap represents declarations in its inline style attribute. Its computedStyleMap() represents computed style exposed through Typed OM. Styles originating from rules, inheritance, layers, or defaults will not appear in the inline map. An empty authored entry does not mean the property has no effect.

CSS Typed OM vs getComputedStyle becomes useful when a design tool names the layer it is reading. Use attributeStyleMap to edit inline declarations the tool owns. Use computedStyleMap() to inspect the post-cascade typed value when supported. Use getComputedStyle for a serialized resolved view and fallback. Do not write resolved pixels back as though they were the author’s token.

Cascade ownership is a separate problem. Keep value inspection separate from cascade ownership, and show the source rule through browser tooling or a dedicated stylesheet model when provenance matters. The lifecycle figure marks authored declaration, cascade, computed value, resolved serialization, mutation, and reserialization as different stations.

CSS value lifecycleAuthored declaration, cascade, computed value, legacy resolution, typed conversion, mutation, and serialization remain distinct stations.authoredcascadecomputedresolved / typedmutationreadback + serialization
CSS value lifecycle
Authored declaration, cascade, computed value, legacy resolution, typed conversion, mutation, and serialization remain distinct stations.
  1. Authored declaration carries syntax and source intent.
  2. Cascade selects a winning value.
  3. Computed-value processing resolves according to property rules.
  4. getComputedStyle exposes a legacy resolved serialization; computedStyleMap exposes supported typed values.
  5. Mutation writes to an owned declaration and must be read back.
Figure 2: Reading computed output cannot reconstruct every authored token or cascade owner.

Convert units without parsing strings

A CSSUnitValue can convert between compatible absolute units with .to(unit). Converting 96 pixels to inches is defined; converting a percentage to pixels may require a layout basis the value does not carry. Calculations and relative units can remain structured or unsupported depending on the property and browser. Catch conversion errors as typed outcomes.

In CSS Typed OM vs getComputedStyle, the typed path never extracts numbers from text. It checks the value class and calls the conversion method. The legacy path returns the string and labels any numeric interpretation as application parsing. Keeping those paths distinct prevents a fallback regex from masquerading as Typed OM semantics.

Pin conversion fixtures that are context independent, then include context-dependent cases as “requires basis.” The lab checks 96px → 1in only when the executing browser returns a convertible unit value. It records percentages, calc(), and keywords without asserting false parity. Unit-safe code is code that knows when it lacks enough information.

Handle lists and transforms structurally

Many CSS properties are lists or composite values. A transform can be represented as a CSSTransformValue containing typed components, enabling inspection or mutation without splitting parentheses and commas. Other properties may return CSSStyleValue, CSSUnparsedValue, or no useful typed representation. Feature coverage is property-specific.

CSS Typed OM vs getComputedStyle should therefore be decided per operation, not once for an entire application. A transform editor may benefit greatly while a color inspector still relies on serialization in a given browser. Record class, iterable components, unit conversions, and serialization for every property row.

When mutating, construct values through supported APIs and read them back. A successful assignment does not prove semantic equivalence if the browser normalizes or rejects components. The specimen toggles transform, length, color, and a custom property, then compares both read paths. The readback step also records which interface produced each representation. No timing data is collected; this is a capability and meaning test, not a performance benchmark.

Treat custom properties as a boundary

Unregistered custom properties carry token streams and often appear through Typed OM as CSSUnparsedValue. That is honest: the browser cannot infer whether --space means a length, color, or arbitrary grammar. Stringifying it may be useful for display, but numeric conversion would require an application-owned parser and schema.

The CSS Properties and Values API lets authors register syntax, inheritance, and an initial value. Registration can give the engine more semantic information, especially for animation and computed-value handling. Still test the executing browser rather than assuming every registered custom property becomes a specific Typed OM class.

For CSS Typed OM vs getComputedStyle, expose three states: typed and convertible, present but unparsed, and unsupported or absent. Register typed custom properties before animating them, but keep the fallback useful. Registration state belongs in the receipt because it changes the browser's interpretation contract. A token inspector should map inspected values back to semantic design tokens instead of overwriting names with resolved colors or pixels.

Build a progressive value adapter

Feature-detect computedStyleMap, attributeStyleMap, and the classes needed by the exact operation. The adapter can expose a small discriminated result: typed, unparsed, legacy-string, or unsupported. Include property, authored source, class name, serialized value, requested conversion, and any error.

CSS Typed OM vs getComputedStyle then becomes a transparent routing decision. Prefer typed values for supported numeric conversions and structured transformations. Fall back to getComputedStyle for display or carefully scoped compatibility. Never silently parse a legacy string and label it typed. If the product owns a parser, name its grammar and test corpus separately.

Colors deserve special care. Serializations may normalize syntax or spaces without preserving the author’s chosen color space. Preserve color-space intent when tooling reads color by retaining token/source metadata where available. A resolved rgb() string may be visually usable but insufficient for a round-trip design editor. The adapter should say when source intent is unavailable instead of guessing from normalized output.

Inspect the live token specimen

The browser lab creates one component with inline width, stylesheet-driven padding and color, a transform list, and registered and unregistered custom properties. It reads every case through both APIs, mutates selected values, and publishes a receipt containing actual constructor names, serialization, conversions, semantic differences, and chosen adapter.

Resize and mutation controls change the specimen deterministically. The CSS Typed OM vs getComputedStyle table updates without hiding that percentages depend on context. If Typed OM is absent, all rows remain visible through the legacy route and the capability header says so. If a value is unparsed, that state appears as data rather than an empty cell.

Use the lab as a starting corpus, then add the properties your product manipulates. Inspect a token editor on every supported engine, because one successful length says nothing about transforms or custom properties. Export the JSON receipt with browser version during compatibility review. The article’s claims stay bounded to observed API shapes and specification semantics, not universal support.

Runnable artifact — A capability and semantics fixture in the executing browser, not a cross-browser performance benchmark or promise of complete Typed OM coverage.

<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>CSS Typed OM comparison lab</title><style>@property --meter{syntax:'<length>';inherits:false;initial-value:8px}:root{color-scheme:dark}*{box-sizing:border-box}body{max-width:980px;margin:auto;padding:24px;background:#101018;color:#fff;font:16px/1.5 system-ui}button,a{min-height:44px;padding:10px;font:inherit}#frame{width:384px;max-width:100%;border:1px dashed #8d8ea2;padding:8px}.specimen{--raw-token:calc(1rem + 2px);--meter:12px;width:50%;padding:1.25rem;color:oklch(82% .12 165);transform:translateX(6px) rotate(2deg);border:2px solid currentColor;border-radius:14px}table{width:100%;border-collapse:collapse}th,td{padding:8px;border-bottom:1px solid #6f7084;text-align:left;vertical-align:top}textarea{width:100%;min-height:260px;background:#08080e;color:#fff}.scroll{overflow:auto}@media(max-width:560px){body{padding:14px}table{min-width:1080px}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}</style><main><h1>CSS Typed OM vs getComputedStyle</h1><p id="status" aria-live="polite"></p><div id="frame"><div id="specimen" class="specimen">Live token specimen</div></div><p><button id="mutate">Mutate width, transform, color, and --meter</button> <button id="resize">Resize containing block</button> <button id="legacy">Force legacy mode</button></p><div class="scroll" tabindex="0" aria-label="Property adapter results"><table><caption>Authored-map and computed-map execution receipt</caption><thead><tr><th>Property</th><th>Requested operation</th><th>Source model</th><th>Authored value</th><th>Computed class/value</th><th>Legacy resolved string</th><th>Conversion</th><th>Adapter/caveat</th></tr></thead><tbody id="rows"></tbody></table></div><textarea id="receipt" readonly aria-label="Execution receipt"></textarea><p><a id="download" download="css-typed-om-receipt.json">Download JSON receipt</a></p></main><script>
const el=document.querySelector('#specimen'),frame=document.querySelector('#frame'),properties=['width','padding-left','transform','color','--meter','--raw-token'],operations={width:'convert and observe resize', 'padding-left':'inspect computed length',transform:'edit structured components',color:'display resolved color','--meter':'read registered custom length','--raw-token':'preserve authored token stream'};let forcedLegacy=new URLSearchParams(location.search).get('legacy')==='1',wide=false,mutationCount=0;
const serialize=value=>value==null?null:String(value);
function authoredValue(property,hasAuthored){if(!hasAuthored)return null;try{return el.attributeStyleMap.get(property)??null}catch{return null}}
function conversionOracle(){if(typeof CSSUnitValue!=='function')return{supported:false,px96ToIn:null,incompatible:'Typed OM unavailable'};let px96ToIn=null,incompatible=null;try{px96ToIn=new CSSUnitValue(96,'px').to('in').value}catch(error){px96ToIn=error.name}try{new CSSUnitValue(1,'deg').to('px');incompatible='unexpected-success'}catch(error){incompatible=error.name}return{supported:true,px96ToIn,incompatible,pass:px96ToIn===1&&incompatible!=='unexpected-success'}}
function read(){const hasTyped=typeof el.computedStyleMap==='function',hasAuthored=!!el.attributeStyleMap,useTyped=hasTyped&&!forcedLegacy,legacy=getComputedStyle(el),rows=properties.map(property=>{const authored=authoredValue(property,hasAuthored);let computed=null,error=null,conversion=null;if(useTyped){try{computed=el.computedStyleMap().get(property);if(computed?.constructor?.name==='CSSUnitValue'){try{conversion=property==='width'?computed.to('in').value+'in':computed.to('px').value+'px'}catch(e){conversion='incompatible: '+e.name}}}catch(e){error=e.name}}const computedClass=computed?.constructor?.name||null,authoredClass=authored?.constructor?.name||null,authoredSerialization=serialize(authored),legacyResolvedString=legacy.getPropertyValue(property).trim(),adapter=useTyped&&computed?(computedClass==='CSSUnparsedValue'?'typed-unparsed':'typed-computed'):'legacy-string';return{property,requestedOperation:operations[property],sourceModel:authored?'inline declaration via attributeStyleMap':'stylesheet declaration selected by cascade',authoredClass,authoredSerialization,computedClass,computedSerialization:serialize(computed),legacyResolvedString,conversion,error,adapter,resolvedComputedCaveat:'computedStyleMap exposes a computed typed value when supported; getComputedStyle exposes a legacy resolved serialization; neither identifies the winning stylesheet rule'}}),oracle=conversionOracle(),data={fixture:'css-typed-om-value-receipt-v2',capability:{computedStyleMap:hasTyped,attributeStyleMap:hasAuthored,CSSUnitValue:typeof CSSUnitValue==='function'},forcedLegacy,containerWidth:frame.getBoundingClientRect().width,mutationCount,oracle,rows,benchmark:false};document.querySelector('#status').textContent=forcedLegacy?'Forced legacy-string mode; authored-map reads remain visible.':hasTyped?'Typed computed map active; unsupported values and authored absences remain explicit.':'Typed OM unavailable; every computed row uses labeled legacy-string output.';document.querySelector('#rows').innerHTML=rows.map(r=>'<tr><th>'+r.property+'</th><td>'+r.requestedOperation+'</td><td>'+r.sourceModel+'</td><td>'+(r.authoredClass||'none')+' · '+(r.authoredSerialization||'—')+'</td><td>'+(r.computedClass||'unsupported')+' · '+(r.computedSerialization||'—')+'</td><td>'+r.legacyResolvedString+'</td><td>'+(r.conversion||'—')+'</td><td>'+r.adapter+' · '+r.resolvedComputedCaveat+'</td></tr>').join('');const pass=rows.length===6&&(!oracle.supported||oracle.pass)&&rows.every(row=>row.legacyResolvedString!==undefined);const receipt=document.querySelector('#receipt');receipt.value=(pass?'PASS: ':'FAIL: ')+JSON.stringify(data,null,2);receipt.dataset.execution=JSON.stringify(data);document.querySelector('#download').href=pass?URL.createObjectURL(new Blob([JSON.stringify(data,null,2)],{type:'application/json'})):'';return data}
function setAuthored(property,typedValue,stringValue){if(el.attributeStyleMap){try{el.attributeStyleMap.set(property,typedValue);return 'attributeStyleMap'}catch{}}el.style.setProperty(property,stringValue);return 'style-fallback'}
document.querySelector('#mutate').onclick=()=>{setAuthored('width',typeof CSS!=='undefined'&&CSS.px?CSS.px(240):'240px','240px');let transformValue='translateX(14px)';try{transformValue=new CSSTransformValue([new CSSTranslate(CSS.px(14),CSS.px(0))])}catch{}setAuthored('transform',transformValue,'translateX(14px)');setAuthored('color','rgb(255, 198, 92)','rgb(255, 198, 92)');setAuthored('--meter',typeof CSS!=='undefined'&&CSS.px?CSS.px(24):'24px','24px');mutationCount++;read()};
document.querySelector('#resize').onclick=()=>{wide=!wide;frame.style.width=wide?'75%':'384px';if(el.attributeStyleMap)try{el.attributeStyleMap.delete('width')}catch{}else el.style.removeProperty('width');read()};document.querySelector('#legacy').onclick=()=>{forcedLegacy=!forcedLegacy;read()};read();
</script></html>

Read the property matrix without false parity

The third figure compares width, padding, transform, color, registered custom property, and unregistered custom property. Rows carry an expected operation rather than a simplistic winner: convert, display, structurally edit, preserve source intent, or expose an unparsed token stream. The chosen interface can differ row by row.

CSS Typed OM vs getComputedStyle favors Typed OM when typed arithmetic eliminates fragile parsing and current support covers the property. It favors getComputedStyle when the product only needs a resolved display string or must serve engines without the typed surface. It favors a stylesheet/token model when authored provenance matters more than computed output.

The matrix’s semantic table repeats class, string, conversion, and adapter labels without color. At mobile widths the table scrolls inside a labeled region; controls remain at least 44 pixels high. Reduced-motion mode disables specimen transitions. These details prevent a compatibility tool from becoming inaccessible precisely when someone zooms to inspect values.

Property-by-property adapter matrixSix specimen properties choose typed, unparsed, legacy-string, or source-model handling according to the requested operation.property / tasktypedunparsedlegacysource modelwidth · converttransform · editcolor · display--raw · preservepadding · inspect
Property-by-property adapter matrix
Six specimen properties choose typed, unparsed, legacy-string, or source-model handling according to the requested operation.
Adapter outcomes
TaskPreferred pathBoundary
Compatible unit conversionTypedRequires CSSUnitValue and compatible unit
Structured transform editTypedProperty support varies
Display resolved colorLegacy stringMay not preserve source color space
Preserve design tokenSource modelComputed output lacks provenance
Figure 3: One application can truthfully use several adapters without pretending their semantics are identical.

Choose the smallest stable interface

Use getComputedStyle for broad, read-only resolved serialization. Add CSS Typed OM for concrete operations that benefit from value classes, compatible-unit conversion, or structured transforms. Read attributeStyleMap only for declarations that the application intentionally owns. Preserve unparsed and unsupported states, and keep a stylesheet or design-token source of truth for author intent.

A CSS Typed OM vs getComputedStyle decision record should list properties, operations, supported browsers, active adapter, readback behavior, and fallback differences. Revisit it when the draft, browser coverage, registered-property behavior, or design-tool needs change. Avoid a platform-wide migration whose only metric is fewer strings.

Take one action: run the comparison lab on one real token inspector before replacing string reads. Add its highest-risk properties, export receipts from supported browsers, and choose the smallest interface that performs each operation truthfully. Preserve one unsupported fixture so fallback behavior remains tested over time. A mixed adapter is not architectural failure; it is often the accurate representation of today’s platform.