0

I understand how to change my web site theme programmatically or using states with Antd and react, but how do I respect user settings for dark mode, without having to reload the web site for the theme to apply, when browser changes mode automatically based on system settings.

My understanding is that since Antd 5.x moved to cssinjs, I can no longer use theme colors in my css anymore, so @media-query isn't an option, but I might be wrong.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Yann Bizeul
  • 403
  • 3
  • 10
  • maybe this reference helps : https://stackoverflow.com/questions/69956563/how-to-detect-if-a-browser-supports-dark-mode – andika_kurniawan Aug 16 '23 at 08:51
  • It explains how to use JS to check for dark mode, but not really helpful in this context, thanks though ! – Yann Bizeul Aug 16 '23 at 10:15
  • Did you try this? [Preset Algorithms](https://ant.design/docs/react/customize-theme#use-preset-algorithms). You need to store the algorithm in state and also attach listener on `window.matchMedia('(prefers-color-scheme: dark)')`. Based on user settings, set the algorithm. There's no need to reload the page – Muhammad Nouman Rafique Aug 16 '23 at 12:55

1 Answers1

2

Like what one of the commenters mentioned, add a listener to listen on changes in the system's color scheme. If it changes, then change the darkMode state so React can re-evaluate and propagate it to the ConfigProvider.

Be sure to also include another useEffect to do the initial evaluation for setting darkMode on initial page load.

Here's a minimum viable example for implementing it:

import React, { useEffect, useState, useCallback } from "react";
import { Button, ConfigProvider, Input, Space, theme } from "antd";

const App: React.FC = () => {
  const [darkMode, setDarkMode] = useState(false);
  const windowQuery = window.matchMedia("(prefers-color-scheme:dark)");

  const darkModeChange = useCallback((event: MediaQueryListEvent) => {
    console.log(event.matches ? true : false);
    setDarkMode(event.matches ? true : false);
  }, []);

  useEffect(() => {
    windowQuery.addEventListener("change", darkModeChange);
    return () => {
      windowQuery.removeEventListener("change", darkModeChange);
    };
  }, [windowQuery, darkModeChange]);

  useEffect(() => {
    console.log(windowQuery.matches ? true : false);
    setDarkMode(windowQuery.matches ? true : false);
  }, []);

  return (
    <ConfigProvider
      theme={{
        algorithm: darkMode ? theme.darkAlgorithm : theme.compactAlgorithm
      }}
    >
      <Space>
        <Input placeholder="Please Input" />
        <Button type="primary">Submit</Button>
      </Space>
    </ConfigProvider>
  );
};

export default App;

DEMO

Scratch'N'Purr
  • 9,959
  • 2
  • 35
  • 51
  • This seems very promising, thank you, it almost works, the only issue is regular texts, like in

    are still black, even though ant component are all good, see https://codesandbox.io/s/sandpack-project-forked-s68hn4?file=/app.tsx
    – Yann Bizeul Aug 17 '23 at 08:29
  • I ended up adding color: white to the global html css and it's working – Yann Bizeul Aug 17 '23 at 08:48
  • I just noticed something that's not great, the page actually flickers white then black when loading and couldn't find a way to fix this. – Yann Bizeul Aug 22 '23 at 19:36
  • @YannBizeul do you have a demo of your code in codesandbox? My gut instinct tells me that it's because by default darkMode is initally set to false, but after page loads, the `useEffect` triggers which causes the state to update. – Scratch'N'Purr Aug 26 '23 at 17:02
  • Yes you can use the same one https://codesandbox.io/s/sandpack-project-forked-s68hn4?file=/app.tsx switching it to true doesn't help :-/ – Yann Bizeul Aug 26 '23 at 23:01
  • Ok, so the flicker is just a symptom of the initial load which isn't possible to fix. Your browser determines a default background color for blank web pages. This initial default color is white in most cases (unless you change the theme of your browser). After the useEffect hook runs, the web app's theme colors are applied which override the browser's default background color. Does the flicker occur on subsequent page reload? – Scratch'N'Purr Aug 27 '23 at 14:56
  • Yep it does every time you refresh. That’s the problem with css-in-js I think. We outsmart the browser and forcibly set the bg color but without giving a dark theme @media alternative. I’ll try and find if there is a way to find the right style for the default background and create a dark theme alt in the regular css. Bummer. – Yann Bizeul Aug 27 '23 at 18:42
  • I think I made it slightly better by forcing "#root .ant-layout { background-color: black;}". See difference between https://ybfeed.tynsoe.org (old) and https://ybfeed-dev.tynsoe.org (new). But I would have to do that for every component, not just html/body... – Yann Bizeul Aug 27 '23 at 20:02
  • That's so cumbersome, having to do it for every component :( – Scratch'N'Purr Aug 28 '23 at 13:48