Переоцененная технология или необходимость? Узнайте все о тёмной теме и о том, как ее поддерживать, чтобы сделать приложение удобнее для пользователей.
Введение
Тёмная тема до тёмной темы
Мы прошли полный цикл с тёмной темой. На заре персональных компьютеров тёмная тема была не вопросом выбора, а фактом: монохромные CRT работали, направляя электронные лучи на фосфоресцирующий экран, а фосфор, использовавшийся в ранних ЭЛТ, был зелёным. Поскольку текст отображался зеленым цветом, а остальная часть экрана была черной, эти модели часто называли зелеными экранами.
Позже появились цветные ЭЛТ, которые отображали несколько цветов с помощью красного, зеленого и синего люминофоров. Они создавали белый цвет, активируя все три люминофора одновременно. С появлением более сложных WYSIWYG настольных издательских систем идея сделать виртуальный документ похожим на физический лист бумаги стала популярной.
Так зародился тренд на темный текст на белом фоне, который затем перешел в ранний интернет, основанный на документах. Первый в истории браузер, WorldWideWeb (помните, CSS ещё даже не был изобретён), отображал веб-страницы таким образом. Интересный факт: второй в истории браузер, Line Mode Browser, был терминальным и использовал зелёный текст на тёмном фоне. В настоящее время веб-страницы и веб-приложения обычно разрабатываются с темным текстом на светлом фоне. Это базовое предположение также задано в коде в таблицах стилей агента пользователя, включая Chrome.
ЭЛТ-мониторы давно ушли в прошлое. Сейчас контент чаще всего создают и смотрят на мобильных устройствах с LCD с подсветкой или энергосберегающими AMOLED. Появление более компактных и портативных компьютеров, планшетов и смартфонов привело к новым моделям использования. Досуг, например просмотр веб-страниц, программирование для удовольствия и игры высокого класса, часто происходит в нерабочее время в условиях низкой освещенности. Люди даже пользуются своими устройствами в постели по ночам. Чем чаще люди пользуются устройствами в темноте, тем популярнее становится идея вернуться к истокам – светлому тексту на тёмном фоне.
Зачем нужна тёмная тема
Тёмная тема из эстетических соображений
Когда людей спрашивают, почему им нравится тёмная тема, самый популярный ответ – "она меньше утомляет глаза", а на втором месте – "она элегантная и красивая". В документации для разработчиков Apple прямо пишет: "Выбор светлой или тёмной темы для большинства пользователей – вопрос эстетики, который может быть не связан с условиями окружающего освещения".
Тёмная тема как инструмент специальных возможностей
Есть и те, кому тёмная тема необходима как инструмент специальных возможностей, например пользователи со слабым зрением. Самый ранний пример такого инструмента специальных возможностей, который мне удалось найти, – это функция CloseView в System 7, в которой был переключатель для режимов Черный на белом и Белый на черном. Хотя System 7 поддерживала цвет, пользовательский интерфейс по умолчанию оставался черно-белым.
Эти реализации на основе инверсии продемонстрировали свои слабые стороны, когда был введен цвет. Исследование аудитории, проведенное Спиро и его коллегами, et al., показало, как люди со слабым зрением используют вычислительные устройства. Все опрошенные пользователи не любили инвертированные изображения, но многие предпочитали светлый текст на темном фоне. Apple учитывает это предпочтение пользователя с помощью функции под названием Smart Invert, которая инвертирует цвета на дисплее, за исключением изображений, мультимедиа и некоторых приложений, использующих тёмные цветовые стили.
Особой формой слабого зрения является синдром компьютерного зрения, также известный как цифровое зрительное напряжение, который определяется как "сочетание проблем с глазами и зрением, связанных с использованием компьютеров (включая настольные компьютеры, ноутбуки и планшеты) и других электронных дисплеев (например, смартфонов и электронных книг)". Предполагается, что использование электронных устройств подростками, особенно в ночное время, приводит к повышенному риску сокращения продолжительности сна, увеличению задержки начала сна и усилению дефицита сна. Кроме того, согласно отчетам, воздействие синего света влияет на регуляцию циркадного ритма и цикла сна, а нерегулярное освещение может привести к недосыпанию, что, согласно исследованию Розенфилда, может повлиять на настроение и работоспособность. Чтобы ограничить эти негативные эффекты, уменьшите синий свет, отрегулировав цветовую температуру дисплея с помощью таких функций, как Night Shift в iOS или Ночная подсветка в Android. Также избегайте яркого или неравномерного света, используя тёмные темы или ночные режимы.
Экономия энергии при использовании тёмной темы на экранах AMOLED
Наконец, тёмная тема позволяет значительно экономить заряд батареи на устройствах с AMOLED. Исследования Android, в которых основное внимание уделялось популярным приложениям Google, таким как YouTube, показали, что экономия энергии может составлять до 60%. В видео ниже вы найдете подробную информацию об этих исследованиях и экономии энергии в каждом приложении.
Включение тёмной темы в операционной системе.
Теперь, когда я рассказал, почему тёмная тема так важна для многих пользователей, давайте посмотрим, как вы можете её поддерживать.
В операционных системах, поддерживающих тёмную тему, обычно есть возможность активировать её в настройках. В macOS X эта настройка находится в разделе Основные системных настроек и называется Оформление (скриншот). В Windows 10 она находится в разделе Цвета и называется Выбор цвета (скриншот). В Android Q переключатель Тёмная тема находится в разделе Экран (скриншот). В iOS 13 Оформление можно изменить в разделе Экран и яркость (скриншот).
Запрос медиа prefers-color-scheme
И последнее, что я хочу сказать, прежде чем мы начнем.
Запросы медиа позволяют авторам тестировать и запрашивать значения или функции агента пользователя или устройства отображения независимо от отображаемого документа.
Они используются в правиле CSS @media для условного применения стилей к документу, а также в различных других контекстах и языках, таких как HTML и JavaScript.
В Media Queries Level 5 представлены так называемые медиафункции пользовательских настроек, то есть способ, позволяющий сайтам определять, как пользователь предпочитает отображать контент.
Медиафункция prefers-color-scheme используется для определения того, какую цветовую тему (светлую или темную) пользователь выбрал для страницы.
Возможные значения:
light– указывает, что пользователь предпочитает светлую тему (темный текст на светлом фоне).dark– указывает, что пользователь предпочитает тёмную тему (светлый текст на тёмном фоне).
Поддержка тёмной темы
Как узнать, поддерживает ли браузер тёмную тему
Поскольку тёмная тема задается с помощью запроса медиа, вы можете легко проверить, поддерживает ли ее текущий браузер. Для этого нужно посмотреть, соответствует ли запрос медиа prefers-color-scheme.
Обратите внимание, что я не указываю значение, а просто проверяю, соответствует ли запрос медиа.
if (window.matchMedia('(prefers-color-scheme)').media !== 'not all') {
console.log('🎉 Dark mode is supported');
}
На момент написания этой статьи медиазапрос prefers-color-scheme поддерживается в Chrome и Edge версии 76 и выше, Firefox версии 67 и выше, Safari версии 12.1 и выше на macOS и версии 13 и выше на iOS.
Информацию о других браузерах можно найти в таблицах поддержки Can I use.
Узнавать о предпочтениях пользователя во время запроса
Заголовок подсказки клиента Sec-CH-Prefers-Color-Scheme позволяет сайтам получать информацию о предпочитаемой пользователем цветовой схеме во время запроса. Это дает серверам возможность встраивать правильный CSS и избегать мерцания неправильной цветовой темы.
Тёмная тема на практике
Давайте посмотрим, как поддержка тёмной темы выглядит на практике. Как и в фильме Горец, с тёмной темой должен остаться только один: тёмный или светлый, но не оба сразу! Почему я упоминаю об этом? Потому что этот факт должен повлиять на стратегию загрузки. Не заставляйте пользователей скачивать CSS в критическом пути рендеринга, который предназначен для режима, который они в данный момент не используют. Чтобы оптимизировать скорость загрузки, я разделил CSS для примера приложения, в котором на практике показаны следующие рекомендации, на три части, чтобы отложить некритичный CSS:
style.css, в котором содержатся общие правила, применяемые ко всему сайту.dark.css, содержащий только правила, необходимые для тёмной темы.light.css, содержащий только правила, необходимые для светлой темы.
Стратегия загрузки
Два последних, light.css и dark.css, загружаются условно с помощью запроса <link media>.
Сначала не все браузеры будут поддерживать prefers-color-scheme (это можно определить с помощью шаблона выше). Я решаю эту проблему динамически, загружая файл light.css по умолчанию через условно вставленный элемент <link rel="stylesheet"> в крошечном встроенном скрипте (светлая тема – произвольный выбор, я также мог бы сделать темную тему резервной по умолчанию).
Чтобы избежать вспышки нестилизованного контента, я скрываю контент страницы до загрузки light.css.
<script>
// If `prefers-color-scheme` is not supported, fall back to light mode.
// In this case, light.css will be downloaded with `highest` priority.
if (window.matchMedia('(prefers-color-scheme: dark)').media === 'not all') {
document.documentElement.style.display = 'none';
document.head.insertAdjacentHTML(
'beforeend',
'<link rel="stylesheet" href="/light.css" onload="document.documentElement.style.display = \'\'">',
);
}
</script>
<!--
Conditionally either load the light or the dark stylesheet. The matching file
will be downloaded with `highest`, the non-matching file with `lowest`
priority. If the browser doesn't support `prefers-color-scheme`, the media
query is unknown and the files are downloaded with `lowest` priority (but
above I already force `highest` priority for my default light experience).
-->
<link rel="stylesheet" href="/dark.css" media="(prefers-color-scheme: dark)" />
<link
rel="stylesheet"
href="/light.css"
media="(prefers-color-scheme: light)"
/>
<!-- The main stylesheet -->
<link rel="stylesheet" href="/style.css" />
Архитектура таблицы стилей
Я по максимуму использую переменные CSS, чтобы мой общий файл style.css оставался общим, а все настройки светлой и тёмной тем находились в двух других файлах: dark.css и light.css.
Ниже приведен фрагмент кода, который дает общее представление о том, как это делается.
Я объявляю две переменные, --color и --background-color, которые по сути создают базовые темы: тёмный текст на светлом фоне и светлый текст на тёмном фоне.
/* light.css: 👉 dark-on-light */
:root {
--color: rgb(5, 5, 5);
--background-color: rgb(250, 250, 250);
}
/* dark.css: 👉 light-on-dark */
:root {
--color: rgb(250, 250, 250);
--background-color: rgb(5, 5, 5);
}
Затем в style.css я использую эти переменные в правиле body { … }.
Они определены в псевдоклассе CSS :root – селекторе, который в HTML представляет элемент <html> и идентичен селектору html, но имеет более высокую специфичность. Они каскадируются вниз, что позволяет мне объявлять глобальные переменные CSS.
/* style.css */
:root {
color-scheme: light dark;
}
body {
color: var(--color);
background-color: var(--background-color);
}
В приведенном выше примере кода вы, вероятно, заметили свойство color-scheme со значением light dark, разделенным пробелом.
Это сообщает браузеру, какие цветовые темы поддерживает мое приложение, и позволяет ему активировать специальные варианты таблицы стилей агента пользователя. Это полезно, например, для того, чтобы браузер мог отображать поля формы с темным фоном и светлым текстом, настраивать полосы прокрутки или включать цвет выделения с учетом темы.
Подробные сведения о color-scheme приведены в документации по CSS Color Adjustment Module Level 1.
Все остальное – это просто вопрос определения переменных CSS для важных элементов на моем сайте.
Семантическая организация стилей очень помогает при работе с тёмной темой.
Например, вместо --highlight-yellow лучше использовать --accent-color, поскольку в темном режиме желтый цвет может выглядеть иначе.
Ниже приведен пример некоторых других переменных, которые я использую в своем примере.
/* dark.css */
:root {
--color: rgb(250, 250, 250);
--background-color: rgb(5, 5, 5);
--link-color: rgb(0, 188, 212);
--main-headline-color: rgb(233, 30, 99);
--accent-background-color: rgb(0, 188, 212);
--accent-color: rgb(5, 5, 5);
}
/* light.css */
:root {
--color: rgb(5, 5, 5);
--background-color: rgb(250, 250, 250);
--link-color: rgb(0, 0, 238);
--main-headline-color: rgb(0, 0, 192);
--accent-background-color: rgb(0, 0, 238);
--accent-color: rgb(250, 250, 250);
}
Полный пример
В встроенной демоверсии ниже показан полный пример, в котором на практике реализованы описанные выше концепции. Попробуйте переключить тёмную тему в настройках операционной системы и посмотрите, как изменится страница.
Загрузка влияния
Поэкспериментировав с этим примером, вы поймете, почему я загружаю dark.css и light.css с помощью запросов медиа.
Попробуйте переключить темную тему и перезагрузить страницу.
Таблицы стилей, которые не соответствуют текущему режиму, все равно загружаются, но с самым низким приоритетом,
поэтому они не конкурируют с ресурсами, которые нужны сайту прямо сейчас.
prefers-color-scheme, загружает CSS тёмного режима с самым низким приоритетом.Реакция на изменения тёмной темы
Как и в случае с любым другим изменением медиазапроса, на изменения тёмного режима можно подписаться через JavaScript.
Например, вы можете динамически менять значок сайта или <meta name="theme-color">, который определяет цвет адресной строки в Chrome.
Чтобы увидеть, как это работает, откройте демоверсию в отдельной вкладке.
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeMediaQuery.addEventListener('change', (e) => {
const darkModeOn = e.matches;
console.log(`Dark mode is ${darkModeOn ? '🌒 on' : '☀️ off'}.`);
});
Начиная с Chromium 93 и Safari 15, вы можете настраивать цвет на основе запроса медиа с помощью атрибута media элемента цвета темы meta. Будет выбрано первое совпадение. Например, у вас может быть один цвет для
светлого режима и другой для тёмного режима. На момент написания этой статьи
определить их в манифесте нельзя. Подробнее о проблеме w3c/manifest#975 на GitHub…
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="white"
/>
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black" />
Отладка и тестирование тёмной темы
Эмуляция prefers-color-scheme в инструментах разработчика
Переключать цветовую схему всей операционной системы может быть неудобно, поэтому теперь в инструментах разработчика Chrome можно эмулировать предпочитаемую пользователем цветовую схему так, чтобы это влияло только на текущую видимую вкладку.
Откройте меню управления, начните вводить Rendering, выполните команду Show Rendering и измените параметр Эмулировать медиафункцию CSS prefers-color-scheme.

