CSS-vars-hook: How to manipulate CSS Custom properties in React
Tags: react, css, hooks, javascript
Published: 2020-11-17 14:01:27

## Problem
As a developer I need to manipulate HTML element's style from within React components. Changing color, size, position, animation etc. 

## Classic solution
Vanilla React gives me an ability to change an element's class name or style attribute. Like we did in good old jQuery times.

```jsx
const Component = () => {
  return (
    <div
      className="foo"
      style={{color: 'chucknorris'}}>
      Hello
   </div>
  )
}
```
This approach has two major problems:
1. **CSS class method** is convenient if you have enumerable amount of values to put in CSS. So they can be described by limited amount of CSS classes like `button-primary`, `button-secondary` etc. But CSS classes will not help you much, when you come into applying non-enumerable range of values, e.g. you want to animate element position. Simply because you can't create class name for every possible coordinates.

2. `style` attribute is more flexible. You can apply any value to it during runtime and the browser will repaint the document accordingly. This solution also doesn't play well. It may and will conflict with existing CSS, since `style` attribute rules have preference over rules defined in the stylesheet. Also, you can not use Media queries, pseudo-classes or pseudo-elements here.

## CSS in JS
`CSS in JS` pattern became popular last years. There are plenty of libraries today (Styled Components, Aphrodite, JSS and others), which allow you to define CSS rules inside JavaScript code, thus making it accessible during your component's runtime. 

```jsx
import React from 'react';
import styled from 'styled-components';

const Component = styled.div`
  /* color value changes depending on props.danger value */
  color: ${props => props.danger ? 'red' || 'green'};
`
```
While being efficient and widely adopted `CSS in JS` also has problems I would like to address.

1. **Additional layer of complexity**. Putting a `CSS in JS` library into use adds an extra layer to your front-end stack, which can sometimes be unnecessary. It’s not always worth the hassle, especially in the case of a simpler project.

2. **Hard to debug**. Generated class names and selectors significantly worsen code readability, if you use your browser’s devtools for debugging. This also makes the learning curve much more steep and hard to master for beginners.

3. **Mishmash of languages**. Having two programming languages in a single module was never a good idea (I'm looking at you, JSX). Add `CSS in JS` library and congrats, now you have three of them.

## css-vars-hook

### Example

Let me show you the code first.

{% codepen https://codepen.io/morewings/pen/jOrJove default-tab=js,result %}

This pen demonstrates how to manipulate background color from inside React component using [css-vars-hook](https://github.com/morewings/css-vars-hook). `css-vars-hook` is a tiny package allows applying [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) to HTML Elements, rendered by React component.

You can provide any valid color value and change box color accordingly.

To make it work you need to add `--boxColor` variable to your CSS.

```css
.demo-box {
  background: var(--boxColor);
  /*...*/
}
```

### Features

- **Native**. Uses vanilla CSS, no need to implement additional processing.
- **Fast**. CSS variables manipulation doesn't trigger component reconciliation.
- **Simple**. It's just CSS and React interoperation.

### Interface

Library exposes two exports: `useTheme` and `useVariable`

`useTheme` applies multiple CSS properties to a given Html Element.

```js
import {useTheme} from 'css-vars-hook';

const {
  /* Theme container element setter. <div ref={setRef} /> */
  setRef,
  /* React ref. Use as theme container element getter only. */
  ref,
  /* Object containing style properties {'--foo': 'bar'}.
Apply on target element to prevent flash 
of unstyled content during server-side rendering.
  <div style={style} ref={setRef} /> */
  style,
  /* Get variable value. function(variableName: string) => string */
  getVariable,
  /* Set variable value. function(variableName: string, value: (string|number)) => void */
  setVariable,
  /* Remove variable. function(variableName: string) => void */
  removeVariable
} = useTheme({foo: 'bar'});
```

`useVariable` applies a single CSS property to a given HTML Element. 

```js
import {useVariable} from 'css-vars-hook';

const {
  ref,
  setRef,
  style,
  setVariable,
  getVariable,
  removeVariable
} = useVariable('variableName', 'value');
```

### Server-side rendering (SSR)

Since CSS variables are applied after initial render, you need to apply `style` attribute to the target HTML Element to prevent flash of unstyled content.

```jsx
// ...
const Component = () => {
  const { setRef, style } = useTheme({foo: 'bar'});
  return (
    <div ref={setRef} style={style}>Hello world!</div>
  )
}
```