A complete guide of the Houdini APIs, example of usage and its browser support
What is Houdini anyway? 😠
Me, like two year ago
I learnt a lot about Houdini last year, and I'm amazed by all the possibilities it offers. I'm convinced Houdini is the future of CSS, and it was time for me to write the ultimate guide for it!
Houdini is a collection of browser APIs that allows JavaScript to interact with the CSS rendering engine.
Quite exciting!It allows us to create complex layouts, custom and programmable backgrounds, advanced animations and way more.
In this post, we'll walk through each API of the specification and examine the compatibility.
There are two groups of APIs:
- Low-level APIs that are the building blocks for the high-level APIs. These are the Type Object Model API, the CSS Properties & Values API, Font Metrics API and Worklets.
- High-level APIs interact with the CSS rendering engine. They consist of the Paint, the Layout, and Animation API.
Ready? Let's start!!
Low-level APIs
Type Object Model API
Interacting with CSS properties using JS can be painful, especially when using units.
You'll have to work with a string containing the whole CSS value, something looking like 120px
or 2.4rem
.
Type Object Model API exposes these values as a JS object:
{
value: 2.4,
unit: "rem"
}
Much better to work with!
Our DOM elements now got a computedStyleMap
method to work with the non-inline style and the attributeStyleMap
attribute to work with inline styles.
⚠️ Careful, while attributeStyleMap
is an attribute, computedStyleMap
is a method (a method is a function in an object) and needs to be called before we can access anything.
Here is how we use these properties:
// Set and get an inline style
element.attributeStyleMap.set("width", CSS.rem(48))
element.attributeStyleMap.get("width")
// => {value: 48, unit: "rem"}
// Set and get computed style (note the "()" after computedStyleMap)
element.computedStyleMap().set("height", CSS.em(12))
element.computedStyleMap().get("height")
// => {value: 12, unit: "em"}
In October 2021 this is supported in every browser except Firefox and Safari.
CSS Properties and Values API
The CSS Properties and Values API allows us to define CSS Custom Properties (aka CSS variables) in a more precise way.
We can now define a type, an initial value and its inheritance behavior.
To define a property, we use registerProperty
as such:
CSS.registerProperty({
name: "--brandingColor",
syntax: "<color>",
inherits: false,
initialValue: "goldenrod",
});
We'll be able to define it in the CSS in the future:
@property --brandingColor{
syntax: "<color>";
inherits: false;
initial-value: goldenrod;
}
The syntax
property represents the type of the value. It accepts: <number>
, <percentage>
, <length-percentage>
, <color>
, <image>
, <url>
, <integer>
and <angle>
. There is more on the W3C specification.
Setting the syntax
helps the browser knowing how to transition between values.
In CSS, you can transition between colors but cannot between gradients.
Here, by defining --brandingColor
we can, for example, animate a gradient 😎
That is how we proceed:
.element{
--brandingColor: goldenrod;
background: linear-gradient(90deg, khaki 0%, var(--brandingColor) 100%);
transition: --brandingColor 800ms ease-in-out;
}
.element:hover{
--brandingColor: gold;
}
The hover animation will only work if the --brandingColor
property type is <color>
.
If your browser supports this API, the block should animate on this demo:
In October 2021 this is supported in every browser except Firefox and Safari.
Font Metrics API
Font Metrics API aims to give developers dimensions of text elements. It is really complex and hacky to do this right now, so this will solve a lot.
Unfortunately, this interface is still in its early stage and is not supported in any browser yet.
Worklets
Worklets are scripts that plugs to low-level parts of the rendering engine. It runs JavaScript and WebAssembly code.
Houdini introduces three Worklets: the Pain Worklet, the Layout Worklet and the Animation Worklet that are used to power our high-level APIs.
High-level APIs
Paint API
Paint API let us use the 2D Rendering Context to draw backgrounds, text, and borders. We can draw using JS function, and we can use CSS Variables as parameters for this function.
To use the Paint API:
- Register the Paint Worklet
- Add it as a module
- Call it with
paint()
in your CSS
The Paint Worklet code needs its own JS file.
This is how you register it:
// cornerbox.js
class CornerBox{
paint(ctx, geom, properties) {
// The actual painting happens there
}
}
// Register our class under a specific name
registerPaint('cornerbox', CornerBox);
Then, we need to add it as a module where we put our JavaScript. We also declare the property we might want to animate using the CSS Property and Value API:
//main.js
// We register the property we want to animate
CSS.registerProperty({
name: "--cornerbox-length",
syntax: "<length>",
inherits: false,
initialValue: "120px",
});
CSS.registerProperty({
name: "--cornerbox-width",
syntax: "<length>",
inherits: false,
initialValue: "16px",
});
// Add the module from a local file
CSS.paintWorklet.addModule("./cornerbox.js");
// Or add it from a CDN
CSS.paintWorklet.addModule("https://unpkg.com/cornerbox@0.0.3/CornerBox.js");
We can now use paint(cornerbox)
in our CSS:
.element {
width: 20rem;
height: 20rem;
--cornerbox-color: #5f64e2;
--cornerbox-length: 120px;
--cornerbox-width: 16px;
background: paint(cornerbox);
transition: --cornerbox-length 400ms ease-in-out,
--cornerbox-width 400ms ease-in-out;
}
.element:hover{
--cornerbox-length: 220px;
--cornerbox-width: 24px;
}
We use --cornerbox-length
, --corner-width
and --corner-color
to configure our corner-box.
Have a look at the demo below to see it in action 🤙
This wraps up the Paint API section.
Paint Worklets is what hooked me to Houdini in the first place!
There are endless applications to this 🤗✨
You should definitely check out Houdini.how! It's a collection of Paint Worklets that are ready to use. This is where the Corner Box worklet comes from.
In October 2021 this is supported in every browser except Firefox and Safari.
Layout API
The Layout API allows us to define new layout modes that can be used as a display
property in our CSS.
It opens up a lot of possibilities! But this is a complex one and the specification is not definitive yet.
For more information, have a look at the specification on W3.org.
We'll show a working example, but not dive too deep into the code.
In this section, we'll use the Google Chrome Lab's Masonry Worklet.
That is what a Layout Worklet look like (the logic has been removed here):
// masonry.js
registerLayout('masonry', class {
static get inputProperties() {
return [ '--padding', '--columns' ];
}
static get inputProperties() {
return [ '--order' ];
}
async intrinsicSizes() {}
async layout(children, edges, constraints, styleMap, breakToken) {
// The actual code happens there
}
});
Like a Paint Worklet, let's add it as a module:
// main.js
// local
CSS.layoutWorklet.addModule("./masonry.js");
// elsewhere
CSS.layoutWorklet.addModule("https://raw.githubusercontent.com/GoogleChromeLabs/houdini-samples/master/layout-worklet/masonry/masonry.js");
And use it in our CSS:
.element{
--padding: 20;
--columns: 2;
display: layout(masonry);
}
And… We got a masonry layout working!
Have a look at the demo:
This is exciting, but not quite ready for now. It is not documented on MDN yet, and the implementation will likely change in the future.
Let's wait a few years on that one!
In October 2021 this feature is hidden behind a flag (Experimental Web Platform features) in every browser except Firefox and Safari.
Animation API
Animation API allows us to make advanced animations!
It aims to provide developers with a more performant way of animating using CSS.
Let's register our Animation Worklet:
//superBounce.js
registerAnimator("superBounce", class {
constructor(options) {
// Our code goes here
}
animate(currentTime, effect) {
// Our code goes here
}
});
And add it as a module:
// main.js
CSS.animationWorklet.addModule("./superBounce.js");
To use an Animation Worklet we need to declare what we would normally declare in a @keyframes
in JavaScript.
Let's put side to side what we would do with keyframes
and what we would do using JavaScript:
// Using the CSS approach
.element{
animation: bounce 800ms ease-in-out infinite;
}
@keyframes bounce {
0% {
transform: scale(1);
}
25% {
transform: scale(1.1);
}
50% {
transform: scale(1);
}
75% {
transform: scale(1.15);
}
}
// The JavaScript approach
const keyframes = [{
transform: 'scale(1)',
offset: 0
},
{
transform: 'scale(1.1)',
offset: 0.25
},
{
transform: 'scale(1)',
offset: 0.50
},
{
transform: 'scale(1.15)',
offset: 0.75
},
]
const timing = {
duration: 800,
easing: "ease-in-out",
iterations: Infinity
}
const element = document.querySelector('.element--js')
element.animate(keyframes, timing)
Using JavaScript, we can do a bit more than we CSS. For example, we can define the easing
in each keyframe.
Also, we can bind the animation progress to Scroll Events, play and pause it at will, change the playback rate, reverse the animation etc…
Here is a demo on CodePen:
That's it, we learned how to make custom animation using the Web Animation API 😎
For a more in-depth read, read the MDN guide about Web Animation API.
In October 2021 this feature is hidden behind a flag (Experimental Web Platform features) in every browser except Firefox and Safari.
Conclusion
I am personally very much excited about all these new features, especially the Paint API!!
I would love to see in the future a lot of available layout and paint worklets that we could customize using CSS variables.
That'll be an outstanding new step for CSS 😍
I'm Tom Quinonero, I write about design systems and CSS, Follow me on Twitter for more tips and resources 🤙
Sources and links
- CSS Houdini Playground: A CodePen collection I made to illustrate this post
- A Practical Overview Of CSS Houdini by Adrian Bece: An exhaustive and clear guide on Houdini. Probably the best thing to read if you want to go deeper!
- Paintlets: A gallery of tweakable and downloadable paint worklets (Chrome only)
- Houdini.how: A library of Worklet, mostly Paint worklets
- Houdini Spellbook: A nice introduction guide to Houdini
- Houdini Samples: A GitHub repository with nice worklets examples
- Generating Knockout Text with the CSS Paint by James South
- Houdini on MDN
Top comments (0)