Переключатель тем

Вы можете использовать компонент Vs Vue3 Select для переключения тем вашего приложения. Разберем пример создания такого переключателя.

Основной смысл сводится к добавлению заданных CSS-классов к тегу body страницы.

Определим список доступных тем, а так же создадим отдельные константы для хранения темной и светлой тем по умолчанию. Объект, описывающий отдельную тему, будет содержать идентификатор темы, метку темы и массив CSS классов.

const defaultLight = {
  label: 'Light default',
  id: 'light-default',
  classes: [],
}
const defaultDark = {
  label: 'Dark default',
  id: 'dark-default',
  classes: ['dark'],
}

const themes = [
  defaultLight,
  defaultDark,
  {
    label: 'Light rounded',
    id: 'light-rounded',
    classes: ['rounded'],
  },
  {
    label: 'Dark rounded',
    id: 'dark-rounded',
    classes: ['dark', 'rounded'],
  },
  {
    label: 'Light line',
    id: 'light-line',
    classes: ['lined'],
  },
  {
    label: 'Dark line',
    id: 'dark-line',
    classes: ['dark', 'lined'],
  },
]

Текущую тему будем хранить в константе current. А так же наблюдателя, который будет реагировать на изменение текущей темы, убирать устаревшие CSS-классы других тем, добавлять новые и сохранять идентификатор темы в localStorage.

Для упрощения примера все CSS-классы, используемые в темах, так же собраны в общий массив availableClasses

const body = document.body
const availableClasses = ['dark', 'light', 'lined', 'rounded']
const STORAGE_CODE = 'DEMO_THEME'
const current = ref(defaultLight)
watch(current, (newValue) => {
    let i = 0
    for (; i < availableClasses.length; ++i) {
        body.classList.remove(availableClasses[i])
    }
    for (i = 0; i < newValue.classes.length; ++i) {
        body.classList.add(newValue.classes[i])
    }
    localStorage.setItem(STORAGE_CODE, newValue.id)
})

Теперь добавим логику восстановления темы при открытии страницы. Если в localStorage не найдено идентификатора темы, то проверяем системную тему, и если она темная, то задаем темную тему по умолчанию.

const load = () => {
    const stored = localStorage.getItem(STORAGE_CODE)
    const exists = themes.find((item) => item.id === stored)
    if (typeof exists !== 'undefined') {
        current.value = exists
    } else if (
        window.matchMedia &&
        window.matchMedia('(prefers-color-scheme: dark)').matches
    ) {
        current.value = defaultDark
    }
}
onMounted(load)

Теперь передаем все необходимые параметры в компонент и производим настройки

<template>
    <v-select
    :searchable="false"
    :clearable="false"
    :options="themes"
    v-model="current"
    class="themes"
    />
</template>

Остается только настроить соответствующие CSS правила.