Nuxt.js app от UI-кита до деплоя. Часть 2: Темная тема

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
Привет, Хабр!

Публикуем вторую часть серии статей о создании современного блога на Nuxt.js. Сегодня реализуем темную тему в приложении, которое мы написали вместе с вами в первой части.

Обратите внимание, что код каждой части можно найти в собственной ветке на Github, а в master доступна версия приложения из последней опубликованной статьи.



Что такое темная тема?


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

Темная тема улучшает визуальную эргономику, снижая нагрузку на глаза, настраивая экран в соответствии с текущими условиями освещения и обеспечивая простоту использования в ночное время или в темноте.

Также нельзя забывать о том, что использование темной темы в веб- и мобильных приложениях может продлить срок службы батареи устройства. Компания Google подтвердила, что темная тема на OLED экранах очень помогает продлить срок службы батареи.

@nuxtjs/color-mode


Для реализации темной темы мы будем использовать модуль @nuxtjs/color-mode, который предоставляет следующие возможности:

  • добавляет класс .${color}-mode к тегу <html> для упрощения управлением темами CSS;
  • работает в любом режиме Nuxt (static, ssr или spa);
  • автоматически определяет цветовой режим системы на устройстве пользователя и может установить соответствующую тему, исходя из этих данных;
  • позволяет синхронизировать выбранную тему между вкладками и окнами;
  • позволяет использовать реализованные темы для отдельных страниц, а не для всего приложения (идеально подходит для постепенной разработки);
  • также модуль поддерживает IE9+ (не уверен, что это всё еще актуально в современной разработке, но кому-то может пригодиться).

Для начала установим модуль:

npm i --save-dev @nuxtjs/color-mode

А затем добавим добавим информацию об этом модуле в секцию buildModules в файле nuxt.config.js:

{
  buildModules: [
    '@nuxtjs/color-mode'
  ]
}

Отлично! Теперь, если мы запустим наше приложение и откроем вкладку Elements в консоли разработчика, то увидим, что к тегу html добавился класс, который соответствует теме операционный системы, например, в нашем случае class="light-mode".

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


На следующем этапе давайте реализуем переключатель, который будет менять темную тему на светлую и наоборот.

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

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

Для этого создадим компонент AppOptions со следующим содержимым:

<template lang="pug">
section.section
  .content
    .app-options
      switcher-color-mode
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'AppOptions',
})
</script>

<style lang="scss" scoped>
.app-options {
  display: flex;
  margin-top: 24px;
}
</style>

Компонент на Github.

Как мы видим, в этом компоненте нет никакой логики, он просто устанавливает внешние отступы для вложенных компонентов. Сейчас у нас только один вложенный компонент switcher-color-mode, реализуем его.

Взглянем на секцию script этого компонента:

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'SwitcherColorMode',

  computed: {
    icon() {
      return (this as any).$colorMode.value === 'light'
        ? 'assets/icons/sun.svg'
        : 'assets/icons/moon.svg'
    },
  },

  methods: {
    changeColorMode() {
      ;(this as any).$colorMode.preference =
        (this as any).$colorMode.value === 'light' ? 'dark' : 'light'
    },
  },
})
</script>

Здесь мы реализуем метод changeColorMode, который меняет тему в объекте, предоставляемом модулем @nuxtjs/color-mode.

При изменении значения $colorMode.preference также будет установлен соответствующий класс у тега html: class="light-mode" или class="dark-mode".

Кроме того, здесь есть вычисляемое свойство icon, которое возвращает нужную нам иконку в зависимости от выбранной темы. Обратите внимание, что для корректной работы нужно добавить иконки sun.svg и moon.svg в директорию assets/icons.

Шаблон компонента будет выглядеть следующим образом:

<template lang="pug">
button(@click="changeColorMode")
  img(
    alt="theme-icon"
    :src="getDynamicFile(icon)"
  )
</template>

Здесь всё совсем просто! У нас есть кнопка, при клике на которую мы вызываем метод changeColorMode и меняем нашу тему. Внутри кнопки мы показываем изображение выбранной темы.

Компонент на Github.

Остается только добавить этот компонент на главную страницу нашего приложения. После этого шаблон страницы должен выглядеть так:

