Getting Reactive with CSS

David Khourshid · @davidkpiano

CSS is not powerful

but that's a good thing.

What if web apps

felt native?

What if interactive web UIs

were easier to make

with CSS?

CSS

IS

AWESOME

CSS

IS

AW...

FUL

CSS

IS

FUN

CTIONAL

CSS Variables

Functional & Reactive

CSS in JS

React, Angular, Vue

There are only two hard things
in computer science:

  • Naming things

  • Vertically centering things

CSS-Modules, etc:

.button {
  /* ... */
}
.Anklickbar-Knopfart {
  /* ... */
}

<DIV ID="oDiv"
  STYLE="background-color: #CFCFCF; position: absolute; 
         left: expression(document.body.clientWidth / 2 - oDiv.offsetWidth / 2);
         top: expression(document.body.clientHeight / 2 - oDiv.offsetHeight / 2)">
  Example DIV
</DIV>

CSS Expressions (IE7)

<style type="text/javascript">
tags.H1.color = "red";
tags.p.fontSize = "20pt";
with (tags.H3) {
    color = "green";
}
with (tags.H2) {
    color = "red";
    fontSize = "16pt";
    marginTop = "4cm";
}
</style>

JavaScript Style Sheets (Netscape 4)

.box {
  --delta-x: 150px;
}

CSS

.box {
  --delta-x: 150px;
  transform: translateX(
    var(--delta-x));
}

CSS

.box {
  --delta-x: 150;
  transform: translateX(
    calc(var(--delta-x) * 1px));
}

CSS

.box {
  transform:
    translateX(calc(var(--delta-x) * 1px));
}

const box = document.querySelector('#box');
const hBox = new Hammer(box);

hBox.on('panleft panright', (e) => {
  box.style
    .setProperty('--delta-x', e.deltaX);
});

hBox.on('panend', () => {
  box.style
    .setProperty('--delta-x', 0);
});

CSS

JAVASCRIPT

.box {
  --not-panning: calc(-1 * var(--panning) + 1);
  transition:
    transform
    calc(var(--not-panning) * .6s)
    ease-out;
  transform:
    translateX(calc(var(--delta-x) * 1px))
    rotate(calc(var(--delta-x) * .1deg));
}

const box = document.querySelector('#box');
const hBox = new Hammer(box);

hBox.on('panleft panright', (e) => {
  box.style
    .setProperty('--delta-x', e.deltaX);
  box.style
    .setProperty('--panning', 1); // true
});

hBox.on('panend', () => {
  box.style
    .setProperty('--delta-x', 0);
  box.style
    .setProperty('--panning', 0); // false
});

CSS

JAVASCRIPT

calc(-1 * var(--panning) + 1)
-1 * 1 + 1 = 0
-1 * 0 + 1 = 1

sudo npm install wifi

Why CSS Variables?

Media Queries

:root {
  --pan-fraction: 1;
  --duration: 0.3s;
}

@media (max-width: 700px) {
  :root {
    --pan-fraction: 0.5;
  }
}

@media (prefers-reduced-motion) {
  :root {
    --duration: 0s;
  }
}

.card {
  transform: translateX(calc(
    var(--pan-fraction)
    * var(--delta-x)
    * 1px
  ));
  transition-duration: var(--duration);
}

Pseudoselectors

.box {
  --progress-x:
    calc(var(--delta-x, 0) / 100);

  transform:
    translateX(calc(var(--delta-x, 0));
}

.box::before {
  content: 'Nicht';
  color: red;
  opacity:
    calc(var(--progress-x) * -1));
}

.box::after {
  content: 'Jawohl';
  color: green;
  opacity: var(--progress-x);
}

Performance

Inspection

What if

you wanted to

react

What if

to any event

at any time

you wanted to

react

What if

to any event

at any time

to create rich, interactive UIs

you wanted to

react

What if

to any event

at any time

to create rich, interactive UIs

in an expressive, declarative way?

you wanted to

react

Functional Reactive Animations

(Continuous) events

Discrete changes

A "reactive animation" is one involving discrete changes, due to events.

By allowing programmers to express the "what" of an interactive animation, one can hope to then automate the "how" of its presentation.

[

]

. . .

Value

Array

Promise

Observable

3s

1s

2s

3.5s

Observables with RxJS


const box = document.querySelector('.box');

const mouseMove$ = Rx.Observable
  .fromEvent(box, 'mousemove')
  .map(e => ({
    x: e.clientX,
    y: e.clientY
  });
const hBox = new Hammer(box);

const pan$ = Rx.Observable
  .fromEventPattern(handler =>
    hBox.on('panleft panright', handler))
  .map(e => ({
    deltaX: e.deltaX,
    deltaY: e.deltaY
  });

Rx.Observable.fromEvent()

  • ​DOM element

  • event name

Rx.Observable.fromEventPattern()

  • ​function that adds handler

  • function that removes handler

ball$.map(toSquare)
ball$.delay(300)
ball$.filter(isRed)
ball$.scan((a, b) => a + b)

1

1

1

5

2

2

1

1

2

3

8

10

12

13

Marble Diagrams

Observaballs







observable$.subscribe((value) => {
  // do anything with the value
});


pan$.subscribe((value) => {
  const { deltaX } = value;

  box.style
    .setProperty('--delta-x', deltaX);
});

Subscribing to Observables

If you can read this, the WiFi is a little spotty

please WiFi work 🙏

🔜

oh my god

why did I make so many demos

JavaScript

CSS

JavaScript

CSS

JavaScript

CSS

Any source

Any medium

Material Motion

What will you make with CSS Variables?

Thank you CSSConfEU!

Getting Reactive with CSS

By David Khourshid

Getting Reactive with CSS

CSSConfEU 2017

  • 12,599

More from David Khourshid