Vincent De Oliveira · @iamvdo
Allô, c’est @iamvdo
 
        Pour CSS ?
Créer des polyfills CSS
Il est aujourd’hui difficile impossible de polyfiller CSS, même avec JS.
https://philipwalton.com/articles/the-dark-side-of-polyfilling-css/
Créer ses propres effets graphiques
Créer ses propres extensions au langage
En un mot
Innover
Bienvenue Houdini!
* Plus ou moins (en fonction des navigateurs)
Houdini c’est du 
JS-in-CSS
Ou plutôt du CSS-by-JS
Peut-on utiliser Houdini ?
Amélioration progressive FTW
Attention, peinture fraiche !
Tout ce qui est présenté ici peut ne plus fonctionner du jour au lendemain ¯\_(ツ)_/¯
CSSOM++
// CSSOM
el.style.width = '50px'
el.style.setProperty('width', '50px')
el.style.setProperty('transform', 'translate(' + x + 'px, ' + y + 'px)')// Typed OM
el.attributeStyleMap.set('width', '50px')
el.attributeStyleMap.set('width', CSS.px(50))
el.attributeStyleMap.set('transform', new CSSTranslate(CSS.px(x), CSS.px(y)))
el.computedStyleMap().get('width') // CSSUnitValue {value: 50, unit: 'px'}
let [x, y] = [10, 10];
let transform = new CSSTranslate(CSS.px(x), CSS.px(y))
transform.x.value = 50
transform.toString() // "translate(50px, 10px)"
// Parse CSS
let css = CSSNumericValue.parse('42.0px') // {value: 42, unit: 'px'}
let css = CSSStyleValue.parse('transform', 'translate3d(10px,10px,0) scale(0.5)');
/*
CSSTranslateValue {
  0: CSSTranslate {
    is2D: false
    x: CSSUnitValue {value: 10, unit: 'px'}
    y: CSSUnitValue {value: 10, unit: 'px'}
    z: CSSUnitValue {value: 10, unit: 'px'}
  }
  1: CSSScale {
    is2D: true
    x: CSSUnitValue {value: 0.5, unit: 'number'}
    y: CSSUnitValue {value: 0.5, unit: 'number'}
    z: CSSUnitValue {value: 1, unit: 'number'}
  }
  is2D: false
}
*/
Typed OM == Le socle pour Houdini !
Pas spécifique à Houdini
.el {
  box-shadow: 0 3px 3px rgba(0,0,0,.75);
}
.el:hover {
  box-shadow: 0 15px 10px rgba(0,0,0,.75);
}/* Étendre CSS: propriétés raccourcies */
.el {
  box-shadow: var(--box-shadow-x, 0) var(--box-shadow-y, 3px)
              var(--box-shadow-blur, 3px)
              var(--box-shadow-color, rgba(0,0,0,.75));
}
.el:hover {
  --box-shadow-y: 15px;
  --box-shadow-blur: 10px;
}.el {
  text-shadow: 
    var(--x, 0)
    var(--y, 3px)
    var(--blur, 3px)
    var(--color, rgba(0,0,0,.75));
}// Modifier en JS
el.addEventListener('mousemove', e => {
  el.attributeStyleMap.set('--x', e.offsetX)
  el.attributeStyleMap.set('--y', e.offsetY)
  el.attributeStyleMap.set('--blur', blur)
})Properties & Values API
// Déclarer notre propre propriété animable
CSS.registerProperty({
  name: '--box-shadow-blur',
  syntax: '<length>',
  inherits: false,
  initialValue: '0px'
}).el {
  transition-property: --box-shadow-blur, --box-shadow-y;
  transition-duration: .45s;
}
.el:hover {
  --box-shadow-y: 15px;
  --box-shadow-blur: 10px;
}Créatif Animer un dégradé
Worklet
CSS.paintWorklet.addModule('paint.js')registerPaint()paint()registerPaint('circle', class {
  paint(ctx, geom, props, args) {
    // Get the center point and radius
    const x = geom.width / 2;
    const y = geom.height / 2;
    const radius = Math.min(x, y);
    // Draw the circle
    ctx.fillStyle = 'deeppink';
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * Math.PI);
    ctx.fill();
  }
}.el {
  background-image: paint(circle);
}registerPaint('circle-props', class {
  static get inputProperties() { return ['--circle-color']; }
  paint(ctx, geom, props, args) {
    // Determine the center point and radius.
    const x = geom.width / 2;
    const y = geom.height / 2;
    const radius = Math.min(x, y);
    // Draw the circle
    ctx.fillStyle = props.get('--circle-color').value;
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * Math.PI);
    ctx.fill();
  }
}.el {
  --circle-color: deepskyblue;
  background-image: paint(circle-props);
}registerPaint('circle-ripple', class { static get inputProperties() { return [ '--circle-color', '--circle-radius', '--circle-x', '--circle-y' ]} paint(ctx, geom, props, args) { const x = props.get('--circle-x').value; const y = props.get('--circle-y').value; const radius = props.get('--circle-radius').value; } }el.addEventListener('click', e => { el.classList.add('animating'); el.attributeStyleMap.set('--circle-x', e.offsetX); el.attributeStyleMap.set('--circle-y', e.offsetY); });
.el {
  --circle-radius: 0;
  --circle-color: deepskyblue;
  background-image: paint(circle-ripple);
}
.el.animating {
  transition: --circle-radius 1s,
              --circle-color 1s;
  --circle-radius: 300;
  --circle-color: transparent;
}Créatif Dessin artistique
Créatif Arrière-plans Dégradé des coins
Polyfill Arrière-plans corner-shape
Polyfill Créatif Arrière-plans background-filter / opacity / rotate
Créatif Arrière-plans Surligneur
Créatif Bordures Flèche d’infobulle
Créatif Bordures Bordures dessinées
Worklet réutilisable et jetable ?
Légèrement contournable: graine aléatoire (seed random)
Créatif Masques Bulles aléatoires
Créatif Masques Grille irrégulière
Créatif 🤯 JS-in-CSS
Worklet
CSS.layoutWorklet.addModule('layout.js')registerLayout()layout()registerLayout('center', class {
  *layout(children, edges, constraintSpace, props) {
    let childFragments = [];
    for(let child of children) {
      let childFragment = yield child.layoutNextFragment();
      let childHalfSize = childFragment.inlineSize / 2;
      let parentHalfSize = constraintSpace.fixedInlineSize / 2;
      childFragment.inlineOffset = parentHalfSize - childHalfSize;
      childFragments.push(childFragment);
    }
    return { childFragments };
  }
});.parent {
  display: layout(center);
}registerLayout('position', class {
  static get inputProperties() { return ['--position-x']; }
  *layout(children, edges, constraintSpace, props) {
    let posx = props.get('--position-x').value;
    let pos = constraintSpace.fixedInlineSize / (100 / posx);
  }
});.parent {
  display: layout(position);
  --position-x: 50%;
}registerLayout('position', class { static get inputProperties() { } *layout(children, edges, constraintSpace, props) { } });parent.addEventListener('click', e => { parent.classList.add('animating'); let pos = 100 * e.offsetX / parentWidth; parent.attributeStyleMap.set('--position-x', CSS.percent(pos)); });
.parent {
  display: layout(position);
  --position-x: 50%;
}
.parent.animating {
  transition: --position-x 1s;
}Créatif Masonry
Polyfill Android RelativeLayout
Créatif SVG Path
Worklet
CSS.animationWorklet.addModule('anim.js')registerAnimator()new WorkletAnimation()Basé sur une idée de Scott Kellum
registerAnimator('simple', class { animate(currentTime, effect) { effect.localTime = currentTime; } });.cube { --angle: 0; transform: rotateX(var(--angle)) rotateZ(45deg) rotateY(-45deg); }
new WorkletAnimation('simple',
  new KeyframeEffect(el, [
      { '--angle': 0 },
      { '--angle': '1turn' }
    ],
    { duration: 1 }
  ),
  new ScrollTimeline({
    scrollSource: scrollElement,
    timeRange: 1
  }),
).play();registerAnimator('parallax', class {
  constructor(options = {}) {
    this.factor = options.factor || 1;
  }
  animate(currentTime, effect) {
    effect.localTime = currentTime * this.factor;
  }
});for (let i = 0; i < els.length; i++) {
  new WorkletAnimation('parallax',
    new KeyframeEffect(els[i], [
        { transform: new CSSTranslate(0, 0) },
        { transform: new CSSTranslate(0, CSS.px(scrollHeight)) }
      ], { duration: 1 }
    ),
    new ScrollTimeline({
      scrollSource: scrollElement,
      timeRange: 1
    }),
    { factor: (i / 2) * (1.2 - 1) + 1 }
  ).play();
}animation-timeline: scroll()CSS Parser, Render Tree, Font Metrics
Tout devient possible
Enfin, peut-être…
element(), backdrop-filter, CSS Shaders 😍, etc.paint() pas sur les liens