Skip navigation

Implement theme using SCSS

Instructions for implement dark/light theme and how to switch between them. The theme is persisted when user reload page, also support user preference and sync the setting across multiple tabs.

Basic setup

Create theme CSS variables:

/* Light theme */
:root,
[data-theme='light'] {
  --body-color: #212529;
  --body-bg: #fff;
}

/* Dark theme */
[data-theme='dark'] {
  --body-color: #adb5bd;
  --body-bg: #212529;
}

/* Set background and text color based on the variables */
body {
  background-color: var(--body-bg);
  color: var(--body-color);
}

Create a button to switch between light and dark theme:

<p>
  <button id="theme-switcher">
    Switch theme
  </button>
</p>

Attach below toggleTheme function to button's click event:

const toggleTheme = () => {
  const theme = document.documentElement.getAttribute('data-theme');
  const newTheme = theme === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', newTheme);
}

window.addEventListener('load', () => {
  const themeSwitcher = document.getElementById('theme-switcher');
  themeSwitcher.addEventListener('click', toggleTheme);
});

Full snippet:

<p>
  <button id="theme-switcher">
    Switch theme
  </button>
</p>
<p>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse in malesuada risus. Nunc sit amet ipsum dapibus, dignissim ex non, vestibulum mauris.
</p>
/* Light theme */
:root,
[data-theme='light'] {
  --body-color: #212529;
  --body-bg: #fff;
}

/* Dark theme */
[data-theme='dark'] {
  --body-color: #adb5bd;
  --body-bg: #212529;
}

body {
  background-color: var(--body-bg);
  color: var(--body-color);
}
const toggleTheme = () => {
  const theme = document.documentElement.getAttribute('data-theme');
  const newTheme = theme === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', newTheme);
}

window.addEventListener('load', () => {
  const themeSwitcher = document.getElementById('theme-switcher');
  themeSwitcher.addEventListener('click', toggleTheme);
});

That's it. You already implement the theme feature for your page. It works nicely, but I guess you already notice something is missing. If you reload the page, the them you chose will be reset.

Persist to localStorage

To persist the theme preference, you can store it in localStorage. First, update the toggleTheme function as below:

const toggleTheme = () => {
  const theme = document.documentElement.getAttribute('data-theme');
  const newTheme = theme === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', newTheme);

  // Store the theme preference to `localStorage`
  localStorage.setItem('myTheme1', newTheme);
}

Add new function in the <head> tag and make sure it get executed as soon as possible when user visits the page. If you put it at the end of <body> tag, it will be called after the page is rendered, thus will have flickering issue.

const loadTheme = () => {
  let theme = localStorage.getItem('myTheme1');
  document.documentElement.setAttribute('data-theme', theme)
}

loadTheme();

Full snippet:

const loadTheme = () => {
  let theme = localStorage.getItem('myTheme1');
  document.documentElement.setAttribute('data-theme', theme)
}

loadTheme();

const toggleTheme = () => {
  const theme = document.documentElement.getAttribute('data-theme');
  const newTheme = theme === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', newTheme);

  localStorage.setItem('myTheme1', newTheme);
}

window.addEventListener('load', () => {
  const themeSwitcher = document.getElementById('theme-switcher');
  themeSwitcher.addEventListener('click', toggleTheme);
});

Auto select theme based on user's default perference

Many of you use dark mode in browser, up to this point the theme switcher mechanism does not take it into account. To support it, update the loadTheme function and add conditional checking for (prefers-color-scheme: dark) media as below:

const loadTheme = () => {
  let theme = localStorage.getItem('myTheme2');
  if (theme === 'dark'
    || (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
    document.documentElement.setAttribute('data-theme', 'dark');
  }
}

loadTheme();

Sync theme preference between tabs

When you save something to localStorage, browser will sends an event globally across all tabs with same origin. You can take advantage of this feature to sync the theme preference to all tabs when user changes theme by listening to the storage event as below.

window.addEventListener("storage", (event) => {
  switch (event.key) {
    case 'myTheme3': {
      const theme = event.newValue;
      if (theme === 'dark' || theme === 'light') {
        document.documentElement.setAttribute('data-theme', theme);
      }
    }
  }
});