Creating a Dark, Light, System (Auto) Toggle Switch in Astro
Tags: astro, webdev, tutorial
Published: 2022-12-27 16:08:51

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](#final-code)

Check out: [Astroship - Free Astro Starter Template](http://astroship.web3templates.com/)

## 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. 

```html
<select name="themeSwitch" id="themeSwitch">
  <option value="system">System</option>
  <option value="dark">Dark</option>
  <option value="light">Light</option>
</select>
```

## Step 2: Add the CSS

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

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

## 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. 

```html
<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>
```

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. 

```html
---
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>
```