Houdini

programming in CSS

CSS

.CSS { } 

.CSS { } 

CSS Houdini

.CSS-Houdini { } 

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. - CSS Houdini

.CSS-Houdini { } 

Paint API

Typed OM API

Properties & Values API

Animation Worklet

Layout API

Worklets

Font Metrics API

Box Tree API

Parser API

.CSS-Houdini { } 

Extends CSS

Worklets

    .Extends-CSS { } 

    .Extends-CSS .Worklets { }

Worklet connects to the browser engine

Is a Javascript Module

Without control over execution

It has no impact on performance

    .Extends-CSS .Worklets { }

if ('paintWorklet' in CSS) {
    CSS.paintWorklet.addModule('PlaceholderBoxPainter.js');
}
class PlaceholderBoxPainter {
    paint(ctx, size) {
        // Magic 🎩
    }
}

registerPaint('placeholder-box', PlaceholderBoxPainter);

worklet

js

    .Extends-CSS .Worklets { }

Paint API

    .Extendes-CSS { } 

    .Extends-CSS .Paint-API{ }

if ('paintWorklet' in CSS) {
    CSS.paintWorklet.addModule('PlaceholderBoxPainter.js');
}
@supports (background: paint(slanted-bg)) {
  .el span {
    background: paint(slanted-bg);
  }
}

css

js

class SlantedBg {
  constructor () {
    this.hue = 110;
  }
  paint (ctx, geom, props, args) {
    // draw random colors
    ctx.fillStyle = 'hsl(' + this.hue + ', 100%, 50%)';
    this.hue += 10;

    ...
  }
}

registerPaint('slanted-bg', SlantedBg)

worklet

registerPaint('slanted-bg', class {
  constructor () {
    this.hue = 110;
  }
  paint (ctx, geom, props, args) {
    // draw random colors
    ctx.fillStyle = 'hsl(' + this.hue + ', 100%, 50%)';
    this.hue += 10;

    ...
  }
})

    .Extends-CSS .Paint-API{ }

    .Extends-CSS .Paint-API{ }

Houdini-Paint-API-Circles

    .Extends-CSS .Paint-API{ }

Houdini-Paint-API-Circles-props

    .Extends-CSS .Paint-API{ }

Houdini-Paint-API-Circles-args

    .Extends-CSS .Paint-API{ }

Houdini-Paint-API-Circles-props-JS

Properties & Values API

    .Extends-CSS { } 

    .Extends-CSS .Properties-Values-API { } 

CSS.registerProperty({
  name: '--line-with',
  syntax: '<number>',
  inherits: false,
  initialValue: 1
});

js

CSS Properties Syntax

'<length>' accepts length values.
'<length> | <percentage>' accepts lengths, percentages, percentage calc expressions, and length calc expressions, but not calc expressions containing a combination of length and percentage values.​
'<length-percentage>' accepts all values that "<length> | <percentage>" would accept, as well as calc expressions containing a combination of both length..
'big | bigger | BIGGER' accepts the ident "big", or "bigger", or  "BIGGER".
'<length>+' accepts a space-separated list of length values.

'<length>'
'<number>'
'<percentage>'
'<length-percentage>'
'<color>'
'<image>'
'<url>'
'<integer>'
'<angle>'
'<time>'
'<resolution>'
'<transform-function>'
'<custom-ident>'

    .Extends-CSS .Properties-Values-API { } 

    .Extends-CSS .Properties-Values-API { } 

    .Extends-CSS .Properties-Values-API { } 

    .Extends-CSS .Properties-Values-API { } 

@property --theme {
  syntax: '<color>+';
  initial-value: #fff;
  inherits: true;
}
if ("registerProperty" in CSS) {
  CSS.registerProperty({
    name: "--theme",
    syntax: "<color>+",
    initialValue: "#fff",
    inherits: true
  });
}

input

output

    .Extends-CSS .Properties-Values-API { } 

@property --highlight-color {
  inherits: true;
  initial-value: red;
  syntax: "<color>";
}

@property --gap-spacing {
  inherits: false;
  initial-value: 1em;
  syntax: "<length-percentage>";
}
[
  {
    "name": "--highlight-color",
    "inherits": true,
    "initialValue": "red",
    "syntax": "<color>"
  },
  {
    "name": "--gap-spacing",
    "initialValue": "1em",
    "syntax": "<length-percentage>"
  }
]
import properties from './styles.css.properties.json';

if (window.CSS && CSS.registerProperty) {
  for (const descriptor of properties) {
    CSS.registerProperty(descriptor);
  }
}

input

output

js

    .Extends-CSS .Properties-Values-API { } 

