Skip to content

Commit d601a3d

Browse files
authored
Merge pull request #2 from designcise/feat/auto-theme
feat: added 'auto' option
2 parents 9124809 + f85efde commit d601a3d

27 files changed

+806
-463
lines changed

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"printWidth": 100
5+
}

README.md

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@ A simple theme toggle for Next.js 13+ that allows switching between light and da
44

55
```html
66
<html class="dark" style="color-scheme:dark">
7+
<!-- ... -->
8+
</html>
79
```
810

911
You can then [use different CSS selectors to create styles for dark/light themes](https://www.designcise.com/web/tutorial/how-to-create-non-flickering-dark-or-light-mode-toggle-in-next-js-using-localstorage#adding-the-ability-to-switch-themes).
1012

11-
## Goals
13+
## Features
1214

13-
- Provide an easy way of toggling between light and dark themes
14-
- Auto-switch theme on page load based on system settings
15-
- Avoid flicker on page load
16-
- Have no unnecessary bloat
17-
- Have very minimal configuration
18-
- Be simple and intuitive
15+
- Easy implementation with just _two_ lines of code
16+
- No flicker on page load
17+
- Toggle between `light`, `dark` and `auto` modes
18+
- Automatically choose color based on `prefers-color-scheme` when in "`auto`" mode
19+
- Update color when `prefers-color-scheme` changes in `auto` mode
20+
- Switch to opposite color when toggling from "`auto`"
21+
- Data is stored in `localStorage`
22+
- No unnecessary bloat
23+
- Well-tested
1924

2025
## Installation
2126

@@ -46,21 +51,19 @@ At a bare minimum you need to do the following:
4651

4752
```jsx
4853
// app/layout.js
49-
import { cookies } from 'next/headers';
50-
import { Html, ThemeProvider } from '@designcise/next-theme-toggle';
51-
import { getColors } from '@designcise/next-theme-toggle/server';
54+
import { ThemeProvider } from '@designcise/next-theme-toggle';
55+
import { themes } from '@designcise/next-theme-toggle/server';
5256

5357
// 1: specify key for storage
54-
const THEME_STORAGE_KEY = 'theme-preference';
55-
const color = getColors();
58+
const THEME_STORAGE_KEY = 'theme-preference'
5659

5760
export default async function RootLayout() {
5861
// 2: wrap components with `ThemeProvider` to pass theme props down to all components
5962
// 3: pass `storageKey` and (optional) `defaultTheme` to `ThemeProvider`
6063
return (
6164
<html>
6265
<body>
63-
<ThemeProvider storageKey={THEME_STORAGE_KEY} defaultTheme={color.dark}>
66+
<ThemeProvider storageKey={THEME_STORAGE_KEY} defaultTheme={themes.dark}>
6467
{children}
6568
</ThemeProvider>
6669
</body>
@@ -77,35 +80,31 @@ With this setup, the `ThemeProvider` component will automatically inject an inli
7780
// components/ToggleThemeButton/index.jsx
7881
'use client'
7982

80-
import React, { useContext } from 'react';
81-
import { useTheme } from '@designcise/next-theme-toggle';
83+
import React, { useContext } from 'react'
84+
import { useTheme } from '@designcise/next-theme-toggle'
8285

8386
export default function ToggleThemeButton() {
84-
const { toggleTheme } = useTheme();
87+
const { toggleTheme } = useTheme()
8588

86-
return (
87-
<button onClick={toggleTheme}>Toggle Theme</button>
88-
)
89+
return <button onClick={toggleTheme}>Toggle Theme</button>
8990
}
9091
```
9192

92-
You can also do this manually by using `theme`, `color`, and `setTheme()`, for example, like so:
93+
You can also do this manually by using `theme`, `themes`, `colors` and `setTheme()`, for example, like so:
9394

9495
```jsx
9596
// components/ToggleThemeButton/index.jsx
9697
'use client'
9798

98-
import React, { useContext } from 'react';
99-
import { useTheme } from '@designcise/next-theme-toggle';
99+
import React, { useContext } from 'react'
100+
import { useTheme } from '@designcise/next-theme-toggle'
100101

101102
export default function ToggleThemeButton() {
102-
const { theme, color, setTheme } = useTheme();
103+
const { theme, themes, colors, setTheme } = useTheme()
103104

104105
return (
105-
<button
106-
onClick={() => setTheme(theme === color.dark ? color.light : color.dark)}
107-
>
108-
Toggle Theme
106+
<button onClick={() => setTheme(theme === themes.dark ? colors.light : colors.dark)}>
107+
Toggle Theme
109108
</button>
110109
)
111110
}
@@ -115,7 +114,7 @@ export default function ToggleThemeButton() {
115114

116115
```jsx
117116
// app/page.js
118-
import ToggleThemeButton from '@/components/ToggleThemeButton';
117+
import ToggleThemeButton from '@/components/ToggleThemeButton'
119118

120119
export default async function Home() {
121120
return (
@@ -177,44 +176,47 @@ That's it! You should have light/dark theme toggle in your Next.js application.
177176

178177
You can pass the following props to `ThemeProvider`:
179178

180-
| Prop | Type | Description |
181-
|----------------|:--------------------------------------------:|:------------------------------------------------------------------:|
182-
| `children` | `React.ReactChild`&vert;`React.ReactChild[]` | Components to which the theme is passed down to via context. |
183-
| `storageKey` | String | Name of the key used for storage. |
184-
| `defaultTheme` | String | Default theme (`'light'` or `'dark'`) to use on initial page load. |
179+
| Prop | Type | Description |
180+
|----------------|:--------------------------------------------:|:--------------------------------------------------------------------------------------:|
181+
| `children` | `React.ReactChild`&vert;`React.ReactChild[]` | Components to which the theme is passed down to via context. |
182+
| `storageKey` | String | Name of the key used for storage. |
183+
| `defaultTheme` | String | Default theme (`'light'`, `'dark'` or `auto`) to use on page load. Defaults to `auto`. |
185184

186185
### `useTheme()`
187186

188187
The `useTheme()` hook does not take any params; it returns the following:
189188

190-
| Return Value | Type | Description |
191-
|---------------|:--------:|:-------------------------------------------------------------------:|
192-
| `theme` | String | The active theme (`'light'` or `'dark'`). |
193-
| `color` | Object | Color keys (`'light'` or `'dark'`) to compare active theme against. |
194-
| `setTheme` | Function | Setter to set new theme. |
195-
| `toggleTheme` | Function | Toggles the theme between `light` and `dark`. |
189+
| Return Value | Type | Description |
190+
|---------------|:--------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------:|
191+
| `theme` | String | The active theme (`'light'`, `'dark'` or `'auto'`). |
192+
| `themes` | Object | Allowed themes (`{ light: 'light', dark: 'dark', auto: 'auto' }`). |
193+
| `color` | String | The active color (`light` or `dark`). |
194+
| `colors` | Object | Allowed colors (`{ light: 'light', dark: 'dark', auto: 'dark'&vert;'light' }`). `colors.auto` returns `dark` or `light` based on `prefers-color-scheme`. |
195+
| `setTheme` | Function | Setter to set new theme. |
196+
| `toggleTheme` | Function | Toggles the theme between `light` and `dark`. When toggling from `auto`, the opposite color to active color is automatically chosen. |
196197

197-
### `getColors()`
198+
### `themes`
198199

199-
Returns an object, with the following:
200+
An object, with the following properties:
200201

201-
| Property | Type | Value | Description |
202-
|----------|:------:|:---------:|:----------------------------------------:|
203-
| `light` | String | `'light'` | Color value used for light theme. |
204-
| `theme` | String | `'dark'`. | Color value used for dark theme. |
202+
| Property | Type | Value | Description |
203+
|----------|:------:|:---------:|:------------------------------------------------------------:|
204+
| `light` | String | `'light'` | Color value used for "light" theme. |
205+
| `dark` | String | `'dark'`. | Color value used for "dark" theme. |
206+
| `auto` | String | `'auto'`. | Auto-determine color scheme based on `prefers-color-scheme`. |
205207

206-
> **NOTE**: The `getColors()` function can be used in both, client components and server components.
208+
> **NOTE**: The `themes` object can be used in both, client components and server components.
207209
208-
For server components you can import `getColors()` like so:
210+
For server components you can import `themes` like so:
209211

210212
```jsx
211-
import { getColors } from '@designcise/next-theme-toggle/server';
213+
import { themes } from '@designcise/next-theme-toggle/server'
212214
```
213215

214216
For client components, you can import it like so:
215217

216218
```jsx
217-
import { getColors } from '@designcise/next-theme-toggle';
219+
import { themes } from '@designcise/next-theme-toggle'
218220
```
219221

220222
## Testing
@@ -237,8 +239,8 @@ $ yarn test
237239

238240
### Reporting
239241

240-
* File issues at https://github.com/designcise/next-theme-toggle/issues
241-
* Issue patches to https://github.com/designcise/next-theme-toggle/pulls
242+
- File issues at https://github.com/designcise/next-theme-toggle/issues
243+
- Issue patches to https://github.com/designcise/next-theme-toggle/pulls
242244

243245
### Troubleshooting Common Issues
244246

@@ -270,7 +272,7 @@ To fix this, you can add the folder where your CSS or SASS file is located. For
270272

271273
#### `Warning: Extra attributes from the server: class,style` in Console
272274

273-
This warning _only_ shows on dev build and _not_ in the production build. This happens because the injected script adds _additional_ `class` and `style` attributes to the `html` element which _do not_ originally exist on the server-side generated page, leading to a mismatch in the server-side and client-side rendered page.
275+
You can safely ignore this warning as it _only_ shows on dev build and _not_ in the production build. This happens because the injected inline script adds _additional_ `class` and `style` attributes to the `html` element which _do not_ originally exist on the server-side generated page, leading to a _mismatch_ in the server-side and client-side rendered page.
274276

275277
## Contributing
276278

@@ -282,5 +284,5 @@ https://github.com/designcise/next-theme-toggle/blob/main/LICENSE.md
282284

283285
## Resources
284286

285-
- [https://www.designcise.com/web/tutorial/how-to-create-non-flickering-dark-or-light-mode-toggle-in-next-js-using-localstorage](https://www.designcise.com/web/tutorial/how-to-create-non-flickering-dark-or-light-mode-toggle-in-next-js-using-localstorage).
286-
287+
- [How to Create Non-Flickering Dark/Light Mode Toggle in Next.js Using `localStorage`?](https://www.designcise.com/web/tutorial/how-to-create-non-flickering-dark-or-light-mode-toggle-in-next-js-using-localstorage)
288+
- [How to Create Non-Flickering Dark/Light Mode Toggle in Next.js Using Cookies?](https://www.designcise.com/web/tutorial/how-to-create-non-flickering-dark-or-light-mode-toggle-in-next-js-using-cookies).

0 commit comments

Comments
 (0)