Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use media query value when useColorSchemeMediaQuery is set to 'system' #1981

Merged
Merged
42 changes: 39 additions & 3 deletions packages/color-modes/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, {
useState,
useMemo,
SetStateAction,
useCallback,
} from 'react'
import {
jsx,
Expand All @@ -29,6 +30,8 @@ import {
} from './custom-properties'

const STORAGE_KEY = 'theme-ui-color-mode'
const DARK_QUERY = '(prefers-color-scheme: dark)'
const LIGHT_QUERY = '(prefers-color-scheme: light)'

declare module '@theme-ui/core' {
export interface ThemeUIContextValue {
Expand Down Expand Up @@ -64,10 +67,10 @@ const storage = {

const getPreferredColorScheme = (): 'dark' | 'light' | null => {
if (typeof window !== 'undefined' && window.matchMedia) {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
if (window.matchMedia(DARK_QUERY).matches) {
return 'dark'
}
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
if (window.matchMedia(LIGHT_QUERY).matches) {
return 'light'
}
}
Expand Down Expand Up @@ -104,7 +107,11 @@ const TopLevelColorModeProvider = ({
document.documentElement.classList.remove('theme-ui-' + stored)
}

if (stored && stored !== colorMode) {
if (
useColorSchemeMediaQuery !== 'system' &&
stored &&
stored !== colorMode
) {
colorMode = stored
setColorMode(stored)
}
Expand All @@ -117,6 +124,35 @@ const TopLevelColorModeProvider = ({
}
}, [colorMode, useLocalStorage])

const setPreferredColorScheme = useCallback(() => {
const preferredColorScheme = getPreferredColorScheme()
setColorMode(preferredColorScheme || initialColorModeName)
}, [initialColorModeName])

useEffect(() => {
if (useColorSchemeMediaQuery === 'system' && window.matchMedia) {
// It doesn't matter if we add the listener only to the dark media query
// Because in our callback function we'll check for both media queries (light and dark).
const darkMQL = window.matchMedia(DARK_QUERY)
if (typeof darkMQL.addEventListener === 'function') {
darkMQL.addEventListener('change', setPreferredColorScheme)
} else if (typeof darkMQL.addListener === 'function') {
darkMQL.addListener(setPreferredColorScheme)
}
}

return () => {
if (useColorSchemeMediaQuery === 'system' && window.matchMedia) {
const darkMQL = window.matchMedia(DARK_QUERY)
if (typeof darkMQL.removeEventListener === 'function') {
darkMQL.removeEventListener('change', setPreferredColorScheme)
} else if (typeof darkMQL.removeListener === 'function') {
darkMQL.removeListener(setPreferredColorScheme)
}
}
}
}, [useColorSchemeMediaQuery, setPreferredColorScheme])

if (process.env.NODE_ENV !== 'production') {
if (
outerTheme.colors?.modes &&
Expand Down
2 changes: 1 addition & 1 deletion packages/css/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface ThemeUIConfig {
/**
* Initializes the color mode based on the prefers-color-scheme media query
*/
useColorSchemeMediaQuery?: boolean
useColorSchemeMediaQuery?: 'system' | 'initial' | true /* same as 'initial' for compat */ | false

/**
* Adds a global box-sizing: border-box style
Expand Down
7 changes: 6 additions & 1 deletion packages/docs/src/pages/color-modes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ export default (props) => {
<button
onClick={(e) => {
setColorMode(colorMode === 'default' ? 'dark' : 'default')
}}>
}}
>
Toggle {colorMode === 'default' ? 'Dark' : 'Light'}
</button>
</header>
Expand Down Expand Up @@ -263,6 +264,10 @@ enabled by default.
}
```

- To enable the color mode to update when a user's current
`prefers-color-scheme` media query value changes, set
`useColorSchemeMediaQuery` to `'system'`.

### Disable persisting color mode on `localStorage`

To disable `localStorage`, add the `useLocalStorage: false` flag to your theme
Expand Down