Skip to content

Instantly share code, notes, and snippets.

@dombarnes
Created September 12, 2023 00:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dombarnes/91e37b3774dc3eb919da801949c02386 to your computer and use it in GitHub Desktop.
Save dombarnes/91e37b3774dc3eb919da801949c02386 to your computer and use it in GitHub Desktop.
Dark Mode Toggle Vue 3 Component with System-based Option
<template>
<div class="flex flex-col">
<div class="flex items-center">
<label class="switch">
<input ref="darkModeToggle" type="checkbox" name="dark-mode" v-model="darkMode" @change="toggleMode" :disabled="useSystemSetting">
<span class="slider round"></span>
</label>
<label>Dark Mode</label>
</div>
<label class="m-1">
<input type="checkbox" v-model="useSystemSetting" :value="useSystemSetting" @change="systemSettingToggled"/>
Use System setting
</label>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
const useSystemSetting = ref<Boolean>(false)
const darkMode = ref<Boolean>(false)
const selectedTheme = ref<string>('light')
function systemSettingToggled() {
if (useSystemSetting.value) {
if (localStorage.getItem('theme')) {
localStorage.removeItem('theme')
}
const dark = window.matchMedia('(prefers-color-scheme: dark)').matches
setTheme(dark ? 'dark' : 'light', false)
// add an event listener to toggle the color scheme as changes are made, as long as the user hasn't already set their mode with the button
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setSystemDarkMode)
} else {
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', setSystemDarkMode)
}
}
function setSystemDarkMode(event) {
if(!localStorage.getItem('theme')) {
const new_color_scheme = event.matches ? "dark" : "light"
// console.log(`scheme change to ${new_color_scheme}`)
setTheme(new_color_scheme)
}
}
function toggleMode() {
setTheme(darkMode.value ? 'dark' : 'light', true)
}
function setTheme(theme: string, save = false) : void {
// console.log(`setting dark mode ${theme}`)
selectedTheme.value = theme
if (save) localStorage.setItem('theme', selectedTheme.value)
const body = document.body
body.classList.add('dark')
document.documentElement.setAttribute('data-theme', selectedTheme.value)
darkMode.value = theme == 'dark' ? true : false
}
onMounted(() => {
// console.log(selectedTheme.value, useSystemSetting.value, darkMode.value)
if (localStorage.getItem('theme')) {
setTheme(localStorage.getItem('theme'), true)
return
}
if(window.matchMedia('(prefers-color-scheme)').media !== 'not all') {
// the browser is indicating that it supports prefers-color-scheme
if(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// the browser prefers dark mode
setTheme('dark')
} else {
// the browser doesn’t prefer dark mode
setTheme('light')
}
} else {
// browser does not support prefers-color-scheme
setTheme('light', false)
}
})
</script>
<style scoped>
</style>
@dombarnes
Copy link
Author

This component offers two modes:

  • Use System Setting - align darkmode with the System set mode.
  • Standalone - ignore the system mode and set based on toggle. Set option is saved in LocalStorage, and is removed when a user switches to System Setting mode.

Use in combination with CSS variables on root and data-theme attribute to override.

:root {
--background: white;
}
[data-theme="dark"] {
--background: black;
}

You may also want to add this to your <head> to ensure the theme is set before the page renders.

<script>
    const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null
    if (currentTheme) {
      document.documentElement.setAttribute('data-theme', currentTheme)
      if (currentTheme === 'dark') {
        document.documentElement.setAttribute('data-theme', 'dark');
      }
    }
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment