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.
const Component = () => {
return (
<div
className="foo"
style={{color: 'chucknorris'}}>
Hello
</div>
)
}
This approach has two major problems:
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.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, sincestyle
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.
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.
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.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.
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.
This pen demonstrates how to manipulate background color from inside React component using css-vars-hook. css-vars-hook
is a tiny package allows applying CSS Variables 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.
.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.
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.
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.
// ...
const Component = () => {
const { setRef, style } = useTheme({foo: 'bar'});
return (
<div ref={setRef} style={style}>Hello world!</div>
)
}
Top comments (0)