In my website I like to keep the dark/light theme persist over a browser reload by storing the state in localStorage.

Before I used the Next.js <Script> tag and it somehow worked without any flicker, so I never gave much thought to it. But, yesterday I moved my site to Astro, and in Astro if you want to have client side script you just use good old html <script> tag.

So I did this:

<head>
  ...
  <script>
    const isDark = localStorage.getItem("isDark");
    if (isDark === "true") {
      document.documentElement.classList.add("dark");
    } else if (isDark === "false") {
      document.documentElement.classList.remove("dark");
    }
  </script>
</head>

Easy peasy! It worked perfect in firefox and webkit based browsers. But in chrome (or any chromium based browser) you’d see a flash of white content before it turned dark.

Which is darn annoying!

Placing the script in <head>, I expected it to run before the rest of the body content was loaded but apparently it doesn’t.

With some trial and error I discovered setting our old pal type attribute to text/javascript fixes it in chrome and all other chromium based browsers.

<head>
  ...
  <script type="text/javascript">
    // same as before
  </script>
</head>

Wait! Wasn’t that attribute optional!

Well, according to MDN:

Attribute is not set (default), an empty string, or a JavaScript MIME type - Indicates that the script is a “classic script”, containing JavaScript code. Authors are encouraged to omit the attribute if the script refers to JavaScript code rather than specify a MIME type.

The specification also says something similar:

Omitting the attribute, setting it to the empty string, or setting it to a JavaScript MIME type essence match, means that the script is a classic script, to be interpreted according to the JavaScript Script top-level production. Classic scripts are affected by the async and defer attributes, but only when the src attribute is set. Authors should omit the type attribute instead of redundantly setting it.

If I understand them correctly, it essentially means:

<script> == <script type=""> == <script type='text/javascript'>

All of the above are equivalent, so setting a type attribute on the <script> tag is redundant and they recommend against it. Even Douglas Crockford says “…it is better to leave it out. The browser knows what to do.”

Well at least chromium doesn’t know what to do ¯\_(ツ)_/¯.

It’s an annoying discovery.

Whatever the reason, if you’re more informed on the matter please feel to give your two cents by reaching out.

(The darkmode toggle works smooth now btw).

Published on:


If you have something in mind, please don't hesitate to reach out to me via email.