css-vars-hook
v0.7.9
Published
css-vars-hook contains React hooks to set and manipulate CSS custom properties from React component.
Downloads
488
Maintainers
Readme
CSS Variables React hook
css-vars-hook contains React hooks to set and manipulate CSS custom properties (variables).
Highlights
- CSS Variables in React: manage your component design in a fast and convenient way.
- Dynamic Theming: create and manage themes for your application. Apply multiple CSS variables to any HTML element.
- TypeScript Support: The library is written in TypeScript, offering type safety and enhancing developer experience.
- Zero Dependencies: It operates independently without the need for additional libraries, ensuring a lightweight integration.
- Performance: The hook is optimized for performance, with a small footprint that does not impact application speed.
Install
npm install css-vars-hook
Usage
css-vars-hook
exposes two hooks: useRootTheme
, useLocalTheme
. Both of them provide developer a bridge between React Component state and CSS Custom Properties.
useRootTheme
useRootTheme
applies application level themes. API consists of two elements: the hook itself and RootThemeProvider
component which acts as :root
selector. Directly applying theme to the :root
is not compatible with Server side rendering (SSR).
Manipulate theme
Set up
In order to set global theming you need to wrap your application with RootThemeProvider
on highest possible level.
// App.js
import React from 'react';
import {RootThemeProvider} from 'css-vars-hook';
// Theme object contains dictionary of CSS variables you will use later in your application
const theme = {
boxColor: 'purple',
borderColor: 'violet',
}
export const App = () => (
<RootThemeProvider
theme={theme}>
{/*...*/}
</RootThemeProvider>
);
Memoize theme
To avoid unnecessary reconciliations and re-renders theme object has to preserve referential equality during component lifecycle.
Wrong examples
Arbitrary objects are recreated every time React component reconciles. Avoid this when defining theme object.
// Don't do this!!!
const Component: FC = () => {
//...
const theme = {
foo: 'bar'
}
return <RootThemeProvider theme={theme}>{/*...*/}</RootThemeProvider>
}
// Don't do this!!!
const Component: FC = () => {
//...
return <RootThemeProvider theme={{ foo: 'bar' }}>{/*...*/}</RootThemeProvider>
}
Correct examples
Set theme object externally to Component or wrap with useMemo
.
// Correct!
const theme = {
foo: 'bar'
}
const Component: FC = () => {
return <RootThemeProvider theme={theme}>{/*...*/}</RootThemeProvider>
}
// Correct! Theme will preserve until foo property change
const Component: FC<{foo: string}> = ({foo}) => {
const theme = useMemo(() => ({foo}), [foo])
return <RootThemeProvider theme={theme}>{/*...*/}</RootThemeProvider>
}
Change theme
Theme changing methods (setTheme
, setVariable
, removeVariable
) are implemented as effects. They will apply after component re-render. You'll have to wrap the side effect with useEffect
or put in inside callback to move it out of the rendering calculation.
// Component.jsx
import React, { useEffect, useCallback } from "react";
import { useRootTheme } from 'css-vars-hook';
const theme = {
boxColor: 'red',
borderColor: 'green',
}
const Component = () => {
const { setTheme, setVariable, removeVariable } = useRootTheme();
// Set theme value inside useEffect hook
useEffect(() => {
// Theme changing effects can be applied like this. The change will happen after render.
setTheme(theme);
}, [theme, setTheme])
// Set theme value inside callback
const handleVariable = useCallback(() => {
setVariable('boxColor', 'pink');
}, [])
return <button onClick={handleVariable}>Change variable</button>;
}
Caveats
//...
const Component = () => {
const { setTheme } = useRootTheme();
// This will not work!
setTheme(theme)
//...
}
The reason this code isn’t correct is that it tries to do something with the DOM node during rendering. In React, rendering should be a pure calculation of JSX and should not contain side effects like modifying the DOM. Moreover, when Component is called for the first time, its DOM does not exist yet, so there is no theme container to operate with.
Type safety
Global theme type should be defined on a project level. You'll have to redeclare ThemeType
export from css-vars-hook
// types.d.ts
import theme from '@/theme';
declare module 'css-vars-hook' {
// Provide your global theme type here
export type ThemeType = typeof theme;
}
Consume the theme data
CSS variables set by RootThemeProvider
are available globally across all application.
In CSS
// Component.css
.box {
background: var(--boxColor);
border: 1px solid var(--borderColor)
}
In JS
import {useRootTheme} from 'css-vars-hook';
const {
/** Get current theme */
getTheme,
/** Get variable value within active theme */
getVariable,
} = useRootTheme();
console.log(getVariable('boxColor')) // => 'purple'
console.log(getTheme()) // => theme object
useLocalTheme
useLocalTheme
applies theme locally to the wrapped React components.
Set up a local theme
In order to set local theme you need to wrap your component with LocalRoot
component which is returned by useLocalTheme
hook.
import { useLocalTheme } from 'css-vars-hook';
import { useCallback } from "react";
const theme = { boxColor: 'yellow' };
const darkTheme = {boxColor: 'darkYellow'};
const Component = () => {
const { LocalRoot, setTheme } = useLocalTheme();
const setDarkMode = useCallback(() => {
setTheme(darkTheme)
}, []);
return <LocalRoot theme={theme}>{/*...*/}</LocalRoot>
}
Outside different wrapping strategies this hook is similar to useRootTheme
.
Customize LocalRoot
element
By default LocalRoot
is rendered as a div
HTMLElement. You can provide custom element type (button
, span
, e. t. c.) by changing as
prop of LocalRoot
.
import {useLocalTheme} from 'css-vars-hook';
const theme = {boxColor: 'yellow'};
const darkTheme = {boxColor: 'darkYellow'};
const Component = () => {
const {LocalRoot: Button, setTheme} = useLocalTheme();
const setDarkMode = useCallback(() => {
setTheme(darkTheme)
}, [])
return (
<Button
theme={theme}
as="button"
onClick={setDarkMode}>
Set dark mode
</Button>
)
}
Type safety
Local theme type is inferred from corresponding LocalRoot
prop.