The event listener added by window.matchMedia("(prefers-color-scheme: dark)") is not triggered in Safari on iOS 17 when the app is installed as a PWA
Asked Answered
A

1

6

Is it just me, or is there a bug in Safari on iOS 17 where the event listener added by window.matchMedia("(prefers-color-scheme: dark)") is not triggered? There was no such issue in Safari on iOS 16. The issue occurs only when the web app is installed on the home screen as a PWA. In the browser, everything is working fine.

The code:

  useEffect(() => {
    function handleColorSchemeChange(event) {
      // this listener won't trigger when the web app is installed as PWA on the iPhone's home screen
      const newColorScheme = event.matches ? "dark" : "light";
      setCurrentDarkMode(newColorScheme);
    }

    window
      .matchMedia("(prefers-color-scheme: dark)")
      .addEventListener("change", handleColorSchemeChange);

    return () => {
      window
        .matchMedia("(prefers-color-scheme: dark)")
        .removeEventListener("change", handleColorSchemeChange);
    };
  }, []);

I tried installing the PWA application (for example, timezones.digital) on an iPhone and toggled the system's dark/light mode

Aril answered 9/11, 2023 at 4:19 Comment(2)
This is definitely a bug in iOS 17. I've tried various ways to force the correct theme, but it appears that the value of prefers-color-scheme never changes from the one that the app launched with. Here's some discussion on Apple Dev forums developer.apple.com/forums/thread/…Giveandtake
This issue seems to be fixed with IOS 18Grillage
C
0

Findings

I found out by accident that opening another window from the PWA and closing it, will force the color scheme to be re-evaluated.

Another bug I found: moving back by swiping this other window (which also closes it), re-evaluates the color scheme but applies the <meta name="theme-color" ... > contents from the visited page to your PWA (!). This sometimes gave my app blue or red area's around the notch.

Workaround

As a workaround, you can open and close an external window. This will cause a minor flicker, but it's actually barely noticeable.

The challenge is finding a way to initiate this, because non-user-initiated calls will trigger the pop-up blocker. This ruled out my initial idea to attach this to the windows focus-event.

window.open('/colorscheme-fix.html');

where colorscheme-fix.html immediately closes itself (decorate this to your wishes, in case it shows unexpectedly or doesn't close for some reason):

<html>
  <script>window.close();</script>
</html>
Cantaloupe answered 4/4, 2024 at 20:13 Comment(3)
It is a nice idea, however it does not work on my phone. When debugging Chrome blocks it after one or two times, something like that probably also happens on iosGrillage
It is working on iOS. I have it in production and no issues. Why are you making assumptions and not actually testing?Cantaloupe
I have it on my phone and it does not work, not an assumption and something is apparently different.Grillage

© 2022 - 2025 — McMap. All rights reserved.