Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CSS Houdini- the bridge between CSS, JavaScript and the browser

Ebea51dd17c435982c03bc9ae29dc3a6?s=47 Serg Hospodarets
May 17, 2017
120

CSS Houdini- the bridge between CSS, JavaScript and the browser

Live slides + Demos: http://slides.com/malyw/houdini-codemotion#/

Today CSS Custom Properties are supported in all the major browsers. Now it’s time to do the next step- to have an ability to register new Custom Properties from JavaScript and setup the browser how to work with them (e.g. real CSS polyfills). They should work with the same performance as the native CSS properties, being animatable and aligned with CSSOM. Custom Properties can be used as a bridge between CSS and JavaScript. Houdini Task force introduces specs and JavaScript Worklets to expose the interaction with previously fully internal browser rendering mechanisms (during Paint, Layout, Composite stages). All this brings Front-End development to the next level, parts of which are already available for the developers.

Ebea51dd17c435982c03bc9ae29dc3a6?s=128

Serg Hospodarets

May 17, 2017
Tweet

Transcript

  1. CSS Houdini- the bridge between CSS, JavaScript and the browser

    Serg Hospodarets @malyw
  2. About me

  3. CSS Custom Properties

  4. Preprocessors Variables and Operators (+, -, *, /, %) $font-size:

    10px; $font-family: Helvetica, sans-serif; body { font: $font-size $font-family; } .mark{ font-size: 1.5 * $font-size; }
  5. Preprocessor variable problems Each preprocessor has own syntax Additional setup

    Recompilation after changes is required Compilation takes time Absence of interaction with the browser Not aware of the DOM structure
  6. Native CSS Syntax /* declaration */ --VAR_NAME: <declaration-value>; /* usage

    */ var(--VAR_NAME) /* root element selector (global scope) */ /* usually <html> */ :root {/* make available for whole the app */ /* CSS variables declarations */ --main-color: #ff00ff; --main-bg: rgb(200, 255, 255); } body { /* variable usage */ color: var(--main-color); } "--" prefix picked to prevent preprocessors to compile Custom Properties
  7. Declaration and usage

  8. Supported in all the major browsers

  9. Valid examples :root{ --main-color: #4d4e53; --main-bg: rgb(255, 255, 255); --logo-border-color:

    rebeccapurple; --header-height: 68px; --content-padding: 10px 20px; --base-line-height: 1.428571429; --transition-duration: .35s; --external-link: "external link"; --margin-top: calc(2vh + 20px); /* Valid CSS custom properties */ /* can be reused later in, say, JavaScript. */ --foo: if(x > 5) this.width = 10; }
  10. Declaration and use cases /* Default values */ .box{ /*---

    Default values ---*/ /* 10px is used */ margin: var(--possibly-non-existent-value, 10px); /* The --main-padding variable is used */ /* if --box-padding is not defined. */ padding: var(--box-padding, var(--main-padding)); } /* Reuse values in other vars */ .box__highlight::after{ --box-text: 'This is my box'; /* Equal to --box-highlight-text:'This is my box with highlight'; */ --box-highlight-text: var(--box-text)' with highlight'; content: var(--box-highlight-text); }
  11. Declaration and use cases

  12. CSS-Wide Keywords and "all" Property .common-values{ /* applies the value

    of the element’s parent. */ --border: inherit; /* applies the initial value as defined in the CSS specification (an empty value, or nothing in some cases of CSS custom properties). */ --bgcolor: initial; /* applies the inherited value if a property is normally inherited (as in the case of custom properties) or the initial value if the property is normally not inherited. */ --padding: unset; /* resets the property to the default value (user agent’s) (an empty value in the case of CSS custom properties). */ --animation: revert; all: initial; /* reset all other CSS styles */ /* Future? */ --: initial; /* reset all CSS custom properties */ }
  13. All Value Parts Can Be Changed Individually /* Separate values

    in CSS Custom Properties */ .transform { --scale: scale(2); --rotate: rotate(10deg); transform: var(--scale) var(--rotate); } .transform:hover{ --rotate: rotate(90deg); } .transform { transform: scale(2) rotate(10deg); } .transform:hover{ transform: scale(2) rotate(90deg); }
  14. Emulating non- existing CSS rule

  15. Operations: +, -, *, / :root { --block-font-size: 1rem; }

    .block__highlight { /* DOESN'T WORK */ font-size: var(--block-font-size)*1.5; } CSS calc() to the rescue (for values) :root { --block-font-size: 1rem; } .block__highlight { /* WORKS */ font-size: calc(var(--block-font-size)*1.5); }
  16. All together: calc() and change values separately

  17. Calculate all the app colors from the base-color (theming)

  18. Changes to Custom Props have immediate effect // SCSS .box

    { $indent: 30px; margin: $indent; /* 30px */ /* is ignored as changed after value is applied */ $indent: 50px; } .box:hover{ /* is ignored as no assignement is provided after this */ $indent: 80px; /* margin: $indent; to apply */ } // CSS .box { --indent: 30px; margin: var(--indent); /* 50px */ /* is applied as native variables are alive as other CSS props */ --indent: 50px; } .box:hover{ /* is applied, so margin: 80px; on hover */ --indent: 80px; }
  19. CSS Properties are aware ​ of the DOM structure //

    SCSS .text { $text-size: 20px; font-size: $text-size; } .active { $text-size: 30px; } /* CSS */ .text { --text-size: 20px; font-size: var(--text-size); } .active { --text-size: 30px; } <!-- HTML --> <div class="text">.text</div> <div class="text active">.text.active</div>
  20. Using Custom Properties With JavaScript const breakpointsData = document.querySelector('.breakpoints-data'); //

    GET const phone = getComputedStyle(breakpointsData) .getPropertyValue('--phone'); // SET breakpointsData.style .setProperty('--phone', 'custom'); .breakpoints-data { --phone: 480px; --tablet: 800px; } pass breakpoints data from CSS read values... assign CSS value calculated in JS update UI depending on the application state...
  21. Rotate page elements using CSS variables

  22. Check if supported /* CSS */ @supports ( (--a: 0))

    { /* supported */ } @supports ( not (--a: 0)) { /* not supported */ } // JavaScript const isSupported = window.CSS && window.CSS.supports && window.CSS.supports('--a', 0); if (isSupported) { /* supported */ } else { /* not supported */ } <!-- HTML (in case of older browsers support) --> <link href="without-css-custom-properties.css" rel="stylesheet" type="text/css" media="all" /> <script> if(isSupported){ removeCss('without-css-custom-properties.css'); loadCss('css-custom-properties.css'); // + apply some application enhancements // using the custom properties } </script>
  23. Current constraints Custom CSS Properties by default are: not typed

    in result - not animatable
  24. Why animation is important

  25. Why typed CSS properties and values are important support and

    validation in IDE syntax highlight performance Browser support DevTools support linters, compilers...
  26. Introducing CSS property types? Property Value definition field Example value

    text-align left | right | center | justify center padding-top <length> | <percentage> 5% border-width [ <length> | thick | medium | thin ]{1,4} 2px medium 4px
  27. Houdini group CSS Properties & Values API CSS Typed OM

    CSS Parser API Font Metrics API Worklets CSS Layout API CSS Paint API CSS Animation Worklet API CSS Properties & Values API CSS Typed OM
  28. CSS Typed OM spec // CSS -> JS const map

    = document.querySelector('.example').styleMap; console.log( map.get('font-size') ); // CSSSimpleLength {value: 12, type: "px", cssText: "12px"} // JS -> JS console.log( new CSSUnitValue(5, "px") ); // CSSUnitValue{value:5,unit:"px",type:"length",cssText:"5px"} // JS -> CSS // set style "transform: translate3d(0px, -72.0588%, 0px);" elem.outputStyleMap.set('transform', new CSSTransformValue([ new CSSTranslation( 0, new CSSSimpleLength(100 - currentPercent, '%'), 0 )])); behind the “Experimental Web Platform features” flag in
  29. CSS Properties and Values API CSS.registerProperty({ name: "--stop-color", syntax: "<color>",

    inherits: false, initialValue: "black" });
  30. Why it's important Without With

  31. "syntax" of CSS properties Default: "*" Supported Values: "<length>" "<number>"

    "<percentage>" "<length-percentage>" "<color>" "<image>" "<url>" "<integer>" "<angle>" "<time>" "<resolution>" "<transform-function>" Examples: "<length> | <percentage>" both, but not calc() combinations "<length-percentage>" both + calc() combinations of both types "big | bigger" accepts either value "<length>+" accepts a list of length values
  32. And... CSS Custom Properties should be animatable since they are

    provided with types? Let's try!
  33. Native CSS Animation

  34. Web animation API element.animate([ {cssProperty: 'fromValue'}, {cssProperty: 'toValue'} ], {

    duration: timeInMs, fill: 'none|forwards|backwards|both', delay: delayInMs, easing: 'linear|easy-in|cubic-bezier()...', iterations: iterationCount|Infinity }); rabbit.animate( [ { transform: "translateX(0)" }, { transform: "translateX(115px)" } ], { duration: 1000, // ms fill: "forwards", // stay at the end easing: "easy-in-out" } ); @keyframes rabbitMove { 0% { transform: translateX(0); } 100% { transform: translateX(115px); } } .rabbit { animation: rabbitMove 1s ease-in-out; animation-fill-mode: forwards; }
  35. Native JavaScript animation

  36. CSS/JS/Browser CSS JavaScript Browser CSS Custom Properties CSS Property Types

    Typed OM API CSSOM ?
  37. Browser rendering Pixel rendering pipeline Internal browser engine

  38. Worklets Houdini’s goal is to expose browser APIs to allow

    web developers to hook up their own code into the CSS engine. It’s probably not unrealistic to assume that some of these code fragments will have to be run every. single. frame. - similar to Web and Service Workers - have a separate thread - don't interact with DOM directly - limited API -> very performant - use the JS additions, essentially- ECMAScript 2015+ Classes with named methods - triggered when needed and possible
  39. image credit

  40. Paint Worklet The paint stage of CSS is responsible for

    painting the background, content and highlight of a box (based on the box’s size and computed style). The API allows to paint a part of a box in response to size / computed style changes with an additional <image> function. - background-image - border-image - list-style-image - content - cursor Custom image can be paint on every browser paint action. Applicable for:
  41. Paint Worklet example /* CSS */ .multi-border {--border-top-width: 10; border-image:

    paint(border-colors);} // JS CSS.registerProperty({ name: '--border-top-width', syntax: '<number>', inherits: false, initialValue: '0', }); // add a Worklet paintWorklet.addModule('border-colors.js'); // WORKLET "border-colors.js" registerPaint('border-colors', class BorderColors { static get inputProperties() { return ['--border-top-width']; } paint(ctx, size, styleMap) { const elWidth = size.width; const topWidth = styleMap.get('--border-top-width').value; // draw a border ctx.fillStyle = 'magenta'; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(elWidth, 0); ctx.lineTo(elWidth, topWidth); ctx.lineTo(0, topWidth); ctx.lineTo(0, 0); ctx.fill(); } });
  42. Multiple border polyfill using Paint Worklet

  43. Browsers are smart and trigger paint only when and where

    needed ( ) demo
  44. Layout Worklet - to create own layout algorithms - having

    access to set constraints, behaviour, boundaries - interact blocks, fragments and even text /* CSS */ .center { display: layout(centering); } CSS Layout API
  45. /* CSS */ .photos { display: layout(masonry); } // JS

    (layout worklet) registerLayout('masonry', class extends Layout { *layout(/*...*/) {/*...*/} });
  46. Animation Worklet Sticky Header implemented in the stable version of

    Chrome using Houdini task force achievements In-sync with the compositor thread on a “best effort” basis (tried on every frame, but may be as requestAnimationFrame) - animations and mostly scroll effects: - sticky elements - smooth scroll animations - scroll up bar Use cases is available Polyfill
  47. Compositor only properties Pixel rendering pipeline Changing does not trigger

    any geometry changes or painting Carried out by the compositor thread with the help of the GPU. Property changes which can be handled by the compositor alone opacity transform
  48. Scroll position indicator using Animation worklet

  49. Animation Worklet- CSS/JS parts /* CSS */ .scroll-position { /*

    shifted to the left */ left: -100%; /* CSS animator directive to link up elements to an animator instance */ --animator: scroll-position-worklet scrollerElementReference; } /* JS (add a Worklet module) */ animationWorklet.addModule('worklet.js');
  50. Animation Worklet itself /* Animators are classes that are run

    in the worklet and get to control certain attributes of DOM elements. */ registerAnimator('scroll-position-worklet', class ScrollPositionAnimator { static get elements() { return [{ // linked element name name: 'scrollerElementReference', // properties the animator needs to read to compute the animation // animator can be skipped if input props not changed since last frame inputProperties: [], // Output properties are properties that the animator might mutate // "opacity" and/or "transform" outputProperties: ['transform'] }] }; static get timelines() { // timeline options list }; animate(elementMap, timelines) { // Animation frame logic goes here }; }
  51. Timeline and animate registerAnimator('scroll-position-worklet', class { // listen global vertical

    scroll static get timelines() { return [{type: 'scroll', options: {orientation: 'vertical'}}] }; animate(elementMap, timelines) { // current scroll position in range [0-100%] (of page) const scrollPosition = parseFloat(timelines[0].currentTime) * 100; elementMap.get('scrollerElementReference').forEach(elem => { // set CSS "transform:translate3d(`${scrollPosition}%`, 0, 0)"; elem.outputStyleMap.set('transform', new CSSTransformValue([ new CSSTranslation( new CSSSimpleLength(scrollPosition, '%'),0,0 )])); }); }}
  52. Animation Worklet result

  53. "endScrollOffset" option static get timelines() { return [{type: 'scroll', options:

    { orientation: 'vertical', // set scroll position to 100% at 375px endScrollOffset: '375px' }}]};
  54. New Twitter Header effect using Animation Worklet

  55. Consclusions Bright Reality - - (when available) for performance improvements

    and progressive enhancement Bright Future - experiment with using the - play with in Chrome - stay tuned with and ( , , , API etc.) start using CSS Custom Properties register Custom Properties from JS Animation Worklet polyfill Paint Worklet CSS Houdini Group specs Worklets Layout Font Metrics Typed OM
  56. Thank you! Serg Hospodarets @malyw