You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I tried implementing this, and achieved some success, but I'm rolling the feature back. I want to preserve some notes from what I've done though in case this is useful in the future.
So, the browser-native way of implementing light and dark mode is usually done by having a normal stylesheet for 'light' mode, and then creating a media query like so
The browser knows to apply this style based on the browser/OS settings. So far so good.
Introducing a feature to let users toggle light and dark, within a site, is very hard though. Here's a StackOverflow thread with answers. The main thing to notice is that most of the answers don't actually work with @media. They expect you to create top level .light and .dark classes, and the Javascript just switches between those.
With enough clues there and in blog posts I was able to cobble together this JS which does allow switching the theme and still let you use the @media queries. Toggling a theme stores the selected theme in local storage and uses that the next time the page loads.
// https://stackoverflow.com/questions/56300132/how-to-override-css-prefers-color-scheme-setting// Return the system level color scheme, but if something's in local storage, return that// Unless the system scheme matches the the stored scheme, in which case... remove from local storagefunctiongetPreferredColorScheme(){letsystemScheme='light';if(window.matchMedia('(prefers-color-scheme: dark)').matches){systemScheme='dark';}letchosenScheme=systemScheme;if(localStorage.getItem("scheme")){chosenScheme=localStorage.getItem("scheme");}if(systemScheme===chosenScheme){localStorage.removeItem("scheme");}returnchosenScheme;}// Write chosen color scheme to local storage// Unless the system scheme matches the the stored scheme, in which case... remove from local storagefunctionsavePreferredColorScheme(scheme){letsystemScheme='light';if(window.matchMedia('(prefers-color-scheme: dark)').matches){systemScheme='dark';}if(systemScheme===scheme){localStorage.removeItem("scheme");}else{localStorage.setItem("scheme",scheme);}}// Get the current scheme, and apply the oppositefunctiontoggleColorScheme(){letnewScheme="light";letscheme=getPreferredColorScheme();if(scheme==="light"){newScheme="dark";}applyPreferredColorScheme(newScheme);savePreferredColorScheme(newScheme);}// Apply the chosen color scheme by traversing stylesheet rules, and applying a medium.functionapplyPreferredColorScheme(scheme){for(vari=0;i<=document.styleSheets[0].rules.length-1;i++){rule=document.styleSheets[0].rules[i].media;if(rule&&rule.mediaText.includes("prefers-color-scheme")){switch(scheme){case"light":
rule.appendMedium("original-prefers-color-scheme");if(rule.mediaText.includes("light"))rule.deleteMedium("(prefers-color-scheme: light)");if(rule.mediaText.includes("dark"))rule.deleteMedium("(prefers-color-scheme: dark)");break;case"dark":
rule.appendMedium("(prefers-color-scheme: light)");rule.appendMedium("(prefers-color-scheme: dark)");if(rule.mediaText.includes("original"))rule.deleteMedium("original-prefers-color-scheme");break;default:
rule.appendMedium("(prefers-color-scheme: dark)");if(rule.mediaText.includes("light"))rule.deleteMedium("(prefers-color-scheme: light)");if(rule.mediaText.includes("original"))rule.deleteMedium("original-prefers-color-scheme");break;}}}// Change the toggle button to be the opposite of the current schemeif(scheme==="dark"){document.getElementById("icon-sun").style.display='inline';document.getElementById("icon-moon").style.display='none';}else{document.getElementById("icon-moon").style.display='inline';document.getElementById("icon-sun").style.display='none';}}applyPreferredColorScheme(getPreferredColorScheme());
The JS works but I don't actually understand it, it's using mediaText feature, for which I cannot find any good documentation. It seems to have been hastily implemented by browsers.
And although this works, it's still plagued by another issue, the 'white/black flash' - for example if a user has selected a dark theme, and it's in local storage, there's a brief flash of white background before the JS eventually kicks in and applies the CSS across all the rules.
There is a hacky fix for it but it's one hack too far. It's basically a huge amount of effort, questionable code, and hacky practices, for one small feature. I'd like to stick to CSS best practices because CSS is also hard.
Hopefully in the future this situation improves and there's a more direct, simple way of letting JS set the media for the site, or maybe browsers introduce that feature. In the meantime there's also this extension.
The text was updated successfully, but these errors were encountered:
I tried implementing this, and achieved some success, but I'm rolling the feature back. I want to preserve some notes from what I've done though in case this is useful in the future.
So, the browser-native way of implementing light and dark mode is usually done by having a normal stylesheet for 'light' mode, and then creating a media query like so
The browser knows to apply this style based on the browser/OS settings. So far so good.
Introducing a feature to let users toggle light and dark, within a site, is very hard though. Here's a StackOverflow thread with answers. The main thing to notice is that most of the answers don't actually work with
@media
. They expect you to create top level.light
and.dark
classes, and the Javascript just switches between those.With enough clues there and in blog posts I was able to cobble together this JS which does allow switching the theme and still let you use the
@media
queries. Toggling a theme stores the selected theme in local storage and uses that the next time the page loads.On the page I put some SVG icons like so:
Then this Javascript:
The JS works but I don't actually understand it, it's using mediaText feature, for which I cannot find any good documentation. It seems to have been hastily implemented by browsers.
And although this works, it's still plagued by another issue, the 'white/black flash' - for example if a user has selected a dark theme, and it's in local storage, there's a brief flash of white background before the JS eventually kicks in and applies the CSS across all the rules.
There is a hacky fix for it but it's one hack too far. It's basically a huge amount of effort, questionable code, and hacky practices, for one small feature. I'd like to stick to CSS best practices because CSS is also hard.
Hopefully in the future this situation improves and there's a more direct, simple way of letting JS set the media for the site, or maybe browsers introduce that feature. In the meantime there's also this extension.
The text was updated successfully, but these errors were encountered: