docusaurus
docusaurus copied to clipboard
Reset to OS theme / colorMode should possible
Have you read the Contributing Guidelines on issues?
- [X] I have read the Contributing Guidelines on issues.
Prerequisites
- [X] I'm using the latest version of Docusaurus.
- [X] I have tried the
npm run clear
oryarn clear
command. - [X] I have tried
rm -rf node_modules yarn.lock package-lock.json
and re-installing packages. - [X] I have tried creating a repro with https://new.docusaurus.io.
- [X] I have read the console error message carefully (if applicable).
Description
I don't think this is a bug as much as intended behavior, but I think that intention can be confusing so I'll post an issue anyways.
Docusaurus is currently handling themes both using local storage, and syncing the user preferences. This often leads to the theme going out of sync:
https://github.com/facebook/docusaurus/blob/cba8be01a3ae058a328004ba281d28c47ac0a25c/packages/docusaurus-theme-classic/src/index.ts#L42-L52
Reproducible demo
https://preferred-theme-test.vercel.app
Steps to reproduce
Lets say at day I toggle the theme on the website to use light mode. That would store a KV-pair { "theme": "light" }
in localstorage. I leave the page and when I come back at night my device is now using darkmode, so I'd probably want the page to load in dark mode. However, since the KV-pair is in localstorage, that takes precedence so it loads light mode instead.
Expected behavior
I'd want the page to load in dark mode.
Actual behavior
The page loads the KV pair from localstorage and uses light mode.
Your environment
- Public source code: https://github.com/juliusmarminge/preferred-theme-test
- Public site URL: https://preferred-theme-test.vercel.app
- Docusaurus version used: 2.1.0
- Environment name and version (e.g. Chrome 89, Node.js 16.4): Safari, Chrome
- Operating system and version (e.g. Ubuntu 20.04.2 LTS): MacOS 12
Self-service
- [ ] I'd be willing to fix this bug myself.
I can submit a PR for this if you agree with my standpoint, but since I think this is intended I won't do anything until I hear back from you
The UX is quite hard to design. How should the localStorage value be deemed stale? Or should we not persist that value at all?
The UX is quite hard to design. How should the localStorage value be deemed stale? Or should we not persist that value at all?
I usually don't persist it at all tbf but there is definitely a compromise to be made.
+ If you don't use localstorage, the theme is always in sync with the user's device,
- However say the user is on dark mode but want the page in light, the theme would reset to dark on every pageload.
I'd take the payoff and assume that users who want light pages probably has light mode enabled on their device
On https://trpc.io we override this behavior by a bit hacky script implemented here https://github.com/trpc/trpc/pull/2664/files#diff-153fbbe997755581673d0a5178895a70051650f2ed919e23186dbea31b7602ef
I think refreshing definitely should not change the color mode—that would feel really shaky. The only question is whether we can invalidate the localStorage somehow in the following case:
- Light mode is selected—either as default or as user choice
- User's device switched from light mode to dark mode the next time the site is opened
I think to do that we have to persist the current system theme in local storage as well?
(Also, I use system-wide dark mode, but the dark mode on some sites is just horrible that I use light mode on those instead.)
I think refreshing definitely should not change the color mode—that would feel really shaky. The only question is whether we can invalidate the localStorage somehow in the following case:
- Light mode is selected—either as default or as user choice
- User's device switched from light mode to dark mode the next time the site is opened
I think to do that we have to persist the current system theme in local storage as well?
(Also, I use system-wide dark mode, but the dark mode on some sites is just horrible that I use light mode on those instead.)
That sounds like a good idea. We could store a blob
{"theme": "light", "userPreferred": "dark"}
and check if the mediaMatch === userPreferred in some way
I could play around and file a PR if you like?
Ah, sure, feel free to!
Hey,
To me, once a user selects light mode, we persist it and the site should forever use that color. We definitively want to keep using the explicit choice of the user. If user wants to reset to OS setting, this choice must be explicit.
If the user wants to revert to the system theme, then there must be an explicit option to do so, like on many sites.
CF https://developer.mozilla.org/
![CleanShot 2022-09-30 at 16 06 11@2x](https://user-images.githubusercontent.com/749374/193287734-74765262-e31e-4b58-b0df-c245a4b79b38.png)
The problem is that we only have 2 values in the current switch, and the user has no ability to reset to OS switch. I'm more likely willing to add a 3rd icon in our state machine to make it at least possible to revert to os theme (ie erase the localstorage value).
It's not a very conventional UX to have and I'm not sure we can find a good icon representing OS theme though 😅 maybe a mixture of both icons at the same time? Or maybe we'd want to use a dropdown like many other websites 🤷♂️ some people probably won't like it.
Not 100% related to this exact problem but In general I like the ideas expressed here:
- https://twitter.com/bramusblog/status/1529453919344156672
- https://www.bram.us/2022/05/25/dark-mode-toggles-should-be-a-browser-feature/
What we want is more likely to give the ability to reset to "inherit"
Some people won’t like any chosen UI, but this is a tri-state piece of data. Any UI that allows switching between all three states is preferable to the current one which makes one of them inaccessible. Working is better than broken, and only once things work, UX starts to become a topic.
I built this little thing a while ago btw: https://flying-sheep.github.io/react-color-scheme-switch/
I tried to solve this but this need a bit more refactoring than I thought.
We probably need 2 states now:
- colorMode: the current, effective color mode, never
null
(we already have this one) - colorModeChoice: the choice of the user, including
null
(or "os")?
If we only have the effective colorMode in state, then it becomes impossible to know we are in a state of "os color mode", and thus transition to the appropriate next value.
Also, I think the transition state machine should take into account the effective color mode to decide on which value to use next.
IE:
- if colorModeChoice=OS & colorMode=light => click should transition to dark
- if colorModeChoice=OS & colorMode=light => click should transition to light
What I want to avoid here: users on the first visit are likely all having the OS setting. When they first click on the toggle, we probably want to give them feedback. Transitioning from light (OS setting) to light (user choice) may be a weird initial feedback 🤷♂️
The other option is to use a dropdown instead of a toggle button.
That’s exactly the UX the Python community is converging on. I didn’t have time to expand on it when I wrote my last comment, but here’s basically me saying the exact same things as you just did, https://github.com/rust-lang/this-week-in-rust/issues/2274#issuecomment-1333723860, with some example implementations.
how about keeping the same icon button, but with a third state? clicking it cycles between "light"/"dark"/"auto". when set to "auto", just store { "theme": "auto" }
in localStorage
.
we just need a new icon, could be just an "A" or some i18n-friendly icon like a magic wand. the icon could also just be some sort of badge on top of the 🌙/☀️ we already have, to show both the current theme and the fact that it was automatically figured out.
that would be more than enough and is better than what we have currently imho
Yes we agree we need 3 states and an icon to represent the new 3rd state.
That's not really worth pursing this discussion, but rather working on a concrete implementation based on my comments here: https://github.com/facebook/docusaurus/issues/8074#issuecomment-1342662517
I'd like to work on that!
Any progress on adding an OS theme mode?
Hey all,
@slorber, I'd like to suggest the solution adopted by the website https://ui.shadcn.com/ (it's a UI library project being built at https://github.com/shadcn/ui):
Please play with the theme switcher, and you'll notice how the icons work:
- the button's icon always represents the theme currently displayed: either light or dark, it has no third state;
- however, in the menu which opens when the button is clicked, there are the three states, with a "laptop" icon for the system theme.
I find this method very intuitive, practical, easy to understand. It has three states, but it only shows the two icons that everybody understands: Sun and Moon. It doesn't require to come up with an icon for the 3rd-state, which seems to be an impossible task :smile: Even the one chosen by Mozilla is questionable. And the "laptop" icon here is great in the menu, but wouldn't be great in the button, especially because it would be the first one seen by the user when navigating to the site for the first time.
I use this method now on my new projects. I have just changed the name of the third option from "System" to "Same as device" to be more friendly to non-tech people.
(Also, this website uses next-theme under the hood — we could take inspiration from how it handles states.)
Just wanted to revive this. I've made a demo project showing what I suggested above, and explained the implementation in this article.
@slorber I'd be keen to know your opinion on this way of doing it :wink:
Disclaimer: I'm the author of the library.
Would you like to incorporate this animated theme switch? It supports system (automatic) theme as well.
Demo 1: https://mahozad.ir/theme-switch/ Demo 2: https://mahozad.ir/android-pie-chart/
It probably addresses the following related issues too:
- https://github.com/facebook/docusaurus/issues/8938
- https://github.com/facebook/docusaurus/issues/8074
- https://github.com/facebook/docusaurus/issues/7199