├── .all-contributorsrc ├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── __tests__ ├── .eslintrc ├── index.test.js └── initialize.test.js ├── noflash.js.txt ├── package-lock.json ├── package.json ├── src ├── index.js └── initialize.js └── types └── index.d.ts /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "use-dark-mode", 3 | "projectOwner": "donavon", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": false, 11 | "contributors": [ 12 | { 13 | "login": "donavon", 14 | "name": "Donavon West", 15 | "avatar_url": "https://avatars3.githubusercontent.com/u/887639?v=4", 16 | "profile": "http://donavon.com", 17 | "contributions": [ 18 | "infra", 19 | "test", 20 | "example", 21 | "ideas", 22 | "maintenance", 23 | "review", 24 | "tool", 25 | "code" 26 | ] 27 | }, 28 | { 29 | "login": "revelcw", 30 | "name": "Revel Carlberg West", 31 | "avatar_url": "https://avatars2.githubusercontent.com/u/29359616?v=4", 32 | "profile": "https://github.com/revelcw", 33 | "contributions": [ 34 | "ideas" 35 | ] 36 | }, 37 | { 38 | "login": "Andarist", 39 | "name": "Mateusz Burzyński", 40 | "avatar_url": "https://avatars2.githubusercontent.com/u/9800850?v=4", 41 | "profile": "https://github.com/Andarist", 42 | "contributions": [ 43 | "code" 44 | ] 45 | }, 46 | { 47 | "login": "wKovacs64", 48 | "name": "Justin Hall", 49 | "avatar_url": "https://avatars1.githubusercontent.com/u/1288694?v=4", 50 | "profile": "https://github.com/wKovacs64", 51 | "contributions": [ 52 | "code" 53 | ] 54 | }, 55 | { 56 | "login": "fxbabys", 57 | "name": "Jeremy", 58 | "avatar_url": "https://avatars1.githubusercontent.com/u/24556921?v=4", 59 | "profile": "https://github.com/fxbabys", 60 | "contributions": [ 61 | "userTesting", 62 | "bug" 63 | ] 64 | }, 65 | { 66 | "login": "janosh", 67 | "name": "Janosh Riebesell", 68 | "avatar_url": "https://avatars0.githubusercontent.com/u/30958850?v=4", 69 | "profile": "http://janosh.io", 70 | "contributions": [ 71 | "doc" 72 | ] 73 | }, 74 | { 75 | "login": "hipstersmoothie", 76 | "name": "Andrew Lisowski", 77 | "avatar_url": "https://avatars3.githubusercontent.com/u/1192452?v=4", 78 | "profile": "http://hipstersmoothie.com", 79 | "contributions": [ 80 | "doc" 81 | ] 82 | }, 83 | { 84 | "login": "jorgegonzalez", 85 | "name": "Jorge Gonzalez", 86 | "avatar_url": "https://avatars2.githubusercontent.com/u/12901172?v=4", 87 | "profile": "https://jorgegonzalez.io", 88 | "contributions": [ 89 | "code" 90 | ] 91 | } 92 | ], 93 | "contributorsPerLine": 7, 94 | "skipCi": true 95 | } 96 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | indent_size = 2 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "amex", 3 | "plugins": ["react-hooks"], 4 | "rules": { 5 | "react-hooks/rules-of-hooks": 2, 6 | "react-hooks/exhaustive-deps": 2 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | .DS_Store 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (https://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # TypeScript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | # next.js build output 62 | .next 63 | 64 | # lib 65 | lib/ 66 | dist/ 67 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src 3 | .editorconfig 4 | .eslintrc 5 | .babelrc 6 | __tests__ 7 | coverage 8 | .prettierrc 9 | .travis.yml 10 | .all-contributorsrc 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "10" 5 | 6 | jobs: 7 | include: 8 | - stage: test 9 | script: npm t 10 | - stage: npm release 11 | if: branch = master 12 | deploy: 13 | provider: npm 14 | email: $NPM_EMAIL 15 | api_key: $NPM_TOKEN 16 | skip_cleanup: true 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present Donavon West 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @fisch0920/use-dark-mode 2 | 3 | > NOTE: this is a fork of [use-dark-mode](https://github.com/donavon/use-dark-mode) by [donovan](https://github.com/donavon) which adds some much needed maintenance support for React 17 and 18. 4 | 5 | A custom [React Hook](https://reactjs.org/docs/hooks-overview.html) to help you implement a "dark mode" component for your application. 6 | The user setting persists to `localStorage`. 7 | 8 | ❤️ it? ⭐️ it on [GitHub](https://github.com/transitive-bullshit/use-dark-mode/stargazers) 9 | 10 | [](https://badge.fury.io/js/@fisch0920/use-dark-mode) [](#contributors) 11 | 12 |  13 | 14 | `useDarkMode` works in one of two ways: 15 | 16 | 1. By toggling a CSS class on whatever element you specify (defaults to `document.body`). 17 | You then setup your CSS to display different views based on the presence of the selector. For example, the following CSS is used in the demo app to ease the background color in/out of dark mode. 18 | 19 | ```css 20 | body.light-mode { 21 | background-color: #fff; 22 | color: #333; 23 | transition: background-color 0.3s ease; 24 | } 25 | body.dark-mode { 26 | background-color: #1a1919; 27 | color: #999; 28 | } 29 | ``` 30 | 31 | 2. If you don't use global classes, you can specify an `onChange` handler and take care of the implementation of switching to dark mode yourself. 32 | 33 | ## New in Version 2.x 34 | 35 | - `useDarkMode` now persists between sessions. It stores the user setting in 36 | `localStorage`. 37 | 38 | - It shares dark mode state with all other `useDarkMode` components on the page. 39 | 40 | - It shares dark mode state with all other tabs/browser windows. 41 | 42 | - The initial dark mode is queried from the system. Note: this requires a browser that supports the `prefers-color-scheme: dark` media query 43 | ([currently Chrome, Firefox, Safari and Edge](https://caniuse.com/#search=prefers-color-scheme)) 44 | and a system that supports dark mode, such as macOS Mojave. 45 | 46 | - Changing the system dark mode state will also change the state of `useDarkMode` 47 | (i.e, change to light mode in the system will change to light mode in your app). 48 | 49 | - Support for Server Side Rendering (SSR) in version 2.2 and above. 50 | 51 | ## Requirement 52 | 53 | To use `@fisch0920/use-dark-mode`, you must use `react@16.8.0` or greater which includes Hooks. 54 | 55 | ## Installation 56 | 57 | ```sh 58 | $ npm i @fisch0920/use-dark-mode 59 | ``` 60 | 61 | ## Usage 62 | 63 | ```js 64 | const darkMode = useDarkMode(initialState, darkModeConfig); 65 | ``` 66 | 67 | ### Parameters 68 | 69 | You pass `useDarkMode` an `initialState` (a boolean specifying whether it should be in dark mode 70 | by default) and an optional `darkModeConfig` object. The configuration object may contain the following keys. 71 | 72 | | Key | Description | 73 | | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 74 | | `classNameDark` | The class to apply. Default = `dark-mode`. | 75 | | `classNameLight` | The class to apply. Default = `light-mode`. | 76 | | `element` | The element to apply the class name. Default = `document.body`. | 77 | | `onChange` | A function that will be called when the dark mode value changes and it is safe to access the DOM (i.e. it is called from within a `useEffect`). If you specify `onChange` then `classNameDark`, `classNameLight`, and `element` are ignored (i.e. no classes are automatically placed on the DOM). You have full control! | 78 | | `storageKey` | A string that will be used by the `storageProvider` to persist the dark mode value. If you specify a value of `null`, nothing will be persisted. Default = `darkMode`. | 79 | | `storageProvider` | A storage provider. Default = `localStorage`. You will generally never need to change this value. | 80 | 81 | ### Return object 82 | 83 | A `darkMode` object is returned with the following properties. 84 | 85 | | Key | Description | 86 | | :---------- | :------------------------------------------------------ | 87 | | `value` | A boolean containing the current state of dark mode. | 88 | | `enable()` | A function that allows you to set dark mode to `true`. | 89 | | `disable()` | A function that allows you to set dark mode to `false`. | 90 | | `toggle()` | A function that allows you to toggle dark mode. | 91 | 92 | Note that because the methods don't require any parameters, you can call them 93 | direcly from an `onClick` handler from a button, for example 94 | (i.e., no lambda function is required). 95 | 96 | ## Example 97 | 98 | Here is a simple component that uses `useDarkMode` to provide a dark mode toggle control. 99 | If dark mode is selected, the CSS class `dark-mode` is applied to `document.body` and is removed 100 | when de-selected. 101 | 102 | ```jsx 103 | import React from 'react'; 104 | import useDarkMode from '@fisch0920/use-dark-mode'; 105 | 106 | import Toggle from './Toggle'; 107 | 108 | const DarkModeToggle = () => { 109 | const darkMode = useDarkMode(false); 110 | 111 | return ( 112 |