{"id":22813179,"url":"https://github.com/morewings/css-vars-hook","last_synced_at":"2025-06-16T15:09:14.671Z","repository":{"id":38435201,"uuid":"309668116","full_name":"morewings/css-vars-hook","owner":"morewings","description":"Bring dynamic styling into your React components with css-vars-hook. Simple and intuitive hooks for managing CSS Custom Properties. Integrate styles with component logic. ","archived":false,"fork":false,"pushed_at":"2025-06-12T13:40:15.000Z","size":8474,"stargazers_count":26,"open_issues_count":10,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-12T14:44:32.273Z","etag":null,"topics":["css-custom-properties","css-in-js","css-in-react","css-variables","hook","react","react-hook"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/morewings.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-11-03T11:42:07.000Z","updated_at":"2025-06-12T13:40:18.000Z","dependencies_parsed_at":"2023-02-06T06:31:31.059Z","dependency_job_id":"50798f11-d4d3-4446-8743-7f8721aa71eb","html_url":"https://github.com/morewings/css-vars-hook","commit_stats":{"total_commits":226,"total_committers":5,"mean_commits":45.2,"dds":0.6150442477876106,"last_synced_commit":"0f3dacd1378977d609652e49bc243507503f6918"},"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"purl":"pkg:github/morewings/css-vars-hook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morewings%2Fcss-vars-hook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morewings%2Fcss-vars-hook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morewings%2Fcss-vars-hook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morewings%2Fcss-vars-hook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/morewings","download_url":"https://codeload.github.com/morewings/css-vars-hook/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morewings%2Fcss-vars-hook/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260183014,"owners_count":22971195,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css-custom-properties","css-in-js","css-in-react","css-variables","hook","react","react-hook"],"created_at":"2024-12-12T12:15:52.645Z","updated_at":"2025-06-16T15:09:14.640Z","avatar_url":"https://github.com/morewings.png","language":"TypeScript","readme":"[![Node.js CI](https://github.com/morewings/css-vars-hook/actions/workflows/merge-jobs.yml/badge.svg)](https://github.com/morewings/css-vars-hook/actions/workflows/merge-jobs.yml)\n[![yarn version](https://badge.fury.io/js/css-vars-hook.svg)](https://www.npmjs.com/package/css-vars-hook)\n[![npm](https://img.shields.io/npm/dm/css-vars-hook)](https://www.npmcharts.com/compare/css-vars-hook?interval=7)\n[![types included](https://img.shields.io/github/package-json/types/morewings/css-vars-hook)](https://github.com/morewings/css-vars-hook)\n[![zero dependencies](https://img.shields.io/badge/zero-dependencies-teal)](https://github.com/morewings/css-vars-hook)\n[![npm bundle size](https://deno.bundlejs.com/badge?bundle\u0026q=css-vars-hook@latest\u0026config={\"esbuild\":{\"external\":[\"react\",\"react-dom\"]}})](https://bundlejs.com/?q=css-vars-hook@latest\u0026config={\"esbuild\":{\"external\":[\"react\",\"react-dom\"]}})\n[![Maintainability](https://api.codeclimate.com/v1/badges/6e529032595f49447227/maintainability)](https://codeclimate.com/github/morewings/css-vars-hook/maintainability)\n[![Test Coverage](https://api.codeclimate.com/v1/badges/6e529032595f49447227/test_coverage)](https://codeclimate.com/github/morewings/css-vars-hook/test_coverage)\n\n# CSS Variables React hook\n\n[![NPM library Create React App template logo](./design/image.jpg)](#)\n\n[css-vars-hook](https://github.com/morewings/css-vars-hook) contains React hooks to set and manipulate CSS custom properties (variables).\n\n\n[Demo](https://morewings.github.io/css-vars-hook/)\n\n[dev.to article](https://dev.to/morewings/how-to-use-css-vars-hook-to-manipulate-css-custom-properties-in-react-38dg)\n\n## Highlights\n\n- **CSS Variables in React**: manage your component design in a fast and convenient way.\n- **Dynamic Theming**: create and manage themes for your application. Apply multiple CSS variables to any HTML element.\n- **TypeScript Support**: The library is written in TypeScript, offering type safety and enhancing developer experience.\n- **Zero Dependencies**: It operates independently without the need for additional libraries, ensuring a lightweight integration.\n- **Performance**: The hook is optimized for performance, with a small footprint that does not impact application speed.\n\n## Install\n\n```shell script\nnpm install css-vars-hook\n```\n\n## Usage\n\n`css-vars-hook` exposes two hooks: `useRootTheme`, `useLocalTheme`. Both of them provide developer a bridge between **React Component state** and **CSS Custom Properties**.\n\n## `useRootTheme`\n\n`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).\n\n## Manipulate theme\n\n### Set up\n\nIn order to set global theming you need to wrap your application with `RootThemeProvider` on highest possible level.\n\n```jsx\n// App.js\nimport React from 'react';\nimport {RootThemeProvider} from 'css-vars-hook';\n\n// Theme object contains dictionary of CSS variables you will use later in your application\nconst theme = {\n    boxColor: 'purple',\n    borderColor: 'violet',\n}\n\nexport const App = () =\u003e (\n    \u003cRootThemeProvider\n        theme={theme}\u003e\n        {/*...*/}\n    \u003c/RootThemeProvider\u003e\n);\n```\n\n### Memoize theme\n\nTo avoid unnecessary reconciliations and re-renders theme object has to **preserve referential equality** during component lifecycle.\n\n#### Wrong examples\n\nArbitrary objects are recreated every time React component reconciles. Avoid this when defining theme object.\n\n```tsx\n// Don't do this!!!\nconst Component: FC = () =\u003e {\n    //...\n    const theme = {\n        foo: 'bar'\n    }\n\n    return \u003cRootThemeProvider theme={theme}\u003e{/*...*/}\u003c/RootThemeProvider\u003e\n}\n```\n\n```tsx\n// Don't do this!!!\nconst Component: FC = () =\u003e {\n    //...\n    return \u003cRootThemeProvider theme={{ foo: 'bar' }}\u003e{/*...*/}\u003c/RootThemeProvider\u003e\n}\n```\n\n#### Correct examples\n\nSet theme object externally to Component or wrap with `useMemo`.\n\n```tsx\n// Correct!\nconst theme = {\n    foo: 'bar'\n}\n\nconst Component: FC = () =\u003e {\n    return \u003cRootThemeProvider theme={theme}\u003e{/*...*/}\u003c/RootThemeProvider\u003e\n}\n```\n\n```tsx\n// Correct! Theme will preserve until foo property change\nconst Component: FC\u003c{foo: string}\u003e = ({foo}) =\u003e {\n\n    const theme = useMemo(() =\u003e ({foo}), [foo])\n\n    return \u003cRootThemeProvider theme={theme}\u003e{/*...*/}\u003c/RootThemeProvider\u003e\n}\n```\n\n### Change theme\n\nTheme 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.\n```jsx\n// Component.jsx\nimport React, { useEffect, useCallback } from \"react\";\nimport { useRootTheme } from 'css-vars-hook';\n\nconst theme = {\n  boxColor: 'red',\n  borderColor: 'green',\n}\n\nconst Component = () =\u003e {\n  const { setTheme, setVariable, removeVariable } = useRootTheme();\n\n  // Set theme value inside useEffect hook\n  useEffect(() =\u003e {\n    // Theme changing effects can be applied like this. The change will happen after render.\n    setTheme(theme);\n  }, [theme, setTheme])\n\n  // Set theme value inside callback\n  const handleVariable = useCallback(() =\u003e {\n    setVariable('boxColor', 'pink');\n  }, [])\n\n  return \u003cbutton onClick={handleVariable}\u003eChange variable\u003c/button\u003e;\n}\n```\n\n### Caveats\n\n```jsx\n//...\nconst Component = () =\u003e {\n  const { setTheme } = useRootTheme();\n\n  // This will not work!\n  setTheme(theme)\n\n  //...\n}\n```\n\nThe 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.\n\n\n### Type safety\n\nDevelopers can provide theme type to `useRootTheme` hook as a TypeScript Generic.\n\n```tsx\nimport { FC } from \"react\";\n\ntype Theme = {\n    boxColor: 'yellow' | 'blue';\n    borderColor: string;\n};\n\nconst themeYellow: Theme = {\n    boxColor: 'yellow',\n    borderColor: 'blue',\n};\n\nconst Component: FC = () =\u003e {\n    const {setTheme, getTheme, setVariable} = useRootTheme\u003cTheme\u003e();\n    const doSomething = () =\u003e {\n        // theme value will be properly typed this way\n        console.log('root theme', getTheme().boxColor);\n    };\n    //...\n}\n```\n\n\n### Consume the theme data\n\nCSS variables set by `RootThemeProvider` are available globally across all application.\n\n#### In CSS\n\n```postcss\n// Component.css\n\n.box {\n    background: var(--boxColor);\n    border: 1px solid var(--borderColor)\n}\n```\n\n#### In JS\n\n```js\nimport {useRootTheme} from 'css-vars-hook';\n\nconst {\n    /** Get current theme */\n    getTheme,\n    /** Get variable value within active theme */\n    getVariable,\n} = useRootTheme();\n\nconsole.log(getVariable('boxColor')) // =\u003e 'purple'\nconsole.log(getTheme()) // =\u003e theme object\n```\n\n## `useLocalTheme`\n\n`useLocalTheme` applies theme locally to the wrapped React components.\n\n### Set up a local theme\n\nIn order to set local theme you need to wrap your component with `LocalRoot` component which is returned by `useLocalTheme` hook.\n\n```jsx\nimport { useLocalTheme } from 'css-vars-hook';\nimport { useCallback } from \"react\";\n\nconst theme = { boxColor: 'yellow' };\nconst darkTheme = {boxColor: 'darkYellow'};\n\nconst Component = () =\u003e {\n  const { LocalRoot, setTheme } = useLocalTheme();\n  const setDarkMode = useCallback(() =\u003e {\n    setTheme(darkTheme)\n  }, []);\n  return \u003cLocalRoot theme={theme}\u003e{/*...*/}\u003c/LocalRoot\u003e\n}\n```\n\nOutside different wrapping strategies this hook is similar to `useRootTheme`.\n\n### Customize `LocalRoot` element\n\nBy 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`.\n\n```jsx\nimport {useLocalTheme} from 'css-vars-hook';\n\nconst theme = {boxColor: 'yellow'};\nconst darkTheme = {boxColor: 'darkYellow'};\n\nconst Component = () =\u003e {\n    const {LocalRoot: Button, setTheme} = useLocalTheme();\n    const setDarkMode = useCallback(() =\u003e {\n      setTheme(darkTheme)\n    }, [])\n    return (\n      \u003cButton\n        theme={theme}\n        as=\"button\"\n        onClick={setDarkMode}\u003e\n        Set dark mode\n      \u003c/Button\u003e\n    )\n}\n```\n\n### Type safety\n\nLocal theme type is inferred from corresponding `LocalRoot` prop.\n\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorewings%2Fcss-vars-hook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmorewings%2Fcss-vars-hook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorewings%2Fcss-vars-hook/lists"}