<template lang="pug">
.page
  section-header(
    title="Nuxt blog"
    subtitle="The best blog you can find on the global internet"
  )

  app-options

  post-list
</template>

Управление переменными


Как вы, возможно, помните из первой части, для определения всех цветов в приложении мы использовали scss переменные, и теперь всё, что нам остаётся сделать, это изменять значения этих переменных в зависимости от выбранной темы.

Но проблема в том, что scss переменные задаются один раз при сборке приложения и в дальнейшем мы не можем переопределять их при изменении темы.

Это ограничение можно обойти с помощью js, но есть решение намного проще: мы можем использовать нативные css переменные.

Сейчас в нашем файле с переменными assets/styles/variables.scss секция с цветами выглядит следующим образом:

// colors  
$text-primary:                      rgb(22, 22, 23);  
$text-secondary:                    rgb(110, 109, 122);  
$line-color:                        rgb(231, 231, 233);  
$background-color:                  rgb(243, 243, 244);  
$html-background-color:             rgb(255, 255, 255);

Давайте для начала в этом же файле определим две цветовые схемы — светлую и темную — с помощью css переменных:

:root {
  // light theme
  --text-primary:                   rgb(22, 22, 23);  
  --text-secondary:                 rgb(110, 109, 122);  
  --line-color:                     rgb(231, 231, 233);  
  --background-color:               rgb(243, 243, 244);  
  --html-background-color:          rgb(255, 255, 255);  
  
  // dark theme  
  &.dark-mode {
    --text-primary:                 rgb(250, 250, 250);  
    --text-secondary:               rgb(188, 187, 201);  
    --line-color:                   rgb(45, 55, 72);  
    --background-color:             rgb(45, 55, 72);  
    --html-background-color:        rgb(26, 32, 44);  
  }  
}

Мы определили css переменные в селекторе :root. По стандарту css переменная задается и используется с помощью префикса --.

О css псевдоклассе :root можно прочесть на MDN и W3Schools. Цитата с MDN:

css псевдокласс :root находит корневой элемент дерева документа. Применимо к HTML, :root находит тег html и идентичен селектору по тегу html, но его специфичность выше.

Как мы видим, те цвета, которые раньше были прописаны напрямую в scss переменные, сейчас указаны в css переменных как значения по умолчанию, а при наличии класса .dark-mode эти значения переопределяются.

Теперь наши scss переменные с цветами будут выглядеть следующим образом:

$text-primary:                      var(--text-primary);  
$text-secondary:                    var(--text-secondary);  
$line-color:                        var(--line-color);  
$background-color:                  var(--background-color);  
$html-background-color:             var(--html-background-color);

При переключении темы цветовая схема будет меняться в соответствии с заданными значениями и нам не нужно ничего менять в уже реализованных компонентах.

Заключение


Благодаря этой статье мы научились реализовывать темную тему для приложения на Nuxt.js.

Удалось выполнить все шаги? Как вы думаете, темная тема — это просто хайп или все-таки необходимость? Делитесь мыслями в комментариях.

Ссылки на необходимые материалы:

  • дизайн,
  • Github,
  • демо второй части.
Источник: https://habr.com/ru/company/timeweb/blog/545950/


Интересные статьи

Интересные статьи

Продолжение (начало – здесь) 1.3. Поисковые системы – специализированные и не очень В общем случае результаты поиска в первую очередь зависят от поставленной задачи и корректности з...
Сегодня мы публикуем перевод первой статьи из серии материалов, посвящённых реализации epoll в ядре Linux 3.16.1*. Автор исходит из предположения о том, что читатели знакомы с API и с исп...
Продолжаем делиться выдержками из руководства по выживанию для начинающих техлидов от Дж. Х. Рейнвотера. В первой серии мы рассказывали, с какими породами разработчиков руководителю обычно пр...
Посвящается всем туристам-водникам, рыбакам и морякам, которым удалось или не удалось (но, надеюсь, удастся после прочтения статьи) заклеить дыры на своих ПВХ судах, ибо не Десмоколом единым... ...
Это продолжение длинного рассказа о нашем тернистом пути к созданию мощной, высоконагруженной системы, обеспечивающей работу Биржи. Первая часть тут.