{"id":48285164,"url":"https://github.com/lc-2025/theme-switcher","last_synced_at":"2026-04-04T22:55:49.082Z","repository":{"id":317868958,"uuid":"1069025231","full_name":"lc-2025/theme-switcher","owner":"lc-2025","description":"A theme style selector","archived":false,"fork":false,"pushed_at":"2025-10-20T08:15:46.000Z","size":668,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-23T17:50:55.301Z","etag":null,"topics":["dark-mode","github-actions","headlessui","jest-testing","light-mode","npm-packages","open-source","react-component","tailwind-css","theme-swicther","typescript","ui-library"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@lc-2025/theme-switcher","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/lc-2025.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-03T09:33:14.000Z","updated_at":"2025-10-17T07:11:50.000Z","dependencies_parsed_at":"2025-10-03T16:08:49.166Z","dependency_job_id":"9c25cd58-e7e8-429e-b980-d0b787aac0c4","html_url":"https://github.com/lc-2025/theme-switcher","commit_stats":null,"previous_names":["lc-2025/theme-switcher"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lc-2025/theme-switcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lc-2025%2Ftheme-switcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lc-2025%2Ftheme-switcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lc-2025%2Ftheme-switcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lc-2025%2Ftheme-switcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lc-2025","download_url":"https://codeload.github.com/lc-2025/theme-switcher/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lc-2025%2Ftheme-switcher/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31418273,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["dark-mode","github-actions","headlessui","jest-testing","light-mode","npm-packages","open-source","react-component","tailwind-css","theme-swicther","typescript","ui-library"],"created_at":"2026-04-04T22:55:48.515Z","updated_at":"2026-04-04T22:55:49.064Z","avatar_url":"https://github.com/lc-2025.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Theme Switcher\n\nA theme style selector.\n\n![Theme Switcher](./docs/preview.gif 'Theme Switcher')\n[![Theme Switcher CI](https://github.com/lc-2025/theme-switcher/actions/workflows/ci.yml/badge.svg)](https://github.com/lc-2025/theme-switcher/actions/workflows/ci.yml) [![Theme Switcher CD](https://github.com/lc-2025/theme-switcher/actions/workflows/cd.yml/badge.svg)](https://github.com/lc-2025/theme-switcher/actions/workflows/cd.yml)\n\n## About\n\n_Theme Switcher_ is a stateful _React_ component based on _TailwindCSS_ and _HeadlessUI_ agnostic library for Light and Dark theme management.\nA totally customizable and adaptable ready-to-go utility provided as a _NPM_ package to speed up the workflow.\n\n## Features\n\n- Personalizable layout and template\n- Theme state and storage management\n- Evironmental auto-detection (system \u0026 browser-based)\n\n## Stack\n\n- **Languages**: JavaScript, Typescript, YAML, Bash\n- **Environments**: DOM\n- **Libraries**: Storage Manager, HeadlessUI, Testing Library\n- **Frameworks**: React, TailwindCSS, Jest\n- **Linters/Plugins**: ESLint, Prettier\n- **Compilers**: Babel, TypeScript\n- **Bundlers**: Rollup\n- **Testing**: Jest, Testing Library\n- **Versioning**: GitHub, Husky\n- **Continuous-Integration/Delivery**: GitHub Actions\n- **Deployment**: NPM Registry\n\n## Getting Started\n\nThe package production version is available on _NPM_ at [https://npmjs.com/package/@lc-2025/theme-switcher](https://npmjs.com/package/@lc-2025/theme-switcher).\nFor any contribution, maintanance and/or trial needs, please refer to the following specifications.\n\n### Requirements\n\n_Theme Switcher_ is based on _React_, _TailwindCSS_ and _HeadlessUI_ libraries to provide its features.\nPlease be sure to include these peer dependencies in your setup process.\n\n### Installation\n\nOn terminal, from project root:\n\n```bash\n# Assuming React already installed\n\nnpm i @lc-2025/theme-switcher tailwindcss @headlessui/react\n```\n\n### Configuration\n\n- Update the _TailwindCSS_ configuration:\n\n```js\n// tailwind.config.{js|ts}\n\nconst config = {\n  content: [\n    './src/**/*.{js,ts,jsx,tsx}',\n    // Include Theme Switcher classes as well\n    './node_modules/@lc-2025/theme-switcher/**/*.js',\n  ],\n  // Enable Dark-Mode support via class\n  darkMode: 'class',\n};\n\nexport default config;\n```\n\n- Update the global CSS file:\n\n```css\n/* i.e. {global|index}.css  */\n\n/* Import TailwindCSS */\n@import 'tailwindcss';\n\n/**\n* Register Theme Switcher as source\n* Please note that the path is relative to file location, i.e.\n* - \u003croot\u003e/src/style.css -\u003e @source \"../node_modules/...\n*/\n@source \"../node_modules/@lc-2025/theme-switcher/dist\";\n\n/* Override the default configuration */\n@custom-variant dark (\u0026:where(.dark, .dark *));\n\n/* ... */\n```\n\n- Update the Root component:\n\n```jsx\n// i.e. main.{jsx|tsx}\n\n// ...\n\ncreateRoot(document.getElementById('root')!).render(\n  \u003cStrictMode\u003e\n    {/* Include the Theme Switcher Context provider */}\n    \u003cThemeProvider\u003e\n      \u003cApp /\u003e\n    \u003c/ThemeProvider\u003e\n  \u003c/StrictMode\u003e,\n)\n```\n\n### Usage\n\n- Define your CSS variables/rules:\n\n```css\n/* i.e. {global|index}.css  */\n\n/* ... */\n\n@theme {\n  --black: #000;\n  --white: #fff;\n  --color-primary: var(--black);\n  --color-accent: var(--white);\n}\n\n/* Custom Dark-Mode rules */\n.dark {\n  --color-primary: var(--white);\n  --color-accent: var(--black);\n\n  background-color: var(--color-accent);\n  color: var(--color-primary);\n}\n\n/* ... */\n```\n\n- Include _Theme Switcher_ in your App (basic usage):\n\n```jsx\n// MyComponent.{jsx|tsx}\n\nimport { useEffect } from 'react';\nimport { ThemeSwitcher, useThemeContext } from '@lc-2025/theme-switcher';\n\nconst MyComponent = () =\u003e {\n  // Styles definition\n  const style = {\n    // Pass class names to style properties\n    iconDark: 'my-dark-icon',\n    iconLight: 'my-light-icon',\n  };\n  // i.e. Check selected theme\n  const theme = useThemeContext();\n\n  useEffect(() =\u003e {\n    console.log(theme);\n  });\n\n  /**\n   * Icons\n   * May be any valid JSX/TSX element, i.e.:\n   * - Icon library nodes\n   * - Custom components\n   * - Native DOM nodes\n   * - Etc.\n   */\n  const iconDark = () =\u003e \u003c\u003efoo\u003c/\u003e;\n\n  // ...\n\n  return (\n    //...\n    \u003cThemeSwitcher\n      iconDark={iconDark}\n      iconLight={\u003cspan\u003ebar\u003c/span\u003e}\n      style={style}\n    /\u003e\n    //...\n  );\n};\n```\n\n#### Advanced usage\n\n- With _TailwindCSS_:\n\n```jsx\n// MyComponent.{jsx|tsx}\n\n  // ...\n\n  /**\n   * Styles definition\n   * Theme Switcher is built upon a default style as fallback\n   * Declare TailwindCSS classes or your custom ones to\n   * change the component layout/template.\n   * Default approach: TailwindCSS classes\n   */\n  const styleOverride = {\n    container: 'flex items-center mt-8',\n    field: 'h-8 w-14 bg-blue-300 data-checked:bg-blue-300 data-focus:outline-blue-300',\n    iconLight: 'font-bold mr-4 uppercase',\n    iconDark: 'font-bold ml-4 uppercase',\n    switch: 'bg-blue-600 group-data-[checked]:bg-blue-600 group-data-checked:translate-x-6 size-6'\n  };\n\n  // ...\n\n  return (\n    //...\n    {/* Styles override */}\n    \u003cThemeSwitcher\n      iconDark={\u003c\u003eDark\u003c/\u003e}\n      iconLight={\u003c\u003eLight\u003c/\u003e}\n      style={styleOverride}\n    /\u003e\n    //...\n  );\n```\n\n- With Custom classes:\n\n```css\n/* MyComponent.css */\n\n.my-container {\n  align-items: flex-start;\n  display: flex;\n  flex-direction: column;\n  margin: 2rem 0 0;\n  text-align: center;\n\n  .my-field {\n    height: 3rem;\n    margin: 0.5rem 0;\n    width: 6rem;\n\n    \u0026.my-field--dark {\n      border: 2px dotted var(--color-primary);\n    }\n\n    \u0026.my-field--light {\n      border: 1px solid var(--color-primary);\n    }\n  }\n\n  .my-icon-dark {\n    align-self: flex-end;\n\n    \u0026.my-icon-dark--dark {\n      font-weight: bold;\n    }\n\n    \u0026.my-icon-dark--light {\n      font-weight: normal;\n    }\n  }\n\n  .my-icon-light {\n    font-style: italic;\n  }\n}\n```\n\n```jsx\n// MyComponent.{jsx|tsx}\n\n/**\n * Assuming MyComponent.{css|jsx|tsx} under:\n * - \u003croot\u003e/css/MyComponent.css\n * - \u003croot\u003e/components/MyComponent.{jsx|tsx}\n */\nimport '../css/MyComponent.css';\n\n  // ...\n\n  const styleCustom = {\n    // Total custom\n    container: 'my-container',\n    field: {\n      // Or mixed (Tailwind/Custom)\n      dark: 'my-field my-field--dark bg-red-300 data-checked:bg-red-300 data-focus:outline-red-300',\n      light: 'my-field my-field--light bg-yellow-300 data-checked:bg-yellow-300 data-focus:outline-yellow-300'\n    },\n    iconDark: {\n      dark: 'my-icon-dark my-icon-dark--dark',\n      light: 'my-icon-dark my-icon-dark--light'\n    },\n    iconLight: {\n      light: 'my-icon-light'\n    },\n  };\n\n  // ...\n\n  return (\n    //...\n    {/* Custom styles */}\n    \u003cThemeSwitcher\n      iconDark={\u003c\u003eDark\u003c/\u003e}\n      iconLight={\u003c\u003eLight\u003c/\u003e}\n      style={styleCustom}\n    /\u003e\n    //...\n  );\n```\n\n- With default classes:\n\n```css\n/* i.e. {global|index|MyComponent}.css  */\n\n/* Default CSS classes reference */\n\n/* Container */\n.theme-switcher {\n  /* ... */\n\n  /* Switch */\n  .theme-switcher__field {\n    /* ... */\n\n    /* Slider */\n    .field__switch {\n      /* ... */\n    }\n  }\n\n  /* Icon - Base */\n  .theme-switcher__icon {\n    /* ... */\n  }\n\n  /* Dark variant */\n  .theme-switcher__icon--dark {\n    /* ... */\n  }\n\n  /* Light variant */\n  .theme-switcher__icon--light {\n    /* ... */\n  }\n}\n\n/* ... */\n```\n\n```jsx\n// MyComponent.{jsx|tsx}\n\n/**\n * With custom CSS file only\n *\n * Assuming MyComponent.{css|jsx|tsx} under:\n * - \u003croot\u003e/css/MyComponent.css\n * - \u003croot\u003e/components/MyComponent.{jsx|tsx}\n */\nimport '../css/MyComponent.css';\n\n// ...\n\nreturn (\n  //...\n  \u003cThemeSwitcher iconDark={\u003cspan\u003efoo\u003c/span\u003e} iconLight={\u003cspan\u003ebar\u003c/span\u003e} /\u003e\n  //...\n);\n```\n\n- Icon variants:\n\n```jsx\n// MyComponent.{jsx|tsx}\n\n  // ...\n\n  const theme = useThemeContext();\n\n  // ...\n\n  return (\n    //...\n    {/* Different icons according to the current theme */}\n    \u003cThemeSwitcher\n      iconDark={{\n        // Explicit definition\n        dark: \u003c\u003e{'\u003e8-|'}\u003c/\u003e,\n        light: \u003c\u003e{'D:-)'}\u003c/\u003e\n      }}\n      iconLight={\n        // Conditional rendering\n        theme === 'light' ?\n          \u003ch2\u003eLight\u003c/h2\u003e :\n          \u003c\u003e☀️\u003c/\u003e\n      }\n    /\u003e\n    //...\n  );\n```\n\n### API\n\n#### Props\n\n- `iconDark`\n\n  ```ts\n  ReactNode | { darkIcon?: ReactNode, lightIcon?: ReactNode }\n  ```\n\n  Defines the dark theme icon or its variants\n\n- `iconLight`\n\n  ```ts\n  ReactNode | { darkIcon?: ReactNode, lightIcon?: ReactNode }\n  ```\n\n  Defines the light theme icon or its variants\n\n- `style` (optional)\n\n  [See below](#style)\n\n  Defines the component CSS classes\n\n#### Style\n\n- `container` (optional)\n\n  ```ts\n  string;\n  ```\n\n  Defines the component container\n\n- `field` (optional)\n\n  ```ts\n  { dark?: ReactNode, light?: ReactNode }\n  ```\n\n  Defines the component `Switch` child (slider backgorund) variants\n\n- `iconDark` (optional)\n\n  ```ts\n  { dark?: ReactNode, light?: ReactNode }\n  ```\n\n  Defines the component dark icon variants\n\n- `iconLight` (optional)\n\n  ```ts\n  { dark?: ReactNode, light?: ReactNode }\n  ```\n\n  Defines the component light icon variants\n\n- `switch` (optional)\n\n  ```ts\n  { dark?: ReactNode, light?: ReactNode }\n  ```\n\n  Defines the component slider child variants\n\n## Contributing\n\n### Setting Up\n\nOn terminal, from project root:\n\n- To install dependencies\n\n```bash\nnpm run install\n```\n\n- To lint the sources\n\n```bash\nnpm run lint\n```\n\n- To build the production version\n\n```bash\nnpm run build\n```\n\n### Tests\n\n#### Unit\n\nOn terminal, from project root:\n\n- To run the unit tests in `development` mode\n\n```bash\nnpm run test\n```\n\n- To run the tests in `testing` mode (staging or content-integration/delivery environments)\n\n```bash\nnpm run test:ci\n```\n\n### Deploy\n\n_Theme Switcher_ is integrated and delivered to production via _GitHub Actions_ workflows pipeline, where the package is being set up, tested and built.\nThen the artifacts are deployed on _NPM_ registry available at [https://npmjs.com/package/@lc-2025/theme-switcher](https://npmjs.com/package/@lc-2025/theme-switcher).\n\n- To deploy the production version\n\n```bash\nnpm run deploy\n```\n\nPlease read more about required best practices on the specific [contributing reference document](./.github/CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flc-2025%2Ftheme-switcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flc-2025%2Ftheme-switcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flc-2025%2Ftheme-switcher/lists"}