Typed OM API

    .Extends-CSS { } 

    .Extends-CSS .Typed-OM-API { } 

The Type OM API is an extension of the existing CSS object model (CSSOM) that exposes CSS values as typed JavaScript objects, rather than a simple string of text as they are today.

With Type OM API, CSS values are now members of a new base class CSSStyleValue.

CSSUnitValue

CSSKeywordValue

CSSPositionValue

CSSImageValue

CSSTransformValue

CSSMathValue

el.style.opacity = 0.5;
window.getComputedStyle(el).opacity === "0.5" // Strings!

CSSOM

el.attributeStyleMap.set('opacity', 0.5);
el.computedStyleMap().get('opacity').value // 0.5

Typed OM

    .Extends-CSS .Typed-OM-API { } 

calc(1px - 2 * 3em)


new CSSMathSum(
  CSS.px(1),
  new CSSMathNegate(
    new CSSMathProduct(2, CSS.em(3))
  )
);

Nested expressions

    .Extends-CSS .Typed-OM-API { } 

el.attributeStyleMap.set('font-size', CSS.em(2));
el.attributeStyleMap.get('font-size');
// CSSUnitValue { value: 2, unit: 'em' }


el.attributeStyleMap.set('opacity', CSS.number(.5));
el.attributeStyleMap.get('opacity');
// CSSUnitValue { value: 0.5, unit: 'number' }

attributeStyleMap

    .Extends-CSS .Typed-OM-API { } 

.foo {
  background-position: center bottom 10px;
  transform: translateX(1em) rotate(50deg) skewX(10deg);
  vertical-align: baseline;
  width: calc(100% - 3em);
}

css

    .Extends-CSS .Typed-OM-API { } 

const cs = document.querySelector('.foo').computedStyleMap();

cs.get('vertical-align');
// CSSKeywordValue {
//  value: 'baseline',
// }

cs.get('background-position').x;
// CSSUnitValue {
//   value: 50,
//   unit: 'percent',
// }

cs.get('background-position').y;
// CSSMathSum {
//   operator: 'sum',
//   values: CSSNumericArray {
//     0: CSSUnitValue { value: -10, unit: 'px' },
//     1: CSSUnitValue { value: 100, unit: 'percent' },
//   },
// }

cs.get('width');
// CSSMathSum {
//   operator: 'sum',
//   length: 2,
//   values: CSSNumericArray {
//     0: CSSUnitValue { value: -90, unit: 'px' },
//     1: CSSUnitValue { value: 100, unit: 'percent' },
//   },
// }

cs.get('transform');
// CSSTransformValue {
//   is2d: true,
//   length: 3,
//   0: CSSTranslate {
//     is2d: true,
//     x: CSSUnitValue { value: 20, unit: 'px' },
//     y: CSSUnitValue { value: 0, unit: 'px' },
//     z: CSSUnitValue { value: 0, unit: 'px' },
//   },
//   1: CSSRotate {
//     is2d: true,
//     angle: CSSUnitValue { value: 50, unit: 'deg' },
//     x: CSSUnitValue { value: 0, unit: 'number' },
//     y: CSSUnitValue { value: 0, unit: 'number' },
//     z: CSSUnitValue { value: 1, unit: 'number' },
//   },
//   2: CSSSkewX {
//     is2d: true,
//     ax: CSSUnitValue { value: 10, unit: 'deg' },
//   },
// }

computedStyleMap

    .Extends-CSS .Typed-OM-API { } 

Animation Worklet

    .Extends-CSS { } 

    .Extends-CSS Animation-Worlet { } 

Layout API

    .Extends-CSS { } 

    .Extends-CSS  .Layout-API {}

Support

Conclusions

    .Conclusions { } 

Polyfills

This is not for me

Houdini is NOT like a Babel for CSS

Free will

🤪

I see another talk...

CSS

is

Awesome

Resources

    .Resources { } 

⭐️ are welcome 😊

Developer Advocate 🥑
at
@SchibstedSpain

 

Co-Founder & UI Engineers

at @sublime_codes

Joan León

.Hello {

}

#Javascript

#CSS

#PostCSS

#Animation

#SVG

SUI Components

@carlosvillu

@midudev

.Talk::after {

}

content: "Any question?";

.Thanks {

}

Houdini: programming in CSS

By Joan León

Houdini: programming in CSS

Codemotion Madrid 2018: CSS siempre se ha considerado la parte menos controlable, complicada, mágica y en ocasiones algo aleatoria. Houdini nos ofrece un conjunto de APIs y herramientas Javascript que nos dan la posibilidad de extender CSS para acceder al proceso de diseño y estilo del motor de renderizado del navegador.

  • 2,661