DEV Community

Tan Li Hau
Tan Li Hau

Posted on • Originally published at lihautan.com

Notes: The CSS Podcast #027 - "Houdini Series: Typed Object Model"

Podcast

Link to the Podcast

Notes

CSS Typed Object Model (Typed OM)

  • CSS Typed OM API allow manipulating CSS styles through a typed JS representation rather than a simple string.
  • Provide performance win. Browser understands the structured JS representation and no longer needs to parse CSS string from scratch.
const div = document.createElement('div'); // browser needs to parse the string to understand and use it div.style.height = '5px'; // browser understands and use the value as 5px div.style.height = CSS.px(5); 
Enter fullscreen mode Exit fullscreen mode
  • Built-in error handling. You can't provide invalid value to a type.
div.attributeStyleMap.set('color', CSS.px(10)) // TypeError: Failed to set, invalid type for property 
Enter fullscreen mode Exit fullscreen mode
  • Rather than manipulating raw string, developer can create / transform CSS in a meaningful object
const _5px = CSS.px(5); // 5px const _15px = _5px.add(CSS.px(10)); // 15px; const div = document.createElement('div'); div.style.height = _15px; // <div style="height: 15px;"></div> 
Enter fullscreen mode Exit fullscreen mode
  • API based on functional programming concept
  • To check browser support Typed OM, currently (21 Nov 2020) supported in Safari Tech Preview & Chromium
// checking browser support if (window.CSS && CSS.number) { // 😍 browser supports Typed OM! } 
Enter fullscreen mode Exit fullscreen mode

computedStyleMap

<script> const element = document.querySelector('#app'); console.log(element.computedStyleMap().get('font-size')); // Specification: CSSUnitValue { value: 2, unit: 'rem' } // Chrome: CSSUnitValue { value: 32, unit: 'px' } console.log(window.getComputedStyle(element).fontSize); // "32px" </script> <div id="app" style="font-size: 2rem;"></div> 
Enter fullscreen mode Exit fullscreen mode

attributeStyleMap

  • parse, modify inline styles
<script> const element = document.querySelector('#app'); const inlineStyles = element.attributeStyleMap; console.log(inlineStyles.get('font-size')); // CSSUnitValue { value: 2, unit: 'rem' } inlineStyles.set('height', CSS.px(10)); // <div id="app" style="font-size: 2rem; height: 10px;"></div>  inlineStyles.clear(); // <div id="app" style=""></div>  inlineStyles.append('background-image', 'linear-gradient(yellow, green)'); inlineStyles.append('background-image', 'linear-gradient(to bottom, blue, red)'); // <div id="app" // style="background-image: linear-gradient(yellow, green),  // linear-gradient(to bottom, blue, red)"></div>  inlineStyles.delete('background'); // <div id="app" style=""></div>  inlineStyles.has('opacity'); // false </script> <div id="app" style="font-size: 2rem;"></div> 
Enter fullscreen mode Exit fullscreen mode
<script> // `attributeStyleMap` only gets inline style console.log(element.attributeStyleMap.get('color')); // null console.log(element.computedStyleMap().get('color')); // CSSStyleValue { /* red */ } </script> <div id="app" style="font-size: 2rem;"></div> <style> #app { color: red; } </style> 
Enter fullscreen mode Exit fullscreen mode

Types of CSSStyleValue

  • CSSImageValue
  • CSSKeywordValue
  • CSSNumericValue
  • CSSPositionValue
  • CSSTransformValue
  • CSSUnparsedValue

create CSSStyleValue

CSSStyleValue.parse('font-size', '32px'); // CSSUnitValue { value: 2, unit: 'px' } CSSStyleValue.parse('transform', 'translate3d(10px, 20px, 30px) scale(1.5)'); /* CSSTransformValue { 0: CSSTranslate { is2D: false x: CSSUnitValue { value: 10, unit: 'px' } y: CSSUnitValue { value: 20, unit: 'px' } z: CSSUnitValue { value: 30, unit: 'px' } } 1: CSSScale { is2D: true x: CSSUnitValue { value: 1.5, unit: 'number' } y: CSSUnitValue { value: 1.5, unit: 'number' } z: CSSUnitValue { value: 1, unit: 'number' } } } */ 
Enter fullscreen mode Exit fullscreen mode

CSSImageValue

  • does not cover linear-gradient

CSSKeywordValue

  • display: none, none is a CSSKeywordValue
CSSStyleValue.parse('display', 'none'); // CSSKeywordValue { value: 'none' } const keywordValue = new CSSKeywordValue('flex'); // CSSKeywordValue { value: 'flex' } keywordValue.value; // 'flex' 
Enter fullscreen mode Exit fullscreen mode

CSSNumericValue

  • CSSNumericValue has a few subclasses, eg: CSSUnitValue, CSSMathValue
// Convert units CSS.px(48).to('in') // CSSUnitValue { value: 0.5, unit: 'in' } CSS.px(48).to('rem') // Error // Cannot transform absolute unit to relative unit 
Enter fullscreen mode Exit fullscreen mode

CSSMathValue

  • CSSMathNegate, CSSMathMin, CSSMathMax, CSSMathSum, CSSMathProduct, CSSMathInvert
new CSSMathSum(CSS.px(5), CSS.px(10)); // 15px div.attributeStyleMap.set('width', new CSSMathMax(CSS.rem(10), CSS.px(30))); // <div style="width: max(10rem, 30px)"></div> 
Enter fullscreen mode Exit fullscreen mode

CSSPositionValue

const position = new CSSPositionValue(CSS.px(20), CSS.px(50)) position.x; // CSSUnitValue { value: 20, unit: 'px' } position.y; // CSSUnitValue { value: 50, unit: 'px' } 
Enter fullscreen mode Exit fullscreen mode

CSSTransformValue

  • CSSTranslate, CSSScale, CSSRotate, CSSSkew, CSSSkewX, CSSSkewY, CSSPerspective, CSSMatrixComponent
const transformValue = CSSStyleValue.parse('transform', 'translate3d(10px, 20px, 30px) scale(1.5)'); // iterate through each transformation for (const transform of transformValue) { console.log(transform); } // CSSTranslate { ... } // CSSScale { ... } // get DOMMatrix out of the transformValue transformValue.toMatrix(); // DOMMatrix { a: 1.5, b: 0, c: 0, ... } 
Enter fullscreen mode Exit fullscreen mode

CSSUnparsedValue

  • CSSCustomProperty, that is not Houdini Property
  • the value is parsed as string
<script> const element = document.querySelector('#app'); element.attributeStyleMap.get('--length'); // CSSUnparsedValue { 0: 3px } </script> <div id="app" style="--length: 3px;"> 
Enter fullscreen mode Exit fullscreen mode

References

Top comments (1)

Collapse
 
lexiebkm profile image
Alexander B.K.

About 3 years since this post, this CSS Typed OM and other Houdini features like defining/declaring custom properties is still like an experimental. I mean, Firefox still does not support them, making me hesitate on using them.