Dark Mode Toggle with Ghost Pro

I’m wondering if I should use a SPA for dark mode toggle. I’m getting a “split second” white background on page load before the js does its thing. Does Ghost Pro supports SSR? That way the class name would get returned from the cached HTML etc.

I’ve posted a video on Twitter below.

Do I need to go the headless cms route to get a perfect dark mode transition with @Ghost The white background flickers when navigating, though the JS is in the head. pic.twitter.com/hwtdcoFtrw

— Daveyon Mayne 😻 (@sylarruby) June 22, 2021

Sounds like your dark mode JS is running asynchronously rather than blocking rendering. I’d suggest putting just the dark mode code in a render-blocking script separate to the rest of your JS.

Hi Kevin.

I have to wait for the DOM to finish load before running the JavaScript:

document.addEventListener("DOMContentLoaded", function() {
   
   // theme could be either 'dark' or 'light'
   // On page load, set the correct theme class
   const theme = localStorage.getItem('theme') || 'light';
  
   // Do something with theme when page first loads.

   // Switch has an eventListener that changes the theme in localStorage,
   // then it toggles the body class. Nothing special.

});

Without showing too much code, by waiting for the dom to load, the body background color will run before the JavaScript changes it. I’ve move the js code at the top of <head> inside default.hbs but same experience.

EDIT:

What looks much better is to hide the body initially then show it on page load.

[..]

body {
  display: "none";
}

[..]

Then in the JavaScript:

document.addEventListener("DOMContentLoaded", function() {
   [..]

   document.body.style.display = 'block';

   [..]
});

I could live with this :wink:

That’s the problem. You shouldn’t need to wait for DOM to finish loading for changing the theme.

Eg…

<style>
/* standard styles */
...

/* dark mode overrides */
html.dark .my-element { ... };
...
</style>

<script>
// render-blocking, ensures theme preference class is present for first paint
const theme = localStorage.getItem('theme') || 'light';
document.querySelector('html').classList.add(theme);

// non-render-blocking, needs DOM to be loaded
document.addEventListener('DOMContentLoaded', function () {
    // toggle button setup etc
});
</script>
1 Like

Rightttt!! Theme class needs to be on html instead of body. Gotcha! Thanks. Works.