Joan León PRO
⚡️ Web Performance Consultant | Speaker | Staff Frontend Engineer at @AdevintaSpain | @GoogleDevExpert in #WebPerf | @cloudinary Ambassador
.CSS { }
.CSS { }
.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 { }
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
'<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 {}
.Conclusions { }
Polyfills
This is not for me
Houdini is NOT like a Babel for CSS
Free will
🤪
I see another talk...
.Resources { }
⭐️ are welcome 😊
Joan León
#Javascript
#CSS
#PostCSS
#Animation
#SVG
@carlosvillu
@midudev
By Joan León
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.