Extending CSS with Houdini
CSS Houdini is a family of low-level APIs that open the browser’s styling and layout engine to JavaScript. Instead of waiting for a new CSS feature to be specified and shipped, you can register custom paint routines, give custom properties real types, and (where supported) drive layout and animation from worklets. This reference lists the main Houdini APIs with their registration syntax, the inputs they consume, and how widely each is supported.
How it works
Most Houdini code runs in a worklet — a lightweight, isolated script context
loaded with addModule. Inside it you register a class:
// paint.js — loaded with CSS.paintWorklet.addModule('paint.js')
registerPaint('checker', class {
static get inputProperties() { return ['--tile-size']; }
paint(ctx, size, props) {
const t = parseInt(props.get('--tile-size'), 10) || 16;
for (let y = 0; y < size.height; y += t)
for (let x = 0; x < size.width; x += t)
if (((x / t) + (y / t)) % 2 === 0) ctx.fillRect(x, y, t, t);
}
});
Typed custom properties come from the main thread:
CSS.registerProperty({
name: '--tile-size', syntax: '<length>',
inherits: false, initialValue: '16px',
});
The worklet reads declared inputProperties, so animating --tile-size
re-runs the paint each frame.
Tips and notes
- Always feature-detect —
if ('paintWorklet' in CSS) { ... }— and provide a CSS fallback. - A registered typed property (e.g.
<color>) becomes animatable; plain untyped--varscannot be smoothly transitioned. - Worklets are isolated: no DOM, no
window. Pass data through input properties and arguments only. - Paint and Properties/Values are the production-ready pair today; treat Layout and Animation worklets as experimental.
- The Typed OM (
attributeStyleMap,CSSStyleValue) is usable standalone and removes brittle string parsing of CSS values.