Как создавать скриншоты prefers-color-scheme с помощью Puppeteer
Puppeteer – это библиотека Node.js, которая предоставляет высокоуровневый API для управления Chrome или Chromium через протокол инструментов разработчика.
В dark-mode-screenshot мы предоставляем скрипт Puppeteer, который позволяет создавать скриншоты страниц в темном и светлом режимах.
Вы можете запустить этот скрипт один раз или сделать его частью набора тестов непрерывной интеграции.
npx dark-mode-screenshot --url https://googlechromelabs.github.io/dark-mode-toggle/demo/ --output screenshot --fullPage --pause 750
Рекомендации по работе с тёмной темой
Избегайте чисто белого цвета
Возможно, вы заметили, что я не использую чисто белый цвет.
Чтобы избежать свечения и размытия на фоне окружающего темного контента, я выбираю чуть более темный белый цвет. Например, rgb(250, 250, 250).
Перекрашивание и затемнение фотографий
Если вы сравните два скриншота ниже, то заметите, что изменилась не только основная тема (с тёмного на светлом на светлое на тёмном), но и главное изображение. Моё изучение аудитории показало, что большинство опрошенных предпочитают чуть менее яркие и блестящие изображения, когда активен тёмный режим. Я называю это перекрашиванием.
Перекрасить изображения можно с помощью CSS-фильтра.
Я использую селектор CSS, который соответствует всем изображениям, в URL которых нет .svg. Это позволяет мне по-разному перекрашивать векторную графику (значки) и изображения (фотографии). Подробнее об этом рассказывается в следующем разделе.
Обратите внимание, что я снова использую переменную CSS, чтобы позже можно было легко изменить фильтр.
Поскольку перекрашивание требуется только в тёмной теме, то есть когда активно правило dark.css, в правиле light.css нет соответствующих инструкций.
/* dark.css */
--image-filter: grayscale(50%);
img:not([src*='.svg']) {
filter: var(--image-filter);
}
Как настроить интенсивность перекрашивания в тёмной теме с помощью JavaScript
У всех разные потребности в тёмной теме.
Придерживаясь описанного выше метода перекрашивания, я могу легко сделать интенсивность оттенков серого пользовательской настройкой, которую можно изменить с помощью JavaScript, а установив значение 0%, я также могу полностью отключить перекрашивание.
Обратите внимание, что document.documentElement ссылается на корневой элемент документа, то есть на тот же элемент, на который можно сослаться с помощью псевдокласса CSS :root.
const filter = 'grayscale(70%)';
document.documentElement.style.setProperty('--image-filter', value);
Инвертировать векторную графику и значки
Для векторной графики, которая в моем случае используется в качестве значков, на которые я ссылаюсь через элементы <img>, я использую другой метод перекрашивания.
Хотя исследования показали, что пользователям не нравится инверсия цветов на фотографиях, она отлично подходит для большинства значков.
И снова я использую переменные CSS, чтобы определить степень инверсии в обычном состоянии и в состоянии :hover.
Обратите внимание, что я снова инвертирую значки только в dark.css, а не в light.css, и что :hover получает разную интенсивность инверсии в двух случаях, чтобы значок выглядел немного темнее или немного ярче, в зависимости от режима, выбранного пользователем.
/* dark.css */
--icon-filter: invert(100%);
--icon-filter_hover: invert(40%);
img[src*='.svg'] {
filter: var(--icon-filter);
}
/* light.css */
--icon-filter_hover: invert(60%);
/* style.css */
img[src*='.svg']:hover {
filter: var(--icon-filter_hover);
}
Использование currentColor для встроенных изображений SVG
Для встроенных изображений SVG вместо инвертирующих фильтров можно использовать ключевое слово CSS currentColor, которое представляет значение свойства color элемента.
Это позволяет использовать значение color в свойствах, которые не получают его по умолчанию.
Если currentColor используется в качестве значения атрибутов fill или stroke SVG, то вместо этого оно принимает значение, унаследованное от свойства color.
Более того, это работает и для <svg><use href="…"></svg>, поэтому вы можете использовать отдельные ресурсы, и currentColor все равно будет применяться в контексте.
Обратите внимание, что это работает только для встроенных или <use href="…"> SVG, но не для SVG, на которые ссылаются как на src изображения или каким-либо образом через CSS.
Посмотрите, как это работает, в демоверсии ниже.
<!-- Some inline SVG -->
<svg xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
>
[…]
</svg>
Плавные переходы между режимами
Переключение между тёмной и светлой темами можно сделать более плавным, поскольку и color, и background-color – это анимируемые свойства CSS.
Чтобы создать анимацию, достаточно объявить два правила transition для двух свойств.
Пример ниже иллюстрирует общую идею. Вы можете посмотреть, как это работает, в демоверсии.
body {
--duration: 0.5s;
--timing: ease;
color: var(--color);
background-color: var(--background-color);
transition: color var(--duration) var(--timing), background-color var(
--duration
) var(--timing);
}
Визуальное оформление с тёмной темой
Хотя для повышения скорости загрузки я рекомендую использовать prefers-color-scheme только в атрибуте media элементов <link>, а не в таблицах стилей, в некоторых случаях может быть целесообразно добавлять prefers-color-scheme непосредственно в HTML-код.
Это и есть художественное руководство.
В веб-дизайне художественное руководство связано с общим визуальным видом страницы и тем, как она передаёт информацию визуально, стимулирует настроение, контрастирует с функциями и психологически привлекает целевую аудиторию.
При использовании тёмной темы дизайнер должен решить, какое изображение лучше всего подходит для конкретного режима и достаточно ли перекрасить изображения.
Если атрибут <source> используется с элементом <picture>, то показываемое изображение может зависеть от атрибута media.
В примере ниже показано, как настроить показ Западного полушария в тёмной теме и Восточного полушария в светлой теме или когда тема не выбрана. Во всех остальных случаях по умолчанию будет показываться Восточное полушарие.
Это, конечно, чисто в иллюстративных целях.
Чтобы увидеть разницу, включите тёмную тему на устройстве.
<picture>
<source srcset="western.webp" media="(prefers-color-scheme: dark)" />
<source srcset="eastern.webp" media="(prefers-color-scheme: light)" />
<img src="eastern.webp" />
</picture>
Тёмная тема с возможностью отключения
Как упоминалось в разделе Зачем нужна тёмная тема, для большинства пользователей она является эстетическим выбором.
В результате некоторым пользователям может нравиться темный интерфейс операционной системы, но они предпочитают видеть веб-страницы так, как они привыкли их видеть.
Отличный вариант – сначала придерживаться сигнала, который браузер отправляет через prefers-color-scheme, а затем при необходимости разрешить пользователям переопределять настройки на уровне системы.
Специальный элемент <dark-mode-toggle>
Вы можете написать код самостоятельно или использовать готовый пользовательский элемент (веб-компонент), который я создал специально для этой цели.
Он называется <dark-mode-toggle> и добавляет на страницу переключатель (тёмная тема: вкл./выкл.) или селектор темы (тема: светлая/тёмная), который можно полностью настроить.
В демо ниже показан элемент в действии.
(Кстати, я также 🤫 незаметно добавил его во все
другие
примеры
выше).
<dark-mode-toggle
legend="Theme Switcher"
appearance="switch"
dark="Dark"
light="Light"
remember="Remember this"
></dark-mode-toggle>
<dark-mode-toggle> в светлой теме.
<dark-mode-toggle> в тёмной теме.
Попробуйте нажать на элементы управления тёмной темой в правом верхнем углу в демоверсии ниже. Если вы установите флажок в третьем и четвертом элементах управления, то увидите, что выбранный режим сохраняется даже после перезагрузки страницы. Это позволяет посетителям использовать тёмную тему в операционной системе, а светлую тему – на вашем сайте, или наоборот.
Выводы
Работа с тёмной темой и ее поддержка – это увлекательный процесс, который открывает новые возможности для дизайна.
Для некоторых посетителей это может быть разницей между невозможностью пользоваться вашим сайтом и тем, чтобы быть счастливым пользователем.
Есть некоторые подводные камни, и тщательное тестирование определенно необходимо, но тёмная тема – это отличная возможность показать, что вы заботитесь обо всех своих пользователях.
Рекомендации, приведенные в этой статье, и вспомогательные инструменты, такие как настраиваемый элемент <dark-mode-toggle>, помогут вам создать отличную тёмную тему.
Расскажите мне в Твиттере, что у вас получилось, была ли эта запись полезной и что можно улучшить.
Благодарим за внимание! 🌒
Статьи по теме
Ресурсы для медиазапроса prefers-color-scheme:
Ресурсы, посвященные метатегу color-scheme и свойству CSS:
- Свойство CSS
color-schemeи метатег - Страница состояния платформы Chrome
- Ошибка Chromium
- Спецификация модуля настройки цвета CSS (уровень 1)
- Проблема с метатегом и свойством CSS на GitHub
- HTML WHATWG GitHub Issue for the meta tag
Ссылки на статьи о тёмной теме:
- Material Design – тёмная тема
- Тёмная тема в веб-инспекторе
- Поддержка тёмной темы в WebKit
- Рекомендации Apple по созданию интерфейсов – тёмная тема
Статьи, использованные для этой записи:
- Что на самом деле делает свойство supported-color-schemes в тёмной теме? 🤔
- Да будет тьма! 🌚 Может быть…
- Перекрашивание для тёмной темы
Благодарности
Медиафункция prefers-color-scheme, свойство CSS color-scheme и связанный метатег – результат работы 👏 Руне Лиллесвеена (Rune Lillesveen).
Руне также является соредактором спецификации CSS Color Adjustment Module Level 1.
Я хочу поблагодарить Лукаша Збылюта (Lukasz Zbylut),
Роуэна Меревуда (Rowan Merewood),
Чирага Десаи (Chirag Desai)
и Роба Додсона (Rob Dodson)
за тщательную проверку этой статьи.
Стратегия загрузки разработана Джейком Арчибальдом (Jake Archibald).
Эмилио Кобос Альварес (Emilio Cobos Álvarez) указал мне на правильный метод обнаружения prefers-color-scheme.
Совет со ссылками на SVG и currentColor взят у Тимоти Хэтчера (Timothy Hatcher).
Наконец, я благодарен многочисленным анонимным участникам различных исследований предпочтений аудитории, которые помогли сформировать рекомендации в этой статье.