Skip to content

Commit d13bbf4

Browse files
Aaronclaude
andcommitted
web: follow system theme changes when no user override
Listen for prefers-color-scheme media query changes and update the theme automatically when the user hasn't explicitly toggled. Toggling back to match the system theme clears the stored override. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2f0a235 commit d13bbf4

1 file changed

Lines changed: 36 additions & 7 deletions

File tree

web/src/ThemeContext.tsx

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,60 @@ export function useTheme(): ThemeContextType {
1818

1919
const STORAGE_KEY = "tilt-theme"
2020

21+
function getSystemTheme(): Theme {
22+
return window.matchMedia?.("(prefers-color-scheme: light)").matches
23+
? "light"
24+
: "dark"
25+
}
26+
2127
function getInitialTheme(): Theme {
2228
const stored = localStorage.getItem(STORAGE_KEY)
2329
if (stored === "light" || stored === "dark") {
2430
return stored
2531
}
26-
if (window.matchMedia?.("(prefers-color-scheme: light)").matches) {
27-
return "light"
28-
}
29-
return "dark"
32+
return getSystemTheme()
33+
}
34+
35+
function hasUserOverride(): boolean {
36+
return localStorage.getItem(STORAGE_KEY) !== null
3037
}
3138

3239
export function ThemeProvider(props: PropsWithChildren<{}>) {
3340
const [theme, setTheme] = useState<Theme>(getInitialTheme)
34-
3541
useEffect(() => {
3642
if (theme === "light") {
3743
document.body.setAttribute("data-theme", "light")
3844
} else {
3945
document.body.removeAttribute("data-theme")
4046
}
41-
localStorage.setItem(STORAGE_KEY, theme)
4247
}, [theme])
4348

49+
// Follow system theme changes when user hasn't explicitly chosen
50+
useEffect(() => {
51+
const mq = window.matchMedia?.("(prefers-color-scheme: light)")
52+
if (!mq) return
53+
54+
function onChange() {
55+
if (!hasUserOverride()) {
56+
setTheme(getSystemTheme())
57+
}
58+
}
59+
60+
mq.addEventListener("change", onChange)
61+
return () => mq.removeEventListener("change", onChange)
62+
}, [])
63+
4464
const toggleTheme = useCallback(() => {
45-
setTheme((prev) => (prev === "dark" ? "light" : "dark"))
65+
setTheme((prev) => {
66+
const next = prev === "dark" ? "light" : "dark"
67+
// If toggling back to the system theme, clear the override
68+
if (next === getSystemTheme()) {
69+
localStorage.removeItem(STORAGE_KEY)
70+
} else {
71+
localStorage.setItem(STORAGE_KEY, next)
72+
}
73+
return next
74+
})
4675
}, [])
4776

4877
return (

0 commit comments

Comments
 (0)