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

Current Color Scheme Hook #70

Open
alnavarrop99 opened this issue May 15, 2024 · 3 comments
Open

Current Color Scheme Hook #70

alnavarrop99 opened this issue May 15, 2024 · 3 comments
Assignees
Labels
hook Add or improve hooks

Comments

@alnavarrop99
Copy link
Contributor

Detect changes in the current color preference through Match Media and the classes defined in the root.

@dlcastillop dlcastillop added the hook Add or improve hooks label May 15, 2024
@alnavarrop99
Copy link
Contributor Author

I need to consult on what is the best way to implement this hook. In my research, I found three general cases where color schemes are defined. A list of the investigated methods and the benefits and limitations that come with them will be specified below:

  1. root.theme: custom classes or attributes on the root element (<html>).
    • Compatibility with old and modern browsers.
    • Use of multiple themes besides the typical light and dark.
  2. @media-color-scheme: media query colors.
    • Compatibility with modern browsers Browser Compatibility.
    • Emulation of only two color schemes: light and dark.
  3. localStorage: persistence of themes in the user's browser, and the value must be synchronized with the current value for proper functioning.

The strategies taken by development teams are based on using all of the above together, in most cases giving priority in the following order:

  • Setter: @media-color-scheme || root.theme -> root.theme -> localStorage
  • Getter: localStorage || @media-color-scheme -> root.theme

Example: Reference: Shadcn/ui
ThemeProvider.tsx

export function ThemeProvider({
  children,
  defaultTheme = 'system',
  storageKey = 'vite-ui-theme',
  ...props
}: ThemeProviderProps) {
  const [theme, setTheme] = useState<Theme>(
    () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
  )

  useEffect(() => {
    const root = window.document.documentElement

    root.classList.remove('light', 'dark')

    if (theme === 'system') {
      const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
        .matches
        ? 'dark'
        : 'light'

      root.classList.add(systemTheme)
      return
    }

    root.classList.add(theme)
  }, [theme])

  const value = {
    theme,
    setTheme: (theme: Theme) => {
      localStorage.setItem(storageKey, theme)
      setTheme(theme)
    },
  }

  return (
    <ThemeProviderContext.Provider {...props} value={value}>
      {children}
    </ThemeProviderContext.Provider>
  )
}

useTheme.ts:

export const useTheme = () => {
  const context = useContext(ThemeProviderContext)

  if (context === undefined)
    throw new Error('useTheme must be used within a ThemeProvider')

  return context
}

Questions:

  1. Priority order for theme selection.
  2. The hook will also handle a setter and synchronize all the previous permissible states ( localStorage and root.theme ).

@dlcastillop
Copy link
Member

  1. Priority order
  • Setter: @media-color-scheme || root.theme -> root.theme -> localStorage
  • Getter: localStorage || @media-color-scheme -> root.theme
  1. Yes, the hook will do all that

Thank you for your research! Very insightful. 🙌

@alnavarrop99
Copy link
Contributor Author

Perfect. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hook Add or improve hooks
Projects
None yet
Development

No branches or pull requests

2 participants