ant-design icon indicating copy to clipboard operation
ant-design copied to clipboard

Toggle dark with @media (prefers-color-scheme: dark) such like ThemeProvider

Open Innei opened this issue 5 years ago • 32 comments

  • [ ] I have searched the issues of this repository and believe that this is not a duplicate.

What problem does this feature solve?

The theme color changes according to the system theme

主题色根据系统主题变换

What does the proposed API look like?

import 'antd/dist/antd.dark.css'

加载这个样式后,并不是直接应用到全局,而是可以通过一个类名,比如 <body class="dark">来切换主题。

再或者是把 less 颜色变量暴露到 css var

:root {
    --red: #ff6347;
    --yellow: #ffc107;
    --blue: #6495ed;
}
@media screen and (prefers-color-scheme: dark) {
:root {
    color: #ddd;
    --red: #ad1a00;
    --blue: #1346a4;
  }
}

Innei avatar Apr 18 '20 08:04 Innei

Similar #16661

Innei avatar Apr 18 '20 08:04 Innei

I'm doing this in .less file and it works fine.

@media (prefers-color-scheme: dark) {
  @import "~antd/dist/antd.dark.less";
}

for toggle dark mode I do this but get a little color bug.

.dark-theme {
  @import "~antd/dist/antd.dark.less";
}

ntsd avatar Apr 23 '20 19:04 ntsd

I only used scss and css, and is there a way to use it in scss?

Innei avatar Apr 24 '20 23:04 Innei

I only used scss and css, and is there a way to use it in scss?

I'm not sure but you can try it

@media (prefers-color-scheme: dark) {
  @import "~antd/dist/antd.dark";
}

Just look at the node_modules/antd/dist/ directory. Compiled styles is here.

ntsd avatar Apr 25 '20 05:04 ntsd

I only used scss and css, and is there a way to use it in scss?

I'm not sure but you can try it

@media (prefers-color-scheme: dark) {
  @import "~antd/dist/antd.dark";
}

Just look at the node_modules/antd/dist/ directory. Compiled styles is here.

I had to try, but failed. If do this, It will not bundle the antd.dark css file.

Innei avatar Apr 26 '20 04:04 Innei

I only used scss and css, and is there a way to use it in scss?

I'm not sure but you can try it

@media (prefers-color-scheme: dark) {
  @import "~antd/dist/antd.dark";
}

Just look at the node_modules/antd/dist/ directory. Compiled styles is here.

I had to try, but failed. If do this, It will not bundle the antd.dark css file.

Have you found a solution to import the file in SASS ?

dukesx avatar Apr 29 '20 17:04 dukesx

I only used scss and css, and is there a way to use it in scss?

I'm not sure but you can try it

@media (prefers-color-scheme: dark) {
  @import "~antd/dist/antd.dark";
}

Just look at the node_modules/antd/dist/ directory. Compiled styles is here.

I had to try, but failed. If do this, It will not bundle the antd.dark css file.

Have you found a solution to import the file in SASS ?

Sorry, I don't have a good idea. I override all colors which antd used in my scss.

Innei avatar Apr 30 '20 04:04 Innei

I found a temporary and easy solution, I copied the css files of antd and antd dark and created two seperate scss file, each had a prefers color scheme only. So in prefers color scheme dark, I put all the dark css and in light the default css. So now each time I change my windows theme to light or dark, the website changes color flawlessly without any issue or extra work of modifying Less. 👍

dukesx avatar Apr 30 '20 09:04 dukesx

@dukesx How to ensure that it can still work normally when update the antd version?

yoyo837 avatar Apr 30 '20 09:04 yoyo837

Look, this is a trade off you will have to make. Ant Design doesn't have a dark mode toggle and it is impossible to toggle it live. Until Ant Design team wakes up and finds out a way to implement dark mode the right way, this is the easiest and "working" way. You can write custom css but that's time taking and your color scheme probably won't fit the components correctly. There is another way that you use nodejs to watch antd folder, if changes happen, you copy all content inside antd.css and antd.dark.css to your new files so that you keep it updated.

dukesx avatar Apr 30 '20 11:04 dukesx

proposed

@primary-color: #1890ff;
@primary-dark-color: #1890ff;

body {
  --color-primary: @primary-color;
}
body.dark {
  --color-primary: @primary-dark-color;
}
body.auto {
  --color-primary: @primary-color;
  @media screen and (prefers-color-scheme: dark) {
    --color-primary: @primary-dark-color;
  }
}

a {
  color: var(--color-primary);
}

Default light theme

Add dark className to body to switch to dark mode

<body class="dark">

Add auto className to body to follow the system

<body class="auto">

