Avoiding Flash of Unthemed Code

If your site has a dark mode or custom theme, you might have a flash of the default theme before JavaScript loads. This site has both a light mode and a custom-settable theme, serialized into localStorage.

Previously it would load the default (dark mode) and then as the JS ran it would transition smoothly into your custom set theme. Obviously not great if you keep landing on my site and it keeps flashing you with the colors you don’t want.

I was browsing /r/reactjs and saw this post on a dark mode toggle, which led me to Donavon’s useDarkMode hook, which then led me to noflash.js.txt. Ah! Here was the solution!

Basically, inline the localStorage reading into the html you generate. I tried putting this in <svelte:head> in my Sapper _layout.svelte file and it worked! (It’s still WIP, because I want to add this to an auth system, but try it out!)

Here’s the code snippet for implementing custom theming in your Svelte/Sapper app!

<script>
  import { onMount } from 'svelte'
  import { themeStore } from '../theme.js' // a writable store

  onMount(renderCSS)
  themeStore.subscribe(renderCSS) // subscribe to theme updates elsewhere in the UI
  function renderCSS() {
    if (typeof document === 'undefined') return // SSR
    const stylesheet = document.getElementById("unique-stylesheet-id");
    if (!stylesheet) return // not rendered yet
    let string = ``
    if ($themeStore.bgColor) string += `--bg-color: ${$themeStore.bgColor};`
    if ($themeStore.textColor) string += `--text-color: ${$themeStore.textColor};`
    if ($themeStore.linkColor) string += `--link-color: ${$themeStore.linkColor};`
    if ($themeStore.lineLength) string += `--line-length: ${$themeStore.lineLength};`
    stylesheet.innerHTML = `html { ${string} }`
  }
</script>

<svelte:head>
  <style id="unique-stylesheet-id"> </style>
  <script>
    // read the stored theme if it exists, 
    // and add it to stylesheet, before the user sees it
    (function() {
      let temp = localStorage.getItem('swyx_io_themeStore')
      if (temp) {
        temp = JSON.parse(temp) // store object
        if (typeof document === 'undefined') return // SSR
        const stylesheet = document.getElementById("unique-stylesheet-id");
        let string = ``
        if (temp.bgColor) string += `--bg-color: ${temp.bgColor};`
        if (temp.textColor) string += `--text-color: ${temp.textColor};`
        if (temp.linkColor) string += `--link-color: ${temp.linkColor};`
        if (temp.lineLength) string += `--line-length: ${temp.lineLength};`
        stylesheet.innerHTML = `html { ${string} }`
      }
    })()
  </script>
</svelte:head>

Tagged in: #tech #svelte #css #ux

Leave a reaction if you liked this post! 🧡
Loading comments...
Webmentions
❤️ 0 💬 14
  • avatar of Osama Qarem
    Osama Qarem mentioned this on 2020-02-18

    Nice. Here is my solution with Gatsby github.com/osamaq/osamaq.…

  • avatar of donavon "spread love" west
    donavon "spread love" west mentioned this on 2020-02-19

    You don't have to link to me either 😘

  • avatar of shawn swyx wang🤗
    shawn swyx wang🤗 mentioned this on 2020-02-19

    it's such a small trick, just take it and implement it by default no need to link to me haha. but I do think it's worth educating users like me who maybe have never thought about how this works

  • avatar of Olivier Tassinari
    Olivier Tassinari mentioned this on 2020-02-19

    It looks like a great blog post we could link or leverage. Maybe in material-ui.com/customization/…? :)

  • avatar of shawn swyx wang🤗
    shawn swyx wang🤗 mentioned this on 2020-02-18

    no not really

  • avatar of donavon "spread love" west
    donavon "spread love" west mentioned this on 2020-02-18

    Glad you found it useful. That flashing was killing me! Your right. There needed to be a hydration step, which is exactly what my noflash.js set out to solve.

  • avatar of Sebastian Silbermann
    Sebastian Silbermann mentioned this on 2020-02-18

    Very nice article. Could I suggest avoiding abbreviations in code examples? They require knowledge of what the code is supposed to do which isn't ideal if you try to understand it. And they might mean other things that you do not want to associate with (here: ss)

  • avatar of shawn swyx wang🤗
    shawn swyx wang🤗 mentioned this on 2020-02-18

    fixed! thanks for feedback. that code was literally cut n paste from my own code, i didnt take the time to go thru and adjust it for external readers. thanks! swyx.io/writing/avoid-… <

  • avatar of Monica.dev 👩🏾‍💻
    Monica.dev 👩🏾‍💻 mentioned this on 2020-02-18

    Local storage for the win! Do you use it other places on your site?

  • avatar of Pierre-Emmanuel 👋
    Pierre-Emmanuel 👋 retweeted
  • avatar of Preet Shihn
    Preet Shihn mentioned this on 2020-02-18

    Haven’t done dark mode either, but used tricks like this for other stuff. Modern webdev is such, don’t think about html loading and inline scripts much. Also, don’t utilize localstorage much in general. Mostly for auth tokens, but could be anything user specific

  • avatar of shawn swyx wang🤗
    shawn swyx wang🤗 mentioned this on 2020-02-18

    yeah I feel like everyone else knew this but me 😂 cos I never had to implement a dark mode for any previous work before

Subscribe to the newsletter

Join >10,000 subscribers getting occasional updates on new posts and projects!

I also write an AI newsletter and a DevRel/DevTools newsletter.

Latest Posts

Search and see all content