CSS

Houdini

- overview -

The objective of the CSS-TAG Houdini Task Force (CSS Houdini) is to jointly develop features that explain the “magic” of Styling and Layout on the web.

The demos presented are, at best, based off of working drafts that are still in flux. No compatible implementations exist. What little actually works only really does so in Chrome Canary with Experimental Web Platform Features enabled (chrome://flags/#enable-experimental-web-platform-features). These demos will likely break if not viewed there. Syntax and semantics are likely to change. In fact, in the course of a year, one API has gone through 4ish incompatible API changes. Some of the examples provided are speculative based on previous and current working implementations and may not reflect final syntax. Terms and conditions apply. Not redeemable for cash. Your mileage may vary.

Worklets

- let's talk about it -

Go to https://houdini.glitch.me/, much muuuuch better then this slides :)

Worklets

Render Engine

Main Thread

Worklet Process

Worklet Process

Worklet Process

Browser JS

Worklet

worklet.addModule

Calls process, executed on a worklet

Title Text

// From https://drafts.css-houdini.org/worklets/ May 8, 2018 Editor's Draft

// From inside the browser's context
await demoWorklet.addModule('path/to/script.js');

// Multiple worklets can be loaded at once, as well
Promise.all([
  demoWorklet1.addModule('script1.js'),
  demoWorklet2.addModule('script2.js'),
]).then(results => {
  // Both worklets have loaded, and we can do tasks that rely on them
});
// From https://drafts.css-houdini.org/worklets/ May 8, 2018 Editor's Draft

// The kind of worklet it is
registerDemoWorklet('name', class { // The name we'll call this worklet

  // Each worklet can define different functions to be used
  // These will be called by the render engine as needed
  process(arg) {
    // Stuff happens in here! What happens depends on the worklet
    // Sometimes it'll return something
    // Other times it'll work directly on the arguments
    return !arg;
  }
});

Paint API

  • Use a 2D Canvas-like drawing context to draw
  • Scale our drawn image based on the size of the element (and redraw too)
  • Style our drawing using Custom Properties

Paint API

await CSS.paintWorklet.addModule('path/to/paint-worklet.js');
// From https://drafts.css-houdini.org/css-paint-api/ Apirl 18, 2018 Editor's Draft

registerPaint('sample-paint', class {
  // Custom properties from element's style to look for
  static get inputProperties() { return ['--foo']; }
  // Input arguments that can be passed to the `paint` function
  static get inputArguments() { return ['<color>']; }
  // Whether Alpha is allowed?
  static get contextOptions() { return {alpha: true}; }

  paint(ctx, size, props, args) {
    // ctx - drawing context
    // size - size of the box being painted
    // props - inputProperties
    // args - array of passed-in arguments

    // Paint code goes here.
  }
});

Animation API

  • Listen for user input, like scroll events!
  • Gives us the ability to drive KeyframeEffect Animations based on user input
  • Do it all of the main thread!

Animation API

// https://wicg.github.io/animation-worklet/ August 13, 2018 Draft Community Group Report
// !!!Changed this weekend!!!

registerAnimator('sample-animator', class {
  constructor(options) {
    // Called when a new animator is instantiated
    // Used to set stuff up for each use of an animator
  }
  animate(currentTime, effect) {
    // currentTime - The current time from the defined timeline
    // effect - Group of effects that this animation is working on

    // Animation frame logic goes here.
    // Usually something to the effect of setting the time of an effect
    effect.localTime = currentTime;
  }
});

Animation API

await CSS.animationWorklet.addModule('path/to/animation-worklet.js');

// A new ScrollTimeline to use! Listen to scroll on the `scrollSource`, 
// divide everything in to `timeRage` pieces, optionally start at 
// `startScrollOffset` and end at `endScrollOffset`
const scrollTimeline = new ScrollTimeline({
  scrollSource,
  timeRange,
});

const effectKeyframes = new KeyframeEffect(
  elem,
  // The Keyframe animation effects we want to apply
  [
    {transform: 'scale(1)'},
    {transform: 'scale(.25)'},
  ],
  {
    // The duration of the effect. If set to `timeRange` it will be a 1:1
    //  movement with the scroll of the element, 0-`timeRange` will be 
    //  faster, >`timeRage` will be slower, and 0 will be off
    duration: timeRange,
  },
);

new WorkletAnimation('sample-animator', effectKeyframes, scrollTimeline, {}).play(); // Make It So

Layout API

  • Literally make your own display properties
  • Pollyfill a layout spec, or build a new one!
  • Everyone likes a good Masonry layout, add one without the normal performance hit!

Layout API

Layout API

Layout API

Layout API

// From https://drafts.css-houdini.org/css-layout-api/ April 24, 2018 Editor's Draft
// !! Not working since this friday? !!
registerLayout('sample-layout', class {
  // Properties to look for on calling element
  static get inputProperties() { return ['--foo']; }
  // Properties to look for on direct child elements
  static get childrenInputProperties() { return ['--bar']; }
  // Options for the Layout
  // `childDisplay` can be 'block' or 'normal'. 'block' is similar to children of flex
  //  and grid containers, 'normal'. Otherwise boxes won't be blockified
  // `sizing` can be 'block-like' or 'manual'. 'block-like' will make the Layout 
  // Constraints's inline size be fixed, and block size be calculated like border-box. 
  // Otherwise, it's just the calculated inlineSize and blockSize
  static get layoutOptions() {
    return {
      childDisplay: 'normal',
      sizing: 'block-like'
    };
  }
  // Generator functions instead of normal functions to support async/parallel layout engines
  // Determines how a box fits its content or fits in to our layout context
  *intrinsicSizes(children, edges, styleMap) {
    // children - Child elements of box being laid out
    // edges - Layout Edges of the box being
    // styleMap - Typed OM style map of box being laid out
    // Intrinsic sizes code goes here.
  }
  *layout(children, edges, constraints, styleMap, breakToken) {
    // children - Child elements of Parent Layout
    // edges - `LayoutEdges` of Parent Layout
    // constraints - `Layout Constraints` of Parent Layout
    // styleMap - Typed OM style map of Parent Layout
    // breakToken - Token (if paginating for printing for example) to resume layout at
    // Layout code goes here.
  }
});

Check out:

Houdini

By Stefan Hagiu

Houdini

  • 538