tolking avatar May 02 '20 04:05 tolking

AntDesign should really look into a ThemeProvider, or extend the ConfigProvider, it is really annoying to be forced to use css vars to customise the theme (considering it's not supported on all browsers).

a system like @zeit-ui/react would work so much better for dynamic theming. Take my example in this thread, we have multiple clients, with their own branding colors, then we have a dark and light mode as well. Many methods have been tested but it simply does not scale, eg; injecting the less vars as suggested in the docs.

The only performant solution I found was to do a complete cssvars override of the antd components.

https://github.com/PaulPCIO/antd-themed-cssvars

@afc163 🙏

paul-vd avatar May 27 '20 14:05 paul-vd

AntDesign should really look into a ThemeProvider, or extend the ConfigProvider, it is really annoying to be forced to use css vars to customise the theme (considering it's not supported on all browsers).

Extending the ConfigProvider would be ideal. We have a similar requirement to your example re light/dark/whitelabeling and i'd really rather not have to go down the scss route. Proper dynamic theming is really the last piece that's missing from Antd

cill-i-am avatar Jun 16 '20 19:06 cill-i-am

I stumbled on this issue and spent three days trying to find a decent solution. Since converting all variables to CSS vars and using SCSS is out of my scope, I had to find an alternative solution. Injecting styles with Less was not scalable since it killed my app's performance. What I first decided was to just do as the Ant team does for their docs: prefetch styles, use theme-switcher, and change the CSS directly on the HTML.

At first, I was very happy with how fast it was compared to solutions using less.modifyVars, however, I hit a block since a flickering occurred when loading and injecting the styles; also, styles that were not Antd conflicted because of the order. Therefore, to solve this I created a library similar to theme-switcher, but using React's API. It comes with some extra features like selecting injection point and adding stylesheet injection status (to display a loading to avoid the flickering).

You can check the library here: react-css-theme-switcher. It currently has 100% code coverage and is open to suggestions and PRs.

Currently, I can customize the theme however I want and my app performance was not as impacted. It is a good solution for the meantime.

I'll be writing a blog post on how to get custom CSS stylesheets and implement react-css-theme-switcher. As soon as it is written, I will update this comment with the link.

TL;DR: My solution to dynamic theming was to use react-css-theme-switcher and swap CSS stylesheets with the theme I need within React.

Edit: Here is the blog post for those who want to read through a tutorial of the solution.

JoseRFelix avatar Jun 21 '20 03:06 JoseRFelix

I stumbled on this issue and spent three days trying to find a decent solution. Since converting all variables to CSS vars and using SCSS is out of my scope, I had to find an alternative solution. Injecting styles with Less was not scalable since it killed my app's performance. What I first decided was to just do as the Ant team does for their docs: prefetch styles, use theme-switcher, and change the CSS directly on the HTML.

At first, I was very happy with how fast it was compared to solutions using less.modifyVars, however, I hit a block since a flickering occurred when loading and injecting the styles; also, styles that were not Antd conflicted because of the order. Therefore, to solve this I created a library similar to theme-switcher, but using React's API. It comes with some extra features like selecting injection point and adding stylesheet injection status (to display a loading to avoid the flickering).

You can check the library here: react-css-theme-switcher. It currently has 100% code coverage and is open to suggestions and PRs.

Currently, I can customize the theme however I want and my app performance was not as impacted. It is a good solution for the meantime.

I'll be writing a blog post on how to get custom CSS stylesheets and implement react-css-theme-switcher. As soon as it is written, I will update this comment with the link.

TL;DR: My solution to dynamic theming was to use react-css-theme-switcher and swap CSS stylesheets with the theme I need within React.

Looks nice. Looking forward to reading it!

cill-i-am avatar Jun 22 '20 15:06 cill-i-am

Same issue here, I use injectType: 'lazyStyleTag' to get around this. But the drawback is quite significant, both of the CSS is at my bundle righty now without any tree shaking.

marklai1998 avatar Sep 04 '20 15:09 marklai1998

This should really be a priority, runtime theming is a pretty much used feature nowadays. Spent several hours to make it work, using a workaround.

The only performant solution I found was to do a complete cssvars override of the antd components.

https://github.com/PaulPCIO/antd-themed-cssvars

Really appreciate the work done here. Too bad that since it's not officially supported, something might break the theme in future releases (minor changes to antd, new components).

For anyone that wants to use the @PaulPCIO approach:

Usage:

  1. git clone the repo and copy all files locally (node-sass is required for CRA)
  2. Import index.scss in your App.js (or relevant) file.
  3. Create themes.scss at index level, import it at the bottom of index.
  4. In themes.scss customize your themes:
.light-theme {
  --primary-color: #007bff;
  --disabled-color: #6c757d;
  --bodyBackground: #ffffff;
  // ... etc
}

.dark-theme {
  --primary-color: #000000;
  --disabled-color: #4a4a4b;
  --bodyBackground: #000000;
  // ... etc
}
  1. Assign light/dark className to App, change on button event click.

nhevia avatar Oct 27 '20 01:10 nhevia

+1 to ThemeProvider

saostad avatar Nov 12 '20 02:11 saostad

Hi community,

Why not just replace all less variables with css vars.

This improvement will allow for super flexible customization tools. It will increase total styles size a little, but it really helps to customize Ant Design globally or any individual piece of the UI.

// https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
@primary-color: var(--antd-blue-6);
@success-color: var(--antd-green-6);
@font-size-sm: var(--antd-font-size-sm);

and define this variables in global styles:

// https://github.com/ant-design/ant-design/blob/master/components/style/core/global.less
:root {
  --antd-blue-6: #0000ff;
  --antd-green-6: #00ff00;
  --antd-font-size-sm: 12px;
}

then we can easily redefine this variables for any pieces of the code:

// global dark theme
body.dark-theme {
  --antd-blue-6: #ffffff;
}
// there is all antd components inside .header will inherit redefined --antd-blue-6 color
.header {
   --antd-blue-6: green;
}

ghost avatar Feb 23 '21 23:02 ghost

I just created a PR to support automatic dark mode through theme generation.

You can use the umi-plugin-antd-theme plugin to generate an automatic dark mode theme.

chenshuai2144/antd-pro-merge-less/pull/24

yisiqi avatar Mar 10 '21 04:03 yisiqi

@yisiqi, will that work for those of use using Create React App or Next.js?

cill-i-am avatar Mar 22 '21 10:03 cill-i-am

CSS Variable support is pretty widespread these days, if the less variables could all just be replaced with CSS Variables I think it would simplify a lot of use cases. Not to mention CRA and Storybook among others not playing nicely with LESS out of the box. I currently have a big file where I assign all antd css variables to less variables to help get around some of these issues.

IronSean avatar Apr 13 '21 15:04 IronSean

Hello people!

So why we have this awesome PR abandoned at all? #26139

@tigran-ucraft and @baldarian can you tell was whats happened in your work?

I was starting to create a PR with something like it 🙃

menosprezzi avatar Jun 23 '21 15:06 menosprezzi

I think that this is very good issue. Because css variables helps us to change theme at runtime. Few months ago, I need an UI framework to support color changes for multi tenant app. I could not find a good solution for antd to change colors at runtime.

emindeniz99 avatar Jul 05 '21 08:07 emindeniz99

It looks like ant design comes with an experimental ConfigProvider nowadays, which accepts a theme prop: https://ant.design/docs/react/customize-theme-variable

I however haven't found a way to export all nextjs dark theme variables, as they are only defined in deeply nested less files

stam avatar Nov 01 '21 11:11 stam

(() => {
  const isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)").matches;
  import(`antd/dist/antd.${isDarkTheme ? 'dark.' : ''}css`);
})();


ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

Write your root component. it works :)

