Skip to content

Commit 8c78733

Browse files
committed
chore: update useDark hook
1 parent c4b3dc6 commit 8c78733

File tree

2 files changed

+78
-10
lines changed

2 files changed

+78
-10
lines changed

playground/src/components/appearance-switch.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import { useDark } from '@/hooks/use-dark'
44

55
export function AppearanceSwitch({ className = '' }: { className?: string }) {
6-
const { toggleDark } = useDark()
6+
const { toggleDark } = useDark({
7+
disableTransition: true,
8+
disableTransitionExclude: ['.i-lucide-sun', '.i-lucide-moon'],
9+
})
710

811
return (
912
<button type="button" onClick={toggleDark} className={`flex ${className}`}>

playground/src/hooks/use-dark.ts

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,77 @@ function useSystemDark() {
2323
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
2424
}
2525

26+
/**
27+
* credit: https://github.com/pacocoursey/next-themes/blob/cd67bfa20ef6ea78a814d65625c530baae4075ef/packages/next-themes/src/index.tsx#L285
28+
*/
29+
function disableAnimation(disableTransitionExclude: string[] = []) {
30+
const css = document.createElement('style')
31+
css.append(
32+
document.createTextNode(
33+
`
34+
*${disableTransitionExclude.map(s => `:not(${s})`).join('')} {
35+
-webkit-transition: none !important;
36+
-moz-transition: none !important;
37+
-o-transition: none !important;
38+
-ms-transition: none !important;
39+
transition: none !important;
40+
}
41+
`,
42+
),
43+
)
44+
document.head.append(css)
45+
46+
return () => {
47+
// Force restyle
48+
;(() => window.getComputedStyle(document.body))()
49+
50+
// Wait for next tick before removing
51+
setTimeout(() => {
52+
css.remove()
53+
}, 1)
54+
}
55+
}
56+
57+
export type Options = {
58+
/**
59+
* @default "use-dark"
60+
*/
61+
storageKey?: string
62+
63+
/**
64+
* @default false
65+
*/
66+
disableTransition?: boolean
67+
68+
/**
69+
* @default []
70+
*/
71+
disableTransitionExclude?: string[]
72+
73+
/**
74+
* @default isDark => document.documentElement.classList.toggle("dark", isDark)
75+
*/
76+
applyDarkMode?: (isDark: boolean) => void
77+
}
78+
2679
const themeOptions = ['system', 'light', 'dark'] as const
27-
export type Theme = (typeof themeOptions)[number]
80+
type Theme = (typeof themeOptions)[number]
2881

29-
function isDarkMode(setting?: Theme | null, isSystemDark?: boolean) {
30-
return setting === 'dark' || (isSystemDark && setting !== 'light')
82+
function isDarkMode(setting?: Theme | null, isSystemDark?: boolean | null) {
83+
return setting === 'dark' || (!!isSystemDark && setting !== 'light')
3184
}
3285

33-
export function useDark(themeKey = 'use-dark') {
34-
const [theme, setTheme] = useLocalStorage<Theme>(themeKey, 'system')
86+
export function useDark(options?: Options) {
87+
const {
88+
storageKey = 'use-dark',
89+
disableTransition = false,
90+
disableTransitionExclude = [],
91+
applyDarkMode = (isDark: boolean) => {
92+
document.documentElement.classList.toggle('dark', isDark)
93+
},
94+
} = options ?? {}
95+
96+
const [theme, setTheme] = useLocalStorage<Theme>(storageKey, 'system')
3597
const isSystemDark = useSystemDark()
3698

3799
const isDark = useMemo(
@@ -40,18 +102,21 @@ export function useDark(themeKey = 'use-dark') {
40102
)
41103

42104
const toggleDark = () => {
105+
const enable = disableTransition
106+
? disableAnimation(disableTransitionExclude)
107+
: null
108+
43109
if (theme === 'system')
44110
setTheme(isSystemDark ? 'light' : 'dark')
45111
else
46112
setTheme('system')
113+
114+
enable?.()
47115
}
48116

49117
useEffect(() => {
50118
const isDark = isDarkMode(theme, isSystemDark)
51-
if (isDark)
52-
document.documentElement.classList.toggle('dark', true)
53-
else
54-
document.documentElement.classList.toggle('dark', false)
119+
applyDarkMode(isDark)
55120

56121
if (
57122
(theme === 'dark' && isSystemDark)

0 commit comments

Comments
 (0)