Load stylesheet with javascript and localStorage

If you change styles on the <body> you get FOUC (Flash Of Unstyled Content). Try using a close equivalent like <main> and spread it 100% x 100% and <html> and <body> as well, but give them margin and padding of 0 in order to ensure <main> covers them completely.

The [disabled] attribute for the <link> is the best way of toggling them because they are still loaded but inert. Also, in the example there is a function called loadTheme(e) that is loaded on the 'DOMContentLoaded' event which insures that all of the DOM is loaded before hand. The example below will not work because localStorage is blocked on SO. There is a functioning example on Plunker. To test it:

  1. Click the green Preview button.
  2. Another frame should appear on the right. Within the frame is the webpage example click the ☀️ button.
  3. It should be in dark mode now. Next, click the refresh button located in the mini-toolbar within the frame or press ctrl+enter for Windows OS or +return for Mac OS.
  4. The page should still be in dark mode. 👍
/* night.css 
main {
  background: #000;
  color: #fff;
}
*/

/* default.css */
:root {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  font: 1ch/1.5 'Segoe UI';
}

body {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  font-size: 4ch;
}

main {
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  color: #000;
}

form {
  width: 80vw;
  margin: 20px auto;
}

fieldset {
  width: max-content;
  min-height: 25px;
  margin-left: auto;
  padding: 0 1.5px 1.5px;
  border-radius: 8px;
  background: inherit;
  color: inherit;
}

button {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  font-size: 4rem;
  text-align: center;
  background: transparent;
  cursor: pointer;
}

#theme::before {
  content: '☀️';
}

.night #theme::before {
  content: '🌙';
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://stackoverflow.com/questions/71031101/lib/default.css" rel="stylesheet">
  <link class="night" href="lib/night.css" rel="stylesheet" disabled>
  <style></style>
</head>

<body>
  <main>
    <form id='UI'>
      <fieldset name="box">
        <legend>Theme</legend>
        <button id='theme' type="button"></button>
      </fieldset>
      <p>Click the "Theme" switch to toggle between `disabled` `true` and `false` on `night.css` and `light.css` `
        <link>`s.</p>
    </form>
  </main>
  <script>
    const UI = document.forms.UI;
    const M = document.querySelector('main');
    const L = document.querySelector('.night')

    const switchTheme = e => {
      const clk = e.target;
      if (clk.matches('button')) {
        M.classList.toggle('night');
        L.toggleAttribute('disabled');
      }
      let status = M.className === 'night' ? 'on' : 'off';
      localStorage.setItem('theme', status);
    };

    const loadTheme = e => {
      let cfg = localStorage.getItem('theme');
      if (cfg === 'on') {
        M.classList.add('night');
        L.removeAttribute('disabled');
      } else {
        M.classList.remove('night');
        L.setAttribute('disabled', true);
      }
    };

    UI.addEventListener('click', switchTheme);
    document.addEventListener('DOMContentLoaded', loadTheme);
  </script>
</body>

</html>

Leave a Comment