Web dev & architect
at Antreem
introduced 2 ways JavaScript could extend CSS:
.indicator {
width: 100%;
color: expression(this.clientWidth > 400 ? 'red' : 'blue');
}
<!-- boxsizing.htc -->
<component lightWeight="true">
<attach event="onpropertychange" onevent="checkPropertyChange()" />
<attach event="ondetach" onevent="restore()" />
<script type="text/javascript">//<![CDATA[
function checkPropertyChange(){ ... }
...
//]]></script>
</component>
CSS is evolving:
We're still like this:
A project inside the W3C to expose parts of the CSS engine to web developers
Think of Workers, but lightweight:
navigator
, location
…fetch()
import
: it's in the language, but always rejectspostMessage
localStorage
, IndexedDB, caches…setTimeout
.delaunay {
background-color: #f60;
background-image: paint(delaunay);
}
// delaunay.js class DelaunayModule {
paint(context, geometry, properties) { ... }
} static get inputProperties() { return [ '--delaunay-point-area', ... ]; }
registerPaint('delaunay', DelaunayModule);
// main.js CSS.paintWorklet.addModule('./delaunay.js').then(...);
paint(context, geometry, properties)
context
is a stripped-down CanvasRenderingContext2D
geometry
has just two numeric properties:↔ width
and ↕ height
👉 properties
is a Map
of the CSS properties given by inputProperties
background-image
border-image
mask-image
list-style-image
::before
and ::after
url()
cursor
element.style.fontSize = '20px';
element.attributeStyleMap.set('font-size', CSS.px(20));
and other stuff
(ok, that's 4)
const width = someComputation();
element.style.width = `${width}px`;
const width = someComputation();
element.attributeStyleMap.set('width', CSS.px(width));
What's .attributeStyleMap
?
Map
StylePropertyMap
There are two defined on elements:
.attributeStyleMap
.style
.computedStyleMap()
getComputedStyleMap
, if they didn't fear to be verbose
getComputedStyle()
.computedStyleMap()
also returns clamped/adjusted values
(e.g. for opacity
, or rounded for z-index
and so on)paint(ctx, geom, properties)
StylePropertyMap
too.opacity
or z-index
el.style.opacity += 0.1; // oh noes!
el.attributeStyleMap.set('margin', 'foo'); // 😱
.box {
width: 20em;
width: red;
}
width: red
gets ignored
.box {
--box-width: 20em;
--box-width: red;
width: var(--box-width);
}
--box-width: 20em
that gets ignored!
.box { animation: streeetch 2s infinite; }
@keyframes streeetch {
from { width: 10em; }
to { width: 20em; }
}
.box {
width: var(--box-width);
animation: streeetch 2s infinite;
}
@keyframes streeetch {
from { --box-width: 10em; }
to { --box-width: 20em; }
}
width
is known to be a length, while --box-width
is notCSS.registerProperty({
name: '--box-width',
syntax: '<length>',
initialValue: CSS.px(0),
inherits: false
});
Register properties in CSS 🎉
@property --box-width {
syntax: '<length>';
initial-value: 0px;
inherits: false;
}
Draw images fast in a worklet
Fast and less error-prone values for CSS
Making custom properties first class citizens
registerPaint('ripple', class {
static get inputProperties() {
return [ '--ripple-color', ... ];
}
paint(ctx, { width, height }, props) {
const color = props.get('--ripple-color');
...
ctx.fillStyle = color;
ctx.arc(centerX.value, centerY.value,
radius.value * farthest / 100, 0, Math.PI * 2);
ctx.fill();
}
});
CSS.registerProperty({
name: '--ripple-color',
syntax: '<color>',
initialValue: '#fff0',
inherits: false
});
CSS.registerProperty({
name: '--ripple-radius',
syntax: '<percentage>',
...
});
...
const duration = button.computedStyleMap() .get('--ripple-duration') .to('ms').value;
button.animate([{ '--ripple-color': '#ffffff80', '--ripple-radius': CSS.percent(0) }, { '--ripple-color': '#ffffff00', '--ripple-radius': CSS.percent(100) }], duration);
display: layout(my-layout)
CSS.layoutWorklet.addModule('./my-layout.js')
registerLayout('my-layout', class {
async layout(children, edges, constraints, properties, breakToken) {
😱 }}); async intrinsicSizes(children, edges, properties) { 🤔 } static inputProperties = ['--foo']; static childrenInputProperties = ['--bar']; static layoutOptions = {childDisplay: 'normal', sizing: 'block-like'};
requestAnimationFrame
Can do a lot…
Could do much more!
registerAnimator('my-animator', class {
constructor(options = {}) { this.options = options; }
}); animate(currentTime, effect) { const newTime = bendTheTime(currentTime); effect.localTime = newTime; }
await CSS.animationWorklet.addModule('./my-anim.js');
const effect = new KeyframeEffect(el, frames);
const animation = new WorkletAnimation( 'my-animator', effect );
animation.play();
new Animation(
effect,
animationOptions,
); document.timeline
ScrollTimeline
🎉const timeline = new ScrollTimeline({
scrollSource: document.scrollElement,
orientation: 'vertical',
}); timeRange: 1000
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed ullamcorper morbi tincidunt ornare massa eget. Orci porta non pulvinar neque laoreet suspendisse interdum consectetur. Sed adipiscing diam donec adipiscing tristique risus nec. Cras pulvinar mattis nunc sed blandit libero volutpat sed. Porta non pulvinar neque laoreet. Pulvinar neque laoreet suspendisse interdum consectetur libero id faucibus nisl. Adipiscing at in tellus integer feugiat scelerisque. Orci a scelerisque purus semper. Proin fermentum leo vel orci porta non. In massa tempor nec feugiat nisl pretium fusce id. Tortor posuere ac ut consequat semper viverra. Ac orci phasellus egestas tellus rutrum tellus pellentesque eu tincidunt. Facilisi etiam dignissim diam quis enim lobortis scelerisque fermentum.
Nullam ac tortor vitae purus. A lacus vestibulum sed arcu non odio. Lectus vestibulum mattis ullamcorper velit sed. Tristique sollicitudin nibh sit amet commodo nulla facilisi. Pulvinar mattis nunc sed blandit libero. Nunc pulvinar sapien et ligula ullamcorper malesuada proin libero nunc. Facilisis mauris sit amet massa vitae tortor. Blandit turpis cursus in hac habitasse platea dictumst quisque sagittis. Vel pharetra vel turpis nunc eget lorem dolor sed. Amet justo donec enim diam vulputate ut pharetra sit amet. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus. Sed elementum tempus egestas sed. In tellus integer feugiat scelerisque. Elit sed vulputate mi sit amet mauris commodo quis.
Auctor neque vitae tempus quam. Turpis tincidunt id aliquet risus feugiat in ante metus. Libero nunc consequat interdum varius sit amet mattis. Blandit cursus risus at ultrices. Morbi tristique senectus et netus et. Nulla at volutpat diam ut. Eget est lorem ipsum dolor sit amet consectetur adipiscing elit. Tempus egestas sed sed risus pretium quam. Feugiat nibh sed pulvinar proin gravida hendrerit. Ut enim blandit volutpat maecenas volutpat blandit aliquam. Odio eu feugiat pretium nibh ipsum consequat nisl vel. Elit ut aliquam purus sit amet. Dignissim diam quis enim lobortis scelerisque fermentum. Porttitor leo a diam sollicitudin. Amet massa vitae tortor condimentum lacinia. Dolor sit amet consectetur adipiscing elit duis tristique sollicitudin. Metus aliquam eleifend mi in nulla posuere sollicitudin. Dolor magna eget est lorem ipsum dolor sit amet. Vulputate ut pharetra sit amet aliquam id diam maecenas.
Et molestie ac feugiat sed lectus vestibulum. Ut lectus arcu bibendum at varius vel pharetra vel. Vitae tortor condimentum lacinia quis vel eros donec. Elit sed vulputate mi sit. Vel pharetra vel turpis nunc eget lorem dolor. Massa vitae tortor condimentum lacinia quis vel eros donec. Purus in massa tempor nec. Pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat. Volutpat blandit aliquam etiam erat velit scelerisque in. Enim diam vulputate ut pharetra sit amet aliquam id. Convallis a cras semper auctor neque vitae tempus quam. Pellentesque sit amet porttitor eget dolor. Sollicitudin ac orci phasellus egestas. Ultricies integer quis auctor elit sed vulputate mi sit. Leo in vitae turpis massa sed elementum tempus. Nisi lacus sed viverra tellus. Eget nulla facilisi etiam dignissim diam quis enim lobortis. In nisl nisi scelerisque eu ultrices vitae auctor eu augue. Fames ac turpis egestas integer eget. A pellentesque sit amet porttitor eget.
Dignissim enim sit amet venenatis urna. In nibh mauris cursus mattis molestie a iaculis at. Vitae tortor condimentum lacinia quis vel eros donec ac. Leo duis ut diam quam nulla porttitor massa. Semper eget duis at tellus at urna condimentum mattis pellentesque. Non consectetur a erat nam at lectus urna. Fermentum leo vel orci porta non pulvinar neque. Ornare arcu odio ut sem nulla pharetra. Sit amet nisl suscipit adipiscing bibendum est ultricies integer. Facilisis mauris sit amet massa vitae tortor condimentum lacinia quis. At tellus at urna condimentum mattis pellentesque id nibh. Nec sagittis aliquam malesuada bibendum arcu vitae. Arcu non sodales neque sodales ut etiam sit amet nisl. Vulputate odio ut enim blandit volutpat maecenas. Magna ac placerat vestibulum lectus mauris ultrices eros. Varius quam quisque id diam vel quam elementum pulvinar. Arcu dui vivamus arcu felis bibendum ut tristique. Pharetra diam sit amet nisl suscipit. Blandit aliquam etiam erat velit. Ipsum nunc aliquet bibendum enim facilisis gravida neque convallis a.
InputTimeline
"foo"
be?for (const question of questions) {
await answer(question)
}