Reactive CSS Explained
Tags: javascript, typescript, css, showdev
Published: 2020-08-13 19:56:15

**Edit, I have released the library reactive-css-properties and anyone wanting to use this library from npm should follow those instructions, most notably, no need for async await**

Some of you will remember my popular series on JavaScript enhanced SCSS by setting and getting css variables in JavaScript rather than targeting DOM nodes directly, also this was indirectly controlled from css calling JavaScript using css variables, honestly it's a mad idea which many seemed to love, its my most popular post by about 5000 or so.

Anyways ever since then I have been working on my own stuff using this method, but it got me thinking along more sane lines... if I change a css variable value I want this to be reactive, eg change a variable in devtools and actually respond from JavaScript.

I have tried several times to write this unicorn function and most of the time it failed until today that is!

Ladies and gentleman I give you `unnamed library` (see the console bellow, oh, ps its WIP and wrote in Typescript but don't let that put you off, it will work in JavaScript)

{% codesandbox 9dx6m %}

let's break it down a bit, here's the usage:

```js
import cssProps from "./cssProps";

const { fontFamily } = cssProps();

(async () => {
  const result = await fontFamily("blue");
  result.onChange((detail) => {
    console.log("e", detail);
  });
  await fontFamily("blue");
  await fontFamily("yellow");
})()
```

Let's look at the first interesting thing.
``` js
const { fontFamily, myVar } = cssProps();
```
At first glance this might look kind of normal so I added something else to be desteuctured, both properties of this function's return object should not exist, they should be undefined, and yet... not only do they exist, simply by getting them, they specify the CSS variable name we will soon create. This is a smoke and mirrors trick I devised whilst hacking with JavaScript Proxy. You can see the similarities with React hooks style but mine's not made of arrays mwhaha! 
I wanted to design the interface with the bear minimum boilerplate to get to work with variables pretty quickly, it doesn't even look to out of the ordinary.

What else to know about this line?
You can pass an element to become the root scope of your css variables, by default it's `document.documentElement` but for custom elements where the root is `:host` you can just pass `this` instead, or even some DOM node to affect only its dependents.

What next? Oh yes:
``` js
(async () => {
```
Because mutations to the DOM can happen at any async moment, I choose to use an observer, this is all native `0 dependencies` (currently). We watch the root node for the style attribute to change. So yeah it's async, and because top level await is not a thing we have wrap around an async function which calls immediately, I rue the day I can not do this.

``` js
const result = await fontFamily("blue");
```
So our destructured property by any name... its callable! Once called, you have then pass a CSS value, in this case it's `"blue"`, you can see this `--font-family:blue;` in the `style attribute` of this example `<html style='--font-family:blue;'>`. In the words of the Cat In The Hat, "that is not all, oh no, that is not all!" something else just happened, we just emitted a custom event `css-prop-change` (I am not entirely happy with this yet, I may change to an observable and bring in RXJS).

What is the value of `result` then?
``` js
{
    key: "--font-family",
    onChange: ƒ value(cb),
    value: "blue"
}
```
Its this 100% immutable object, (remind me to .freeze() it 🤫), you could do this:

``` js
// some css in js
`{
    background: ${result.key} 
}`
```
Obviously result is a bad name but you can see the point, its trivial to insert a usage of this variable key. Or in-fact just write the name in a style sheet in anticipation of your amazing variable to be declared.

What else can you do?
``` js
// after you set your first variable (initial state if you like)
result.onChange((detail) => {
  console.log("e", detail);
});
```
You now have access to `onChange` but I may refactor this too `subscribe` like I said, events.. not sure about it.

Any callback you provide to `onChange` will then be fired on... change, and the callback is passed a detail about what just changed, this comes from the event listener we have pre-added automatically. Something like this:

``` js
{
    event: CustomEvent {isTrusted: false, detail: {…}, type: "css-prop-change", target: html, currentTarget: null, …}
    newValue: "yellow"
    oldValue: "blue"
}
```
But of course, this only fires if the value actually changes, so setting something to blue twice will do nothing, "YAY!".

The main and most amazing thing is, if a variable that was set and tracked by our function changes externally, IN REAL TIME, we see callback fire too, you can literally go to the devtools window and change the value and see the callback fire in response, you could even change the variable through breakpoints `@media` providing the variable is pre declared in css already and this will also fire on break points, (personally I prefer matchMedia API).

Anyway what do you think, should I release it?

(ps, If your wondering, I am taking a break from didi before I burn out - so much to do, need more contrib)