Theme-ui: Use `prefers-color-scheme` preference over localStorage color mode

Created on 23 Mar 2020  Â·  6Comments  Â·  Source: system-ui/theme-ui

Describe the bug

When toggling between light/dark mode on a supoprted device (in this case iOS, or MacOS via Firefox), Theme-UI's color scheme does not update. Updating the app to reflect the user's color mode requires the localstorage to be deleted.

but may I add, setting up dark/light mode in it's current state has been an absolute breeze - thank you!

To Reproduce
Steps to reproduce the behavior:

  1. Follow steps outlined in https://theme-ui.com/color-modes/
  2. Clear local storage and refresh
  3. App should render with your system preferences
  4. Try toggling between light/dark mode in your system preferences and watch the behaviour of the app.

Expected behavior

As I've enabled useColorSchemeMediaQuery, I would expect the color modes to behave as a media query (in other words, on user-agent change - much like a breakpoint).

Screenshots

An example in Theme-UI: (using useColorSchemeMediaQuery)

Demo site toggling between dark and light mode on an iPhone

Examples of 'expected behaviour' in non-Theme-UI applications

Twitter toggling dark and light mode

DuckDuckGo

Additional context
Related issues: https://github.com/system-ui/theme-ui/issues/367

enhancement

Most helpful comment

I agree, this would be awesome.

As a supplement, I went ahead and created a workaround to automatically update (_and override_) the color mode when the prefers-color-scheme media-query changes.

Feel free to use this for the time being if it's helpful.

useEffect(() => {
const switchMode = e => {
const isDarkMode = e.matches
isDarkMode ? setMode("dark") : setMode("light")
}
const darkModeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
darkModeMediaQuery.addListener(switchMode)
}, [setMode])

That said, something I have noticed (_and I'm not sure if this is the case for those using Gatsby_), but when a default theme is set and useColorSchemeMediaQuery is true, I still seem to get a flicker from light to dark upon first load when prefers-color-scheme: dark — for reference, this happens _with_ the Gatsby package installed..

This issue happens when visiting the page for the first time, I'm assuming it may be due to the color mode method initially looking for a stored value in local storage? Could there be a better way to prevent this from happening?

All 6 comments

Hey Joe! This is actually the expected behavior for Theme UI right now, though some folks were having a conversation in #624 about changing it the way you requested (which personally I'd love to see).

@lachlanjc Ah I did take a glance at that! I thought it was focussed more on setting useColorSchemeMediaQuery to true than the behaviour?

I agree, this would be awesome.

As a supplement, I went ahead and created a workaround to automatically update (_and override_) the color mode when the prefers-color-scheme media-query changes.

Feel free to use this for the time being if it's helpful.

useEffect(() => {
const switchMode = e => {
const isDarkMode = e.matches
isDarkMode ? setMode("dark") : setMode("light")
}
const darkModeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
darkModeMediaQuery.addListener(switchMode)
}, [setMode])

That said, something I have noticed (_and I'm not sure if this is the case for those using Gatsby_), but when a default theme is set and useColorSchemeMediaQuery is true, I still seem to get a flicker from light to dark upon first load when prefers-color-scheme: dark — for reference, this happens _with_ the Gatsby package installed..

This issue happens when visiting the page for the first time, I'm assuming it may be due to the color mode method initially looking for a stored value in local storage? Could there be a better way to prevent this from happening?

Extending on @dannykeane's snippet I think it should cleanup on unmount

import { jsx, useColorMode } from "theme-ui";
// ..
const [, setColorMode] = useColorMode();
useEffect(() => {
  const switchMode = (e) => {
    const isDarkMode = e.matches;
    isDarkMode ? setMode("dark") : setMode("light");
  };
  const darkModeMediaQuery = window.matchMedia(
    "(prefers-color-scheme: dark)"
  );
  darkModeMediaQuery.addListener(switchMode);
  // cleanup on unmount
  return () => darkModeMediaQuery.removeEventListener(switchMode);
}, [setMode]);

Please correct me if I'm wrong

If there is interest and if it will be some time until this behavior is fleshed-out in Them-UI, I will publish a gatsby plugin that will provide intuitive default behavior for color modes with configuration options to alter the behavior.

My initial thoughts are that it, most importantly, will detect the user's preferred color mode and adapt UI accordingly (as described by @joe-bell above), have configuration option to override localstorage mode value, provide options for initial color mode behaviors, etc.

As a website user, I 100% expect websites to honor my devices' mode settings and adjust their modes automatically.

I'm a few months late in this discussion, but, after reading the entire thing, I still can't seem to figure out if it's implemented yet or no. According to version1 roadmap, it's not, so, if that's the case, how exactly can I get this to work in Gatsby?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

K-Kit picture K-Kit  Â·  4Comments

muhajirdev picture muhajirdev  Â·  3Comments

jxnblk picture jxnblk  Â·  4Comments

VinSpee picture VinSpee  Â·  3Comments

8eecf0d2 picture 8eecf0d2  Â·  3Comments