I'm trying to add a simple light/dark mode toggle to my Jekyll site.
The way I have it setup now is that the body has a dark
class that is switched to a light
class on toggle, and the theme is persisted in localStorage
and loaded whenever the user goes to a new page. The SCSS simply has selectors for .dark
and .light
that will show the corresponding color scheme.
The problem with this approach is that my default theme is dark
, so if the theme is set to light
there's a very clear .2 second flash as the classes switch from dark
to light
when I load a new page.
I've tried solving the issue by setting the body's visibilty
to hidden
then making it visible
again once the switch has been made, but unfortunately this approach introduces another annoying flash/lag, and there's no good way to stop it from flashing on every page load.
Some of the potential solutions I've thought of, but don't know if they are possible or how to implement them are:
Have Jekyll somehow read from localStorage
and change the class of the rendered HTML based on that (probably the best solution, but also probably not possible)
Somehow have Jekyll produce two separate style sheets from the SCSS and use JS to select the correct one
Would appreciate any insight on how to do these things or potential alternate solutions, thanks!
The idea behind dark mode is that it reduces the light emitted by device screens while maintaining the minimum colour contrast ratios required for readability. Both iPhones and Android handsets offer system-wide dark modes. However, you will still need to set up dark mode on some individual apps.
So breaking this down:
I think using a static site generator means that reading from localStorage and applying a theming class with Javascript is a perfectly good approach. As I see it, there are two possible approaches to minimising the flashing issue that don't involve changing the architecture away from a Jekyll site.
Execute the Javascript sooner, as the page content is loading:
Because the only DOM element that needs to have loaded is the <html>
element itself, to which you can apply a theme class, you could put your 'critical' Javascript in a small script tag inside the <head>
. i.e. just the 'theme-switching' line of code.
It should then execute straight away before the content loads.
Use CSS transitions to minimise the flash itself:
You could hide the page content while the <html>
element has no theming class, and fade it in once one is applied like so:
html {
opacity: 0;
transition: opacity 1s;
}
html.light, html.dark {
opacity: 1;
}
What you want, according to point 3,
Implement some kind of fade in/fade out when changing the visibility from hidden to visible
is --
CSS Transitions
Example usage to show ease-in effect whenever a CSS property change:
body {
transition: background 1s ease-in, color 1s ease-in;
}
.dark {
color: white;
background: black;
}
<body>
<p> Lorem Ipsum dolor sit amet... </p>
<button onclick="document.body.classList.toggle('dark')">Change Theme</button>
</body>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With