theodinproject
theodinproject copied to clipboard
Add option to make Light/Dark mode using system setting.
This pull request resolves issue #4225 . The pertinent code has been modified to provide users with the flexibility to synchronize the application's light/dark mode with their system settings.
Changes Made
- Updated the codebase to incorporate an option for users to set the light/dark mode based on their system preferences.
Screenshots
Thanks @sparshalc, this is cool! I'll get it reviewed as soon as I can.
Hey @sparshalc, thanks for making those updates. I'll get it reviewed again as soon as I can. But please make sure tests and linters are passing on CI before asking for a re-review.
Sorry for the delay @sparshalc, I've been meaning to get to this all week.
I'm just realising the event to keep TOP's theme in sync with the users system theme isn't working. Nothing will happen when that event triggers, it will always try to update the theme as the current theme.
We'd likely need something like this:
connect() {
if (this.currentThemeValue === 'system') {
this.updateTheme(this.selectedSystemTheme());
this.addThemeChangeListener();
}
}
addThemeChangeListener() {
const updateThemeHandler = () => this.updateTheme(this.selectedSystemTheme())
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateThemeHandler);
}
selectedSystemTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
But that would mean we'd have to persist "system" in the theme cookie. Then use that to trigger the JS to set the theme to whatever the users system preferences are.
But, that has issues. Our views are server side rendered, which is fine when we know the dark or light theme before sending the HTML response back and the CSS is loaded in the browser. But if we need JS to set the user system theme after the HTML is sent, we'll get flashing:
https://github.com/TheOdinProject/theodinproject/assets/7963776/0c3e4fd0-4628-4648-9be0-9c6fa1263b27
Turbo is helping us a little with internal links, it won't flash on those. But anytime the user first enters the site or hard reloads with system theme selected it will flash.
I can think of a couple of ways around this, but they are all complex / messy; and definitely not worth doing for a "nice to have" feature. Do you have any ideas?
I'm starting to see why most of the examples out there for system themes are using SPA frameworks lol.
Hey @KevinMulhern , what do you think about hiding the root element once an updateTheme request is sent, and then showing it again once the theme is selected?
We can start by applying a class called 'theme-hidden' to the root element, which sets its CSS display property to 'none'. Then, we'll remove this class using JavaScript after each theme request is sent.
It's a little too dangerous and wide reaching for me. If the updateTheme method doesn't fire for any reason, whether it be a syntax error in the theme controller or an issue with the JS assets loading, the site would be unusable. Light/dark mode should never have that kind of reach.
The other option I've been reading about is using a render blocking JS script, but thats not great either.
Sorry @sparshalc, I know how much work you've put into this PR but I think this might be a show stopper. If it was a widely requested feature, we'd be much more tolerant of throwing a couple of hacks in to get this to work. But theres been no activity in the issue, its not worth living with code that will limit us in the future.