Flash of unexpected content - Svelte and Static Site Generation

I have had support for 2 themes on my website for a long time now. I added the feature using Svelte and saved the user preference in a cookie(theme). It was quite straightforward to implement it in Svelte.

<!-- **__layout.svelte -->**

<script>
  // Default Theme - Rendered Server Side
    let theme = 'light';
    onMount(()=>{
        const result = document.cookie.match(/theme=(.*?)(;|$)/);
    // Change theme based on user's preference on the website.
        theme = result && result[1] || 'theme';
    });
</script>
<div class={theme}>
.... All Website Content Here ....
</div>

It worked just fine.

Then I noticed a problem with it. If the user has chosen a dark theme and then he refreshes the page, for a brief moment, light theme is visible. This flicker is a really a problem because the user has chosen dark theme and suddenly for a brief moment everything becomes white. It might cause strain in eyes in a dark setting.

The reason for this is simply because when onMount occurs, elements are already rendered and visible on screen(with light theme) and then in onMount theme is changed to dark

I added the following code in the app.html itself outside the SvelteKit framework. The code has performance mark entries to measure the improvement.

performance.mark('start')
  // Ensure that theme is applied before the body-wrapper is rendered on the page.
  // Allowing the theme to be switched using Svelte adds a delay in theme change because statically generated pages would have light theme hardcoded and theme switches from light to dark(if required) during onMount only.
  requestAnimationFrame(function ensureTheme() {
      if (!document.getElementById('body-wrapper')) {
          requestAnimationFrame(ensureTheme)
      }
      performance.mark('applyTheme');
      performance.measure('themeRestoreTime', 'start');
      themeToEnsure = document.cookie.match(/theme\s*=([^;]*)/);
      themeToEnsure = themeToEnsure && themeToEnsure[1] || 'light';
      document.getElementById('body-wrapper').classList.add(themeToEnsure);
  })

I instrumented the onMount code as well.

<!-- **__layout.svelte -->**
<script>
  // Default Theme - Rendered Server Side
    let theme = 'light';
    onMount(()=>{
        performance.mark('mountLayout')
    performance.measure('svelteThemeRestoreTime', 'start', 'mountLayout');
        const result = document.cookie.match(/theme=(.*?)(;|$)/);
    // Change theme based on user's preference on the website.
        theme = result && result[1] || 'theme';
    });
</script>
<div class={theme}>
.... All Website Content Here ....
</div>

At 4x CPU, this is how it performed.

Flash%20of%20unexpected%20content%20-%20Svelte%20and%20Static%20Si%2023a86925f81f42488e6cac9bb874a24c/Screenshot_2021-06-21_at_5.55.20_PM.png

Experience during a refresh, Earlier: Notice white background

Flash%20of%20unexpected%20content%20-%20Svelte%20and%20Static%20Si%2023a86925f81f42488e6cac9bb874a24c/before.gif

Experience during a refresh, Now : Notice there is no white background

Flash%20of%20unexpected%20content%20-%20Svelte%20and%20Static%20Si%2023a86925f81f42488e6cac9bb874a24c/after.gif

... Loading comments