doong-jo avatar Jan 14 '22 07:01 doong-jo

@afc163 any update on being able to toggle darkmode with ConfigProvider? 🙏

MarkLyck avatar Jan 25 '22 23:01 MarkLyck

@media (prefers-color-scheme: dark) { @import "~antd/dist/antd.dark.less"; }

Does this need to set the class on the body? I tried it but it didn't work. I'm a little confused. Thanks

ISBN9527 avatar May 17 '22 07:05 ISBN9527

I used this to enable dynamic dark mode in a scss file and it's working:

@use 'sass:meta';

body:not(.dark) {
  @include meta.load-css('node_modules/antd/dist/antd.css', $with: null);
}

body.dark {
  @include meta.load-css('node_modules/antd/dist/antd.dark.css', $with: null);
}

The dark mode switch just adds a dark class to the body tag.

Demo: https://arifszn.github.io

arifszn avatar May 17 '22 07:05 arifszn

Use SCSS Nesting import:

https://sass-lang.com/documentation/at-rules/import#nesting

Create a antd.dark.scss

@media (prefers-color-scheme: dark) {
  @import '~antd/dist/antd.dark';
}

Import antd.dark.scss in your JS index.ts

import React from 'react';

// This for Light
import './antd.less';
// This for Dark
import './antd.dark.scss';

https://github.com/huacnlee/autocorrect/commit/a827af4fddc98d54ecff104a1a8b805f6a74b760

huacnlee avatar Jun 18 '22 02:06 huacnlee