Публикуем вторую часть цикла статей о создании современного блога на 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.
Удалось выполнить все шаги? Как вы думаете, темная тема это просто хайп или все-таки необходимость? Делитесь мыслями в комментариях.
Ссылки на необходимые материалы: