DevGang
Авторизоваться

Как реализовать переключатель тем в JavaScript

В этой статье вы узнаете, как создать переключатель тем на JavaScript. Это должно быть довольно легко сделать, но вы также можете кое-чему научиться из моего кода. Давай повеселимся.

Какие случаи нам необходимо охватить?

Один из самых простых сценариев, которые нам следует рассмотреть, — это изменение темы со светлой на темную и наоборот. Второе, что нам нужно помнить, это то, что некоторые люди предпочитают использовать те же настройки, что и в системе. Полезно для тех, кто в течение дня переключается между темной и светлой темой. Третье — сохранение пользовательских настроек; в противном случае, если вы обновите страницу, все настройки снова будут установлены по умолчанию.

Создать магазин тем

Нашей начальной функцией будет createThemeStore(), которая будет содержать почти все. Я хочу отметить, что, возможно, это не оптимальный подход, но, эй, мы здесь пишем для развлечения.

function createThemeStore(options) {
  // Initial mode
  const initialMode = options.defaultMode || 'system'

  // Initial state
  const state = {
    mode: initialMode,
    systemTheme: getSystemTheme(),
    theme: getThemeByMode(initialMode),
  }
}

Здесь мы создаем состояние всего с тремя переменными:

  • mode: представляет выбранный режим темы с возможными значениями темного, светлого или системного. Это позволяет нам определить, использовать ли тему системы или нет.
  • systemTheme: содержит текущее значение темы в вашей ОС. Даже если мы выберем конкретную тему (темную или светлую), мы все равно обновляем эту переменную при изменении темы ОС, чтобы гарантировать, что мы можем правильно настроить тему, когда пользователь переключается в системный режим.
  • theme: это фактическая тема, которую видит пользователь, с возможными значениями темного или светлого.
  • options.defaultMode: используется для восстановления правильных настроек темы. Например, вы можете сохранить изменения темы в localStorage, а затем использовать ее по умолчанию, гарантируя сохранение настроек пользователя.

Добавить подписки

Когда пользователь меняет тему или обновляется тема ОС, нам нужен способ уведомить наш код. Вот тут-то и приходят на помощь подписки. Нам нужно разрешить подписку на изменения в нашем объекте состояния. Вот код, который нам в этом поможет. Помните, что сейчас мы делаем все внутри createThemeStore().

function createThemeStore(options) {
  // ...

  // Create subscriptions object to be able notify subscribers
  const subscriptions = new Map()
  let subscriptionId = 0 // Just a unique id for every subscriber

  // A place where we send notification to all of our subscribers
  function notifyAboutThemeChange(theme) {
    subscriptions.forEach((notify) => {
      const notificationData = {
        mode: state.mode,
        theme,
      }

      notify(notificationData) // Calls subscribed function (The example how we use it will be later)
    })
  }

  // A function that allows to subscribe to state changes
  function subscribe(callback) {
    subscriptionId++
    subscriptions.set(subscriptionId, callback)

    state.systemTheme = getSystemTheme() // We'll define it later

    if (state.mode === 'system') {
      notifyAboutThemeChange(state.systemTheme)
    } else {
      notifyAboutThemeChange(state.theme)
    }

    return subscriptionId
  }

  // A function that allows to unsubscribe from changes
  function usubscribe(subscriptionId) {
    subscriptions.delete(subscriptionId)
  }

  return {
    subscribe,
    usubscribe,
  }
}

Вот как это работает со стороны потребителя.

// Create a theme store
const store = createThemeStore()

// Suscribe to changes
const subscriptionId = store.subscribe((newTheme) => {
  // Here you'll be seeing theme changes
  console.log(newTheme)
})

// When you need to unsubscribe from theme change, you just call
store.usubscribe(subscriptionId)

Определить настройки системной темы

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

  • getSystemTheme(): это должно вернуть текущую тему ОС, темную или светлую.
  • getThemeByMode(): должен возвращать либо темный, либо светлый цвет в зависимости от режима нашей темы. Например, если установлен тёмный режим, мы возвращаем тёмный. Однако, когда для режима установлен системный, мы проверяем системную тему и отвечаем либо темной, либо светлой, в зависимости от предпочтений ОС.

Важно отметить, что этот код не будет внутри нашей функции createThemeStore(). Мы используем window.matchMedia с медиа-запросом «предпочитаемая цветовая схема», позволяющим нам определить, установлена ​​ли в системе ОС темный цвет или нет.

const mediaQuery = '(prefers-color-scheme: dark)'

// Get's current OS system
function getSystemTheme() {
  if (window.matchMedia(mediaQuery).matches) {
    return 'dark'
  }

  return 'light'
}

// Based on user's preferences we return correct theme
function getThemeByMode(mode) {
  if (mode === 'system') {
    return getSystemTheme()
  }

  return mode
}

function createThemeStore(options) {
  // ...
}

Теперь единственное, что нам нужно сделать, чтобы обнаружить изменения темы нашей ОС, — это добавить прослушиватель событий.

function createThemeStore(options) {
  // ...

  // When the OS preference has changed
  window.matchMedia(mediaQuery).addEventListener('change', (event) => {
    const prefersDarkMode = event.matches

    // We change system theme
    state.systemTheme = prefersDarkMode ? 'dark' : 'light'

    // And if user chose `system` mode we notify about the change
    // in order to be able switch theme when OS settings has changed
    if (state.mode === 'system') {
      notifyAboutThemeChange(state.systemTheme)
    }
  })
}

Добавьте возможность вручную менять режим темы.

Мы реализовали автоматическое обновление тем всякий раз, когда меняются настройки нашей ОС. Единственное, что мы еще не обсудили, — это ручное обновление режима темы. Вы будете использовать эту функцию для кнопок темной, светлой и системной темы.

function createThemeStore(options) {
  // ...

  function changeThemeMode(mode) {
    const newTheme = getThemeByMode(mode)

    state.mode = mode
    state.theme = newTheme

    if (state.mode === 'system') {
      // If the mode is system, send user a system theme
      notifyAboutThemeChange(state.systemTheme)
    } else {
      // Otherwise use the one that we've selected
      notifyAboutThemeChange(state.theme)
    }
  }

  return {
    subscribe,
    usubscribe,
    changeThemeMode,
  }
}

Пример использования

Наш код — это чистый JavaScript, и вы можете использовать его где угодно. Я продемонстрирую пример на React, но вы можете попробовать его в любом фреймворке или библиотеке, которые вам нравятся.

// Create a theme store from saved theme mode
// or use `system` if user hasn't changed preferences
const store = createThemeStore({
  defaultMode: localStorage.getItem("theme") || "system",
});

function MyComponent() {
  // Initial active theme is `null` here, but you could use the actual value
  const [activeTheme, setActiveTheme] = useState(null)

  useEffect(() => {
    // Subscribe to our store changes
    const subscriptionId = store.subscribe((notification) => {
      // Update theme
      setActiveTheme(notification.theme)

      // Save selected theme mode to localStorage
      localStorage.setItem('theme', notification.mode)
    })

    return () => {
      store.usubscribe(subscriptionId)
    }
  }, [])

  return (
    <>
      <p>
        Active theme: <b>{activeTheme}</b>
      </p>
      <p>Change theme to:</p>
      <button onClick={() => store.changeThemeMode("dark")}>Dark</button>
      <button onClick={() => store.changeThemeMode("light")}>Light</button>
      <button onClick={() => store.changeThemeMode("system")}>System</button>
    <>
  )
}

Спасибо!

Я ценю, что вы присоединились ко мне в этом путешествии, и если вам удалось заставить его работать, я горжусь вами! Если что-то у вас не работает или вы хотите найти весь код, вы можете найти его здесь.

Источник:

#JavaScript
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться