DEV Community

Surjith S M
Surjith S M

Posted on

Creating a Dark, Light, System (Auto) Toggle Switch in Astro

You can see a lot of examples with Dark & Light Toggle. But adding "system" or "auto" option along with that is bit tricky.

No time? Jump to the final code

Check out: Astroship - Free Astro Starter Template

System (Auto)

The system or auto option will switch the dark light theme based on user's system preferences. If they have "Auto" set in the system based on day/night, our site would show dark & light accordingly.

Step 1: Create the HTML

First, create themeswitch.astro file in /components folder. Then add the following HTML Code.

<select name="themeSwitch" id="themeSwitch">
  <option value="system">System</option>
  <option value="dark">Dark</option>
  <option value="light">Light</option>
</select>
Enter fullscreen mode Exit fullscreen mode

Step 2: Add the CSS

Now add a style to setup the color-scheme for default HTML elements.

<style>
  :global(.dark) {
    color-scheme: dark;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

Step 2: Add Script

Now, add the below javascript code to the file and it will handle all of the usecase for our theme switch.

<script>
  const select = document.getElementById("themeSwitch") as HTMLSelectElement;
  const theme = localStorage.getItem("theme");
  const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
    ? "dark"
    : "light";

  if (theme !== null) {
    select.value = theme;
  } else {
    select.value = systemTheme;
  }

  function updateTheme(value) {
    const theme = value === "system" ? systemTheme : value;
    document.documentElement.classList.remove("light", "dark");
    document.documentElement.classList.add(theme);
    localStorage.setItem("theme", value);
  }

  updateTheme(select.value);

  select.addEventListener("change", (event: Event) => {
    const select = event.target as HTMLSelectElement;
    updateTheme(select.value);
  });
</script>
Enter fullscreen mode Exit fullscreen mode

That's it. You have a working Theme Switch in Astro.build.

Final Code

I have combined everything and added some styling with tailwindcss. Here's how the page would look finally.

---
import { Icon } from "astro-icon";
---

<div class="inline-flex items-center">
  <Icon class="w-4 h-4 mr-2" name="heroicons-outline:sun" />
  <select name="themeSwitch" id="themeSwitch">
    <option value="system">System</option>
    <option value="dark">Dark</option>
    <option value="light">Light</option>
  </select>
</div>

<style>
  :global(.dark) {
    color-scheme: dark;
  }
</style>

<script>
  const select = document.getElementById("themeSwitch") as HTMLSelectElement;
  const theme = localStorage.getItem("theme");
  const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
    ? "dark"
    : "light";

  if (theme !== null) {
    select.value = theme;
  } else {
    select.value = systemTheme;
  }

  function updateTheme(value) {
    const theme = value === "system" ? systemTheme : value;
    document.documentElement.classList.remove("light", "dark");
    document.documentElement.classList.add(theme);
    localStorage.setItem("theme", value);
  }

  updateTheme(select.value);

  select.addEventListener("change", (event: Event) => {
    const select = event.target as HTMLSelectElement;
    updateTheme(select.value);
  });
</script>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)