DEV Community

Tan Li Hau
Tan Li Hau

Posted on • Originally published at lihautan.com

3

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

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

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.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay