Houdini

programando en 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 { } 

Extendiendo CSS

chrome://flags/#enableexperimental-web-platform-features

    .Extendiendo-CSS { } 

Worklets

    .Extendiendo-CSS { } 

    .Extendiendo-CSS .Worklets { }

Un worklet es básicamente lo que vamos a utilizar para conectar con el motor de CSS del navegador.

Se trata de un módulo en Javascript donde definiremos la "magia" que podremos usar en CSS.

No tenemos control sobre la ejecución de los worklets, el motor de renderizado es quien hace las llamadas cuando es necesario.

No tiene impacto en el rendimiento, porque cada worklet funciona dentro de su propio hilo de ejecución.

    .Extendiendo-CSS .Worklets { }

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

registerPaint('placeholder-box', PlaceholderBoxPainter);

worklet

js

    .Extendiendo-CSS .Worklets { }

(async () => {
  if (typeof CSS === 'undefined' || !('paintWorklet' in CSS)) {
    document.querySelector('.no-dice').hidden = false;
    return;
  }

  const paintModule = URL.createObjectURL(new Blob(
    [ document.querySelector('[type="houdini/paint-worklet"]').textContent],
    { type: "text/javascript" }
  ));

  await CSS.paintWorklet.addModule(paintModule);

})();

js

    .Extendiendo-CSS .Worklets { }

Paint API

    .Extendiendo-CSS { } 

.Extendiendo-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, properties, 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, properties, args) {
    // draw random colors
    ctx.fillStyle = 'hsl(' + this.hue + ', 100%, 50%)';
    this.hue += 10;

    ...
  }
})

.Extendiendo-CSS .Paint-API{ }

.Extendiendo-CSS .Paint-API{ }

.Extendiendo-CSS .Paint-API{ }

Properties & Values API

    .Extendiendo-CSS { } 

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

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

js

CSS Properties Syntax

'<length>' Valores de longitud
'<length> | <percentage>' Ambas combinaciones, pero no calc ()
'<length-percentage>' Ambas combinaciones + calc () de ambos tipos
'big | bigger | BIGGER' Acepta cualquier valor
'<length>+' Acepta una lista de valores de longitud

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

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

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

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

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

    .Extendiendo-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
  });
}

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

input

output

@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);
  }
}

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

input

output

js

Typed OM API

    .Extendiendo-CSS { } 

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

La Type OM API es una extensión del modelo de objetos CSS existente (CSSOM) que expone los valores CSS como objetos JavaScript tipados, en lugar de una simple cadena de texto como lo son hoy.

Con Type OM API, los valores CSS son ahora miembos de una nueva clase base CSSStyleValue. Existen varias subclases de las CSSStyleValue que describen con más precisión un tipo de valor CSS.

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

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

new CSSMathSum(CSS.vw(100), CSS.px(-10)).toString();
// "calc(100vw + -10px)"

new CSSMathNegate(CSS.px(42)).toString() // "calc(-42px)"

new CSSMathInvert(CSS.s(10)).toString() // "calc(1 / 10s)"

new CSSMathProduct(CSS.deg(90), CSS.number(Math.PI/180)).toString();
// "calc(90deg * 0.0174533)"

new CSSMathMin(CSS.percent(80), CSS.px(12)).toString();
// "min(80%, 12px)"

new CSSMathMax(CSS.percent(80), CSS.px(12)).toString();
// "max(80%, 12px)"

Valores matemáticos

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

calc(1px - 2 * 3em)


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

Expresiones anidadas

    .Extendiendo-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

    .Extendiendo-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

    .Extendiendo-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

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

Animation Worklet

    .Extendiendo-CSS { } 

    .Extendiendo-CSS Animation-Worlet { } 

Layout API

    .Extendiendo-CSS { } 

    .Extendiendo-CSS  .Layout-API {}

Soporte

Conclusiones

    .Conclusiones { } 

Polylills

Esto no es para mí

Houdini NO es como Babel para CSS

Libre albedrío

🤔

Visualizo una charla...

CSS Mola!

Recursos

.Recursos { } 

Developer Advocate 🥑
at
@SchibstedSpain

 

Co-Founder & UI Engineers

at @sublime_codes

Joan León

.Hola {

}

#Javascript

#CSS

#PostCSS

#Animation

#SVG

SUI Components

.Gracias {

}

Houdini: programando en CSS

By Joan León

Houdini: programando en CSS

JSDay Canarias 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,225