Home > Designing, Others > The Typed Object Model

The Typed Object Model

November 20th, 2019 Leave a comment Go to comments

I help write technical documentation and one feature I’ve been writing about this year that has really stood out is the Typed Object Model (or Typed OM). If you haven’t come across it yet you would be forgiven as it’s pretty new. It falls under the CSS Houdini suite of API’s and on the surface seems the least exciting. However, it underpins all of them and will eventually change how we view CSS as a language.

It allows for typing of values in CSS. Remember the base syntax of CSS:

selector {
  property: value;
}

That value can be a lot of things, colors, units, images, words. Which to CSS makes sense, but to us, when we access it and try to change it via any means, it comes as a string.

Typed OM allows that to be returned as a type instead. So if we were to access, say 10px in JavaScript, instead of '10px' being returned, we get the number 10 and the unit 'px'. Which is a whole heap easier to work with.

You may have seen some of Ana Tudor’s amazing demos. In her example ‘Hollow’, she registers custom properties in JavaScript to use in her CSS. She’s using the Properties & Values API and thus can determine the types of these properties. This wouldn’t be possible if Typed OM wasn’t around.

How about an example

On the face of it, it seems a little bit more work than the way we’re used to accessing and setting styles at the moment, but there are a lot of benefits. If we want to, say, get the width of an element we would do something like this:

const elementWidth = getComputedStyle(myElement).width;
// returns '300px'

Not only are we accessing a string here, but getComputedStyle forces a re-layout. We may not be aware of what units are being used, and so accessing the numerical part of the string becomes tricky, and if we are running this in our code a number of times it could harm performance.

If we want to use Typed OM and return a typed value we would have to access the properties and values via computedStyleMap() and it’s get() method:

const elementWidth = myElement.computedStyleMap().get('width');
// returns CSSUnitValue {value: 300, type: 'px'}

Now we can access the value and the unit separately, and the value here is returned as an integer.

elementWidth.value // 300
elementWidth.unit // 'px'

We also haven’t forced a re-layout, which is nothing short of a win. Bet you’re now wondering and yes, not only can you access the CSS value as a type, but you can create unit values pretty easily as well, via the global CSS object:

let newWidth = CSS.px(320);
// returns CSSUnitValue {value: 320, type: 'px'}

That’s a small example and one that’s easy to show, as the units part of the specification has been fleshed out somewhat considerably to some other value types. But regardless it allows for a lot less string matching and flexibility.

Another set of values which is well specced are transform values: translate, rotate, scale, etc. This is great because if we wanted to change one of these values on an element without Typed OM, it can be tricky, especially if we’ve set more than one.

.myElement {
  transform: rotate(10deg) scale(1.2);
}

Let’s say we wanted to increase the rotation. If we access this property with getComputedStyle() a matrix is returned.

const transform = getComputedStyle(myElement).transform;
// returns matrix(0.984808, 0.173648, -0.173648, 0.984808, 0, 0)

With the transforms we have on the element, we can’t possibly pull out one value from the matrix. I’m not going to go all vectors, scalar values, and dot product here, but suffice to say, you can’t pull out one value once the matrix has been formed. There are definitely going to be numerous mappings.

If you have just a transform or scale, this is possible by string matching where the value is in the matrix. But that’s information for another day.

We could use custom properties to set the value of these transforms and update those:

.myElement {
  --rotate: 10deg;
  transform: rotate(var(--rotate)) scale(1.2);
}
myElement.style.setProperty('--rotate', '15deg');

However, we need to know the name of the custom property and we are still passing in a string.

Enter Typed OM. Let’s take a look at what’s returned from the computedStyleMap:

myElement.computedStyleMap().get('transform');
/* returns
CSSTransformValue {
  0: CSSRotate {...}
  1: CSSScale {...}
} */

Each transform value has its own type. There’s CSSRotate and CSSScale for the above code. As well as all the others like skew and translate. So instead of dealing with matrix, or still working with strings, we can create these types and apply a CSSTransformValue with them.

const newTransform = new CSSTransformValue([
  new CSSRotate(CSS.deg(15)),
  new CSSScale(CSS.number(1.2), CSS.number(1.2))
])
myElement.attributeStyleMap.set('transform', newTransform);

Notice how we’re creating the transform types with the class method and the new keyword, rather than the factory method used with the numerical values.

We have a lot more control over each individual value, something much needed when we declare multiple values in CSS.

I put together a Pen with all this example code here.

It’s the foundations

As we begin to get more familiar with other API’s within the Houdini suite, the benefits of all of this will become even more obvious. When we declare a new custom property with the Properties and Values API, we set a type and get automatic error handling, for instance. These value types are also passed into the worklets of the Paint API and Layout API, so we can use the power there too.

There are more types I haven’t mentioned, more types to come and really a lot more to write about. If it hasn’t been already, Typed OM is being implemented in all major browsers at this time. It certainly piqued my interest this year, so look out for it as 2020 comes around!

The post The Typed Object Model appeared first on CSS-Tricks.

Categories: Designing, Others Tags:
  1. No comments yet.
  1. No trackbacks yet.
You must be logged in to post a comment.