<body>
  <main>
    <form onsubmit="return false;">
      <button id="change-theme">Switch theme</button>
    </form>
  </main>
</body>
:root, .theme-white {
  --base: 0, 0%;
  --lightness: 100%;
  --color: #1098ad;
}

.theme-dark {
  --base: 210, 10%;
  --lightness: 23%;  
  --color: white;
}

.theme-red {
  --base: 0, 100%;
  --lightness: 32%;
  --color: white;
}

.theme-orange {
  --base: 35, 100%;
  --lightness: 47%;
  --color: black;
}

.theme-blue {
  --base: 209, 77%;
  --lightness: 43%;
  --color: white;
}

.theme-purple {
  --base: 288, 54%;
  --base2: 54%;
  --lightness: 46%;
  --color: #FAEAB3;
}

body {
  display: flex;
  justify-content: center;
  height: 100vh;
  align-items: center;
  --bgColor: hsl(var(--base), var(--lightness));
  --darkerBg: hsl(var(--base), calc(var(--lightness) - 10%));
  background-color: var(--bgColor);
  color: var(--color);
}

button {
  height: 3em;
  font-size: 1.3rem;
  color: inherit;
  background-color: var(--darkerBg);
  border: none;
  cursor: pointer;
}
const CURRENT_THEME_KEY = "current_theme_idx"

// Look if there is a theme already set
const currentTheme = localStorage.getItem(CURRENT_THEME_KEY)
const themeToggle = document.getElementById("change-theme")

function getAvailableThemes() {
  // Ideally use something like:
  // const mainStylesheet = Array.from(document.styleSheets).find(
  //   s => s.title === "main"
  // )
const mainStylesheet =  Array.from(document.styleSheets)[0]

  const rules = Array.from(mainStylesheet.rules)

  // All themes are class selectors that start with "theme-"
  return rules
    .filter(
      r =>
        Boolean(r.selectorText) &&
        r.selectorText.includes("theme-")
    )
    // get name without dot prefix on a capture group
    .map(r => r.selectorText.match(/\.([^\s]*)/)[1])
}

const themes = getAvailableThemes()

const mediaQuery = matchMedia("(prefers-color-scheme: dark)")
let defaultTheme = mediaQuery.matches ? "theme-dark" : "theme-white"

mediaQuery.addEventListener("change", val => {
  defaultTheme = mediaQuery.matches ? "theme-dark" : "theme-white"
  setTheme()
})

function setTheme(idx = null) {
  if(idx !== null) {
    document.body.className = themes[idx]
    // store idx of selected theme
    localStorage.setItem("current_theme_idx", idx)
  } else if (currentTheme) {
    document.body.className = themes[currentTheme]
  } else {
    document.body.className = defaultTheme
  }  
}


themeToggle.addEventListener("click", function toggleTheme() {
  const currentTheme =  Array.from(document.body.classList).find(val => themes.includes(val)) ||
    defaultTheme
  const idx = themes.indexOf(currentTheme)
  const nextIdx = (idx + 1) % themes.length
  setTheme(nextIdx)
})

setTheme()
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.