CSS Houdini - Painting the web

A presentation at CSS Cafe in October 2020 in by Lisi Linhart

Slide 1

Slide 1

CSS Houdini Painting the Web Lisi Linhart - @lisi_linhart - @storyblok

Slide 2

Slide 2

About me I’m a Frontend Engineer with a passion for web animation and UX. The past two years I’ve been lecturing about web development at the University of Applied Science Salzburg. Now I’m an Developer Experience Engineer at Storyblok. lisilinhart.info twitter.com/lisi_linhart codepen.io/lisilinhart www.storyblok.com Lisi Linhart - @lisi_linhart - @storyblok

Slide 3

Slide 3

What is CSS Houdini? ! Lisi Linhart - @lisi_linhart - @storyblok

Slide 4

Slide 4

What is CSS Houdini CSS Houdini is a W3C effort to define lower-level CSS DOM APIs for authors to understand, recreate, and extend high-level CSS authoring features. https://wiki.mozilla.org/CSS/Houdini Lisi Linhart - @lisi_linhart - @storyblok

Slide 5

Slide 5

What is CSS Houdini Extending CSS via JS Houdini introduces low-level JavaScript APIs for the browser’s render engine. This is simimlar to how Service Workers are a low-level JavaScript API for the browser’s cache. Lisi Linhart - @lisi_linhart - @storyblok

Slide 6

Slide 6

What is CSS Houdini Access to the rendering engine Houdini allows you to extend the CSS with new features using JavaScript, hook into CSS rendering engine and tell the browser how to apply CSS during a render process. Lisi Linhart - @lisi_linhart - @storyblok

Slide 7

Slide 7

Can we use it? see ishoudinireadyyet? Lisi Linhart - @lisi_linhart - @storyblok

Slide 8

Slide 8

Testing it out Activate experimental web platform features on chrome://flags/ Lisi Linhart - @lisi_linhart - @storyblok

Slide 9

Slide 9

Houdini APIs Lisi Linhart - @lisi_linhart - @storyblok

Slide 10

Slide 10

Houdini APIs The Houdini APIs allow you to work with the CSS parser, CSSOM, cascade, the layout, paint and composite rendering stages. Lisi Linhart - @lisi_linhart - @storyblok

Slide 11

Slide 11

Houdini APIs - High Level The high level apis are closely related to the browser’s rendering process (style → layout → paint → composite) → Paint API → Layout API → Animation API Lisi Linhart - @lisi_linhart - @storyblok

Slide 12

Slide 12

Paint API (High-level) An extension point for the browser’s paint rendering step where visual properties (color, background, border, etc.) are determined. → similar to a canvas drawing context → can “paint” anywhere, images are supported → has already quite good browser support (apart from Firefox) Lisi Linhart - @lisi_linhart - @storyblok

Slide 13

Slide 13

Paint API (High-level) Demo: https://css-houdini.rocks/tooltip Read more: https://houdini.glitch.me/paint Lisi Linhart - @lisi_linhart - @storyblok

Slide 14

Slide 14

Layout API (High-level) An extension point for the browser’s layout rendering step where element dimensions, position, and alignment are determined. → make your own display properties. Masonry, here we come! → Able to polyfill layout specs like Container Queries Lisi Linhart - @lisi_linhart - @storyblok

Slide 15

Slide 15

Layout API (High-level) Demo: https:// googlechromelabs.github.io/ houdini-samples/layout-worklet/ masonry/ Read more: https://houdini.glitch.me/layout Lisi Linhart - @lisi_linhart - @storyblok

Slide 16

Slide 16

Animation API (High-level) An extension point for browser’s composite rendering step where layers are drawn to the screen and animated. The Animation API allows animation effects to be tied to user input like scrolling in a performant way → Allows animations based on user input → Allows performant parallax Lisi Linhart - @lisi_linhart - @storyblok

Slide 17

Slide 17

Animation API (High-level) Demo: https:// googlechromelabs.github.io/ houdini-samples/animationworklet/twitter-header/ Read more: https://houdini.glitch.me/ animation Lisi Linhart - @lisi_linhart - @storyblok

Slide 18

Slide 18

Houdini APIs - Low Level The low level APIs form a foundation for high-level APIs. They are closely related to the parser extensions -> CSSOM, cascade → Typed Object Model API → Custom Properties & Values API → Font Metrics API → Parser API Lisi Linhart - @lisi_linhart - @storyblok

Slide 19

Slide 19

Typed Object Model API (Low-level) Before Houdini the only way for JavaScript to interact with CSS was by parsing CSS represented as string values and modifying them. The Typed OM gives shape and structure to CSS values that previously were simple strings. It’s an extension to the existing CSS Object Model (CSSOM) that exposes CSS values as typed JavaScript objects, instead of a simple strings. selectedElement.computedStyleMap().get(“font-size”); { } value: 20, unit: “px” selectedElement.attributeStyleMap.set(“font-size”, CSS.em(2)); Read more: https://houdini.glitch.me/typed-om Lisi Linhart - @lisi_linhart - @storyblok

Slide 20

Slide 20

Custom Properties & Values API (Low-level) Custom Properties give shape, structure, and constraints to CSS Variables. The CSS Properties And Values API allows developers to extend CSS variables by adding a type, initial value and define inheritance. → Types are preserved between CSS & JS → Good browser support already CSS.registerProperty({ name: “—colorPrimary”, syntax: “<color>”, inherits: false, initialValue: “blue”, }); Lisi Linhart - @lisi_linhart - @storyblok

Slide 21

Slide 21

Custom Properties & Values API (low-level) Demo: https://css-houdini.rocks/tooltip Read more: https://houdini.glitch.me/customproperties Lisi Linhart - @lisi_linhart - @storyblok

Slide 22

Slide 22

Font Metrics API (Low-level) (Very Early Stage) The Font Metrics API will provide methods for measuring dimensions of text elements that are being rendered on screen in order to allow developers to affect how text elements are being rendered on screen. Multi-line dynamic text truncation is an example of one of those features. Lisi Linhart - @lisi_linhart - @storyblok

Slide 23

Slide 23

Worklets Worklets are extension points for rendering engines. Worklets are scripts that run during render and are independent of the main JavaScript environment. Conceptually, they’re similar to Web Workers. The have very restricted access to the global scope. Houdini introduces following Worklets to extend the browser render engine: → Paint Worklet - Paint API → Animation Worklet - Animation API → Layout Worklet - Layout API https://houdini.glitch.me/worklets Lisi Linhart - @lisi_linhart - @storyblok

Slide 24

Slide 24

The Paint API Lisi Linhart - @lisi_linhart - @storyblok

Slide 25

Slide 25

The Paint API What does it do? The Paint API works very similar to the canvas drawing context. We can use JS to create custom drawing function that draw an image in CSS. We can then use this function for any CSS property that expects an image. You could use it for the background-image, the border-image or the liststyle-image. Lisi Linhart - @lisi_linhart - @storyblok

Slide 26

Slide 26

The Paint API Why do wee need it? → example: extra.css → instead of polyfills like the conic gradient polyfill → Reduce DOM nodes number, e.g. when drawing lots of particles → create fancy painting features Lisi Linhart - @lisi_linhart - @storyblok

Slide 27

Slide 27

How to use in CSS .slanted-bg { background-image: paint(slanted-bg); } Lisi Linhart - @lisi_linhart - @storyblok

Slide 28

Slide 28

How to use in CSS - Test for support .slanted-bg { background: papayawhip; } @supports (background: paint(slanted-bg)) { .slanted-bg { background: paint(slanted-bg); } } Lisi Linhart - @lisi_linhart - @storyblok

Slide 29

Slide 29

How to register in JS

  1. Declare a custom paint class 2. Register paint 3. Load worklet Lisi Linhart - @lisi_linhart - @storyblok

Slide 30

Slide 30

How to register in JS Declare class class SlantedBackground { paint(ctx, geom, props, args) { // paint implementation } } Lisi Linhart - @lisi_linhart - @storyblok

Slide 31

Slide 31

How to register in JS Register paint registerPaint(‘slanted-bg’, SlantedBackground); Lisi Linhart - @lisi_linhart - @storyblok

Slide 32

Slide 32

How to register in JS Load Worklet if (‘paintWorklet’ in CSS) { CSS.paintWorklet.addModule(‘slanted-bg-module.js’); } Lisi Linhart - @lisi_linhart - @storyblok

Slide 33

Slide 33

Paint API Example Lisi Linhart - @lisi_linhart - @storyblok

Slide 34

Slide 34

How my Houdini Journey started Lisi Linhart - @lisi_linhart - @storyblok

Slide 35

Slide 35

FFConf - 19. Nov 18 https://generativeartistry.com Lisi Linhart - @lisi_linhart - @storyblok

Slide 36

Slide 36

CSSConf - 31. May 19 Lisi Linhart - @lisi_linhart - @storyblok

Slide 37

Slide 37

How my Houdini Journey started ! Canvas & Houdini seem really similar Lisi Linhart - @lisi_linhart - @storyblok

Slide 38

Slide 38

How my Houdini Journey started https://generativeartistry.com/tutorials/tiled-lines var canvas = document.querySelector(‘canvas’); var context = canvas.getContext(‘2d’); for(var x = 0; x < size; x += step) { for(var y = 0; y < size; y+= step) { var leftToRight = Math.random() >= 0.5; if(leftToRight) { context.moveTo(x, y); context.lineTo(x + width, y + height); } else { context.moveTo(x + width, y); context.lineTo(x, y + height); } } } context.stroke(); Lisi Linhart - @lisi_linhart - @storyblok

Slide 39

Slide 39

How my Houdini Journey started Let’s try it in Houdini paint(ctx, geom) { for (let y = 0; y <= geom.height; y += size) { for (let x = 0; x <= geom.width; x += size) { let leftToRight = Math.random() >= 0.5; if (leftToRight) { ctx.moveTo(x, y + size); ctx.lineTo(x + size, y); } else { ctx.moveTo(x, y); ctx.lineTo(x + size, y + size); } } } } ctx.stroke(); Lisi Linhart - @lisi_linhart - @storyblok

Slide 40

Slide 40

How my Houdini Journey started Lisi Linhart - @lisi_linhart - @storyblok

Slide 41

Slide 41

Houdini & Generative Art https://codepen.io/lisilinhart/pen/LYEJWQQ Lisi Linhart - @lisi_linhart - @storyblok

Slide 42

Slide 42

  1. Register & Support if (CSS.paintWorklet) { CSS.paintWorklet.addModule(‘/lisilinhart/pen/LYEJWQQ.js’); } else { document.body.classList.add(‘warning’); } Lisi Linhart - @lisi_linhart - @storyblok

Slide 43

Slide 43

  1. Use in CSS .card { background: var(—c-light); background-image: paint(generative); —type: var(—drawType, lines); /* Types: lines,joyDivision,triangles */ —size: 120; —line-color: #E0AF53; —line-width: 2; } input[value=”lines”]:checked ~ .card { —drawType: lines; } Lisi Linhart - @lisi_linhart - @storyblok

Slide 44

Slide 44

  1. JS - registerPaint - input if (typeof registerPaint !== “undefined”) { registerPaint(“generative”, class { } paint(ctx, geom, properties) { … } }); Lisi Linhart - @lisi_linhart - @storyblok

Slide 45

Slide 45

The JS - paint() registerPaint(“generative”, class { static get inputProperties() { return [“—type”, “—size”, “—line-color”, “—line-width”]; } paint(ctx, geom, properties) { let drawType = String(properties.get(“—type”)).replace(’ ‘, ”); let size = Number(properties.get(“—size”)); let lineWidth = Number(properties.get(“—line-width”)); let lineColor = String(properties.get(“—line-color”)); ctx.lineWidth = lineWidth; ctx.strokeStyle = lineColor; const drawFunctions = { “lines”: () => this.drawLines(ctx, geom.width, geom.height, size), “joyDivision”: () => this.drawJoyDivision(ctx, geom.width, geom.height, size), “triangles”: () => this.drawTriangles(ctx, geom.width, geom.height, size), }; }); } drawFunctionsdrawType; Lisi Linhart - @lisi_linhart - @storyblok

Slide 46

Slide 46

The JS - drawLines() registerPaint(“generative”, class { static get inputProperties() { … } paint(ctx, geom, properties) { … } drawLines(ctx, width, height, size) { for (let y = 0; y <= height; y += size) { for (let x = 0; x <= width; x += size) { let leftToRight = Math.random() >= 0.5; if (leftToRight) { ctx.moveTo(x, y + size); ctx.lineTo(x + size, y); } else { ctx.moveTo(x, y); ctx.lineTo(x + size, y + size); } }); } } ctx.stroke(); } Lisi Linhart - @lisi_linhart - @storyblok

Slide 47

Slide 47

How does it perform compared to current CSS features? I wrote an article about it: Performance depends on various factors: → Painting performance between current features and Houdini Paint Examples are quite similar → Painting performance depends on your Houdini module complexity → CSS Houdini adds the overhead of loading the module → Consider adding a CSS fallback until it’s loaded (progressive enhancement!) Lisi Linhart - @lisi_linhart - @storyblok

Slide 48

Slide 48

Conclusion Lisi Linhart - @lisi_linhart - @storyblok

Slide 49

Slide 49

What is the CSS Houdini all about? CSS Houdini allows us to hook into the browser rendering process, so we can develop various CSS features that can be easily shared, implemented and, potentially, added to CSS specification itself. Lisi Linhart - @lisi_linhart - @storyblok

Slide 50

Slide 50

What are the advantages → close access to the CSSOM and parser → better performance than JS polyfills → great for features that depend on requestAnimationFrame or scroll interactions → build any CSS feature you want Lisi Linhart - @lisi_linhart - @storyblok

Slide 51

Slide 51

What is the Paint API all about? → Extend the browser render engine in various ways → Not all features have wide browers support: ishoudinireadyyet? → Allows you to easily include and adapt features that previously where not possible in CSS → great for reducing DOM nodes → no need for JS polyfills, we can just implement our own CSS features! Lisi Linhart - @lisi_linhart - @storyblok

Slide 52

Slide 52

What to keep in mind → Houdini is an experimental technology and is not production ready yet → always check for browser support before implementing any styles using this technique → be careful with animated effects and not do overdo it → great for progressive enhancement Lisi Linhart - @lisi_linhart - @storyblok

Slide 53

Slide 53

How do I get started? Lisi Linhart - @lisi_linhart - @storyblok

Slide 54

Slide 54

How do I get started? https://houdini.glitch.me Lisi Linhart - @lisi_linhart - @storyblok

Slide 55

Slide 55

How do I get started? https://github.com/nucliweb/awesome-css-houdini Lisi Linhart - @lisi_linhart - @storyblok

Slide 56

Slide 56

How do I get started? http://css-houdini.rock Lisi Linhart - @lisi_linhart - @storyblok

Slide 57

Slide 57

How do I get started? https://googlechromelabs.github.io/houdini-samples/ Lisi Linhart - @lisi_linhart - @storyblok

Slide 58

Slide 58

Articles → CSS Tricks - Paint API → Logrocket - Paint API → Google Developers - Houdini → Google Developers - Paint API → Smashing Magazine - CSS Houdini Lisi Linhart - @lisi_linhart - @storyblok

Slide 59

Slide 59

The End Thank you for joining! → My blog → My twitter → My Codepen → Storyblok Lisi Linhart - @lisi_linhart - @storyblok

Slide 60

Slide 60

Storyblok is a headless CMS, that helps your team to tell your story and manage content for every use-case: corporate websites, ecommerce, helpdesks, mobile apps, and screen displays. storyblok.com Lisi Linhart - @lisi_linhart - @storyblok