Fix the White Flash on Page Load When Using a Dark Mode on a Static Site - Build a Theme
August 27, 2021 - 631 words

The problem

This Hugo theme I wrote have had a problem. It defaults to the light mode, but if you switch to the dark mode1, then when loading a new page it will first render the light mode and then automatically switch to the dark, looks like it flashes white a little bit. I thought all the static sites have to face this problem, until one day I found some healthy themes, for instance, PaperMod. So, there must be a way to solve this.

I searched this on the internet. There is an article Fix the White Flash on Page Load When Using a Dark Theme on a Static Site by the maintainer of theme Cupper. I tried their solution. In Chrome, it indeed works; but in Firefox it doesn’t. Firefox will render visibility: hidden; opacity: 0; to a whole blank page, and only after DOMContentLoaded happens showContent() function will show the content. Means: the page still flashes. Before it flashes the light mode, now it flashes the blank page. Just as bad as before.

I think the difference between Firefox and Chrome is because the developers of Chrome used magic, they tried their best to bring Chrome users a better experience. And this covers up the real problem for the Hugo theme developers who use Chrome.

Find the cause

The main idea is still to understand the process of how the browser render a page. Simply put:

  • You request for a page;
  • The browser download the entire HTML and save as DOM, then parse and render the page from top to bottom;
  • Stop parsing when encountering an external CSS or JavaScript file. Download it and then continue;
  • When encountering <style></style> or just loaded external styles, store them as CSSOM and re-render the influential part;
  • When encountering <script></script> or just loaded external scripts, stop parsing and run the scripts. Continue parsing and rendering after finish running them;
  • So on and so on until all the works are done.

The cause to the white flash problem is that I put the JavaScript for theme’s mode switching at the end of <body>, like this:

1
2
3
4
5
6
7
8
<!DOCTYPE html>
<html>
    <body>
        <!-- lots of stuff…… -->

        <script src="changeTheme.js"></script>
    </body>
</html>

The solution

The solution to this problem is quite simple, with two points:

    1. Put the script for theme’s mode switching into <head>, in order to determine the theme currently selected by the user before rendering the body;
    1. Upgrade the container for the theme to the entire <html>, i.e., from
1
2
3
4
<html>
    <body class="theme-container">
    </body>
</html>

to

1
2
3
4
<html class="theme-container">
    <body>
    </body>
</html>

This is because <body> has not been rendered at this time, and the only object that can be manipulated by the script in <head> is <html>.


Now the problem is completely solved, the complete code is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html class="theme-container">
    <head>
        <!-- lots of stuff…… -->

        <script>
            const themeContainer = document.querySelector(".theme-container");
            var theme = localStorage.getItem("theme");
            if (theme == "dark") {
                themeContainer.classList.add("dark");
            } else if (theme == "light") {
                themeContainer.classList.remove("dark");
            }
        </script>
    </head>

    <body>
        <!-- lots of stuff…… -->

        <script src="others.js"></script>
    </body>
</html>

  1. The approach to dark mode in this theme is CSS variable, see this tutorial for reference. ↩︎

See also

  1. No.1: Fix the White Flash on Page Load When Using a Dark Mode on a Static Site - Build a Theme (This one!)
  2. No.2: Make Hugo Invert Color for Images when Using a Dark Mode - Build a Theme