История с тёмным режимом замкнулась. На заре персональных компьютеров «тёмный режим» был не вопросом выбора, а единственной возможностью: монохромные ЭЛТ в компьютерных мониторах испускали электронные лучи на фосфоресцирующий экран с зеленым люминофором. Текст отображался зеленым цветом на черном фоне, поэтому такие мониторы часто назывались зелеными экранами.
Появившиеся впоследствии цветные ЭЛТ отображали несколько цветов — за счет использования красного, зеленого и синего люминофоров. Белый цвет формировался при одновременной активации всех трех люминофоров. С появлением более сложных настольных издательских систем, работающих по принципу WYSIWYG, стала популярной идея делать виртуальный документ похожим на физический лист бумаги.
Концепция темный текст на белом фоне прижилась и была перенесена в ранний интернет, поскольку тогда веб-страницы представляли собой в первую очередь документ. Самый первый браузер — WorldWideWeb (помните: CSS еще не изобрели) отображал веб-страницы именно так. Интересно, что второй браузер — Line Mode Browser, работавший в терминале — отображал страницы зеленым текстом на тёмном фоне. В наши дни на веб-страницах и в веб-приложениях обычно используется тёмный текст на светлом фоне — это базовое предположение, которое, к тому же, жестко закодировано в таблицах стилей браузеров, включая Chrome.
Человек использует смартфон, лежа в постели (источник: Unsplash)
Времена ЭЛТ давно прошли. Мы начали потреблять и создавать контент на мобильных устройствах, в которых используются ЖК-дисплеи с подсветкой и энергоэффективные AMOLED-экраны. Уменьшение габаритов компьютеров, появление планшетов и смартфонов привело к новым моделям использования. Мы часто проводим досуг — просматриваем веб-страницы, программируем «для себя», запускаем игры, — после окончания рабочего дня в условиях низкой освещенности и даже в постели ночью. Чем больше людей используют гаджеты в темноте, тем популярней становится идея вернуться к отображению контента в режиме светлый текст на тёмном фоне.
Использование тёмного режима по эстетическим соображениям #
Если поспрашивать, почему люди предпочитают тёмный режим, самым популярным ответом будет «меньше нагрузка на глаза», а на втором месте — «это элегантно и красиво». Apple в документации по тёмному режиму для разработчиков так и пишут: «Выбор между тёмным и светлым внешним видом для большинства пользователей — вопрос эстетики и может не иметь отношения к условиям освещения».
Тёмный режим как инструмент специальных возможностей #
Есть люди (например, пользователи со слабым зрением), которым тёмный режим нужен и которые используют его как инструмент специальных возможностей. Самый ранний вариант такого применения, который я смог найти, — это функция CloseView в System 7, в которой был переключатель режимов Черный на белом (Black on White) и Белый на черном (White on Black). System 7 поддерживала цветной режим, но интерфейс по умолчанию оставался черно-белым.
Недостатки основанных на инверсии вариантов стали очевидны с началом использования цветного режима. Проведенное Szpiro et al. исследование аудитории об использовании вычислительных устройств людьми со слабым зрением показало, что всем опрошенным инвертированные изображения не нравились, но многие предпочитали светлый текст на тёмном фоне. Apple учитывает такое предпочтение пользователем посредством функции Smart Invert, которая инвертирует цвета на экране, за исключением изображений, мультимедийных файлов и некоторых приложений, использующих стили с тёмными цветами.
Существует особая форма слабого зрения — «синдром компьютерного зрения», также известный как зрительное утомление от цифровых устройств и определяемый как «сочетание проблем с глазами и зрением, связанных с использованием компьютеров (включая настольные, портативные и планшеты) и других электронных дисплеев (например, смартфонов и электронных устройств для чтения)». Предполагают, что использование электронных устройств подростками, особенно в ночное время, повышает риск снижения продолжительности сна, задержку начала сна и дефицит сна. Кроме того, неоднократно сообщалось, что воздействие синего света влияет на регуляцию циркадного ритма и цикла сна, причем нерегулярные условия освещения могут приводить к депривации сна, что будет влиять на настроение и эффективность работы (см. исследование Розенфилда. Снизить эти отрицательные эффекты можно, если уменьшить количество синего цвета посредством настройки цветовой температуры дисплея с помощью таких функций, как Night Shift в iOS или Ночная подсветка (Night Light) в Android. Также следует избегать яркого и нерегулярного освещения в целом — с помощью тёмного режима (или темы).
Энергосбережение в тёмном режиме на AMOLED-экранах #
Наконец, тёмный режим позволяет существенно экономить энергию на AMOLED-экранах. Практические исследования популярных приложений Google на устройствах Android (например, YouTube) показали, что экономия может достигать 60 %. В видео ниже рассказывается подробнее об этих исследованиях и об экономии энергии для различных приложений.
Итак, мы рассмотрели предысторию тёмного режима и разобрались, почему его наличие важно для многих пользователей. Теперь давайте посмотрим, как обеспечить его поддержку.
Настройки тёмной темы в Android Q
Если операционная система поддерживает тёмный режим (или тему), обычно где-нибудь в настройках есть соответствующий переключатель. В macOS X он находится в разделе Основные системных настроек и называется Оформление (скриншот), а в Windows 10 его можно найти в разделе Цвета: пункт Выберите свой цвет (скриншот). В Android Q нужно перейти в раздел Экран и активировать переключатель Тёмная тема (скриншот), а на iOS 13 — в Оформление в разделе Экран и яркость в настройках (скриншот).
Еще немного теории, прежде чем мы двинемся дальше. Запросы медиа позволяют тестировать и запрашивать значения и функции агента пользователя или устройства отображения независимо от того, какой документ отрисовывается. Они используются в CSS-правиле @media для условного применения стилей к документу, а также в других контекстах и языках (например, HTML и JavaScript). Спецификация запросов медиа, уровень 5, вводит так называемые функции медиа для пользовательских предпочтений, которые позволяют сайту определить предпочитаемый пользователем способ отображения контента.
Функция медиа prefers-color-scheme позволяет узнать, какую цветовую тему запросил пользователь для страницы: тёмную или светлую. Значения могут быть следующими:
light: пользователь уведомил систему о том, что предпочитает светлую тему на странице (тёмный текст на светлом фоне);
dark: пользователь уведомил систему о том, что предпочитает тёмную тему на странице (светлый текст на тёмном фоне).
Сообщение о поддержке тёмного режима передается через запрос медиа, поэтому можно легко проверить, поддерживает ли текущий браузер тёмный режим: для этого достаточно запросить prefers-color-scheme. Обратите внимание: я не указываю значение, а просто проверяю запрос медиа на соответствие.
if(window.matchMedia('(prefers-color-scheme)').media !=='not all'){ console.log('🎉 Тёмный режим поддерживается'); }
На момент написания статьи 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 на три части — чтобы отложить некритичные стили:
style.css — общие правила, которые используются на сайте в целом;
dark.css — правила, необходимые только для тёмного режима;
light.css — правила, необходимые только для светлого режима.
Две последних таблицы стилей — light.css и dark.css — загружаются условно с помощью запроса <link media>. Поначалу не все браузеры будут поддерживать prefers-color-scheme (как определить, есть ли поддержка, см. выше). Поэтому я загружаю файл по умолчанию light.css через условно вставленный элемент <link rel="stylesheet"> в небольшом встроенном скрипте (резервным вариантом по умолчанию может быть и тёмная тема — это не принципиально). Чтобы избежать кратковременного показа контента без стилей, я скрываю содержимое страницы до тех пор, пока не загрузится light.css.
<script> // Если `prefers-color-scheme` не поддерживается, переходим в светлый режим. // В этом случае `light.css` будет скачан с приоритетом `highest` (самый высокий). 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> <!-- Условная загрузка светлой или тёмной таблицы стилей. Соответствующий запросу файл будет скачан с приоритетом `highest` (самый высокий), не соответствующий — с приоритетом `lowest` (самый низкий). Если браузер не поддерживает `prefers-color-scheme`, ответ на запрос медиа неизвестен и файлы скачиваются с приоритетом `lowest` (но выше я уже установил `highest` для светлой темы по умолчанию). --> <linkrel="stylesheet"href="/dark.css"media="(prefers-color-scheme: dark)"/> <link rel="stylesheet" href="/light.css" media="(prefers-color-scheme: light)" /> <!-- Основная таблица стилей --> <linkrel="stylesheet"href="/style.css"/>
Я широко использую переменные CSS: это позволяет моему универсальному style.css быть… универсальным. Настройка для светлого и тёмного режимов производится в двух других файлах: dark.css и light.css. Ниже приведен фрагмент самих стилей — по нему общая идея должна быть понятна. Я объявляю две переменные: --color и --background-color, которые, по сути, задают базовые темы тёмный на светлом и светлый на тёмном.
Затем в style.css я использую эти переменные в правиле body { … }. Они определены в псевдоклассе CSS :root — селекторе, который в HTML представляет собой элемент <html> и идентичен селектору html, за исключением того, что его специфичность выше. Благодаря этому переменные проходят по иерархии вниз, что позволяет объявлять глобальные переменные CSS.
В примере кода выше вы наверняка заметили свойство color-scheme со значением light dark (пробел в качестве разделителя).
Оно сообщает браузеру, какие цветовые темы поддерживает приложение, и позволяет активировать специальные варианты таблицы стилей в агенте пользователя, что полезно, например, чтобы браузер мог отрисовывать поля формы с тёмным фоном и светлым текстом, настроить полосы прокрутки, или использовать цвет выделения с учетом темы. Подробнее свойство color-scheme описано в модуле настройки цвета CSS, уровень 1.
Всё остальное — это просто определение переменных CSS для элементов на сайте. При работе с тёмным режимом очень помогает семантическая организация стилей. Например, вместо --highlight-yellow можно использовать переменную --accent-color, поскольку «yellow» (желтый) может и не быть желтым в тёмном режиме. Ниже — еще нескольких переменных, которые я использовал в примере.
В следующем фрагменте на Glitch можно ознакомиться с полным примером, реализующим изложенные выше концепции. Попробуйте включить (выключить) тёмный режим в настройках операционной системы и посмотрите, как отреагирует страница.
Поиграв с моим примером, вы увидите, почему я загружаю dark.css и light.css через запросы медиа. Попробуйте переключить цветовой режим и перезагрузить страницу: не соответствующие запросу таблицы стилей будут загружаться, но уже с самым низким приоритетом, и поэтому не будут мешать ресурсам, которые нужны сайту в данный момент.
Сайт в светлом режиме загружает CSS для тёмного режима с самым низким приоритетом.Сайт в тёмном режиме загружает CSS для светлого режима с самым низким приоритетом.Сайт в используемом по умолчанию светлом режиме в браузере, который не поддерживает prefers-color-scheme, загружает CSS для тёмного режима с самым низким приоритетом.
Реакция на включение (выключение) тёмного режима #
На изменение состояния тёмного режима можно подписаться через JavaScript, как и на любое другое изменение запроса медиа. Так можно, например, динамически изменять значок сайта на странице или значение <meta name="theme-color">, которое определяет цвет панели адреса в Chrome. Полный пример выше показывает эту возможность в действии: чтобы увидеть изменение значка и цвета темы, откройте демонстрацию в отдельной вкладке.
В браузерах Chromium (с версии 93) и Safari (с версии 15) цвет можно настраивать на основе запроса медиа с атрибутом media элемента цвета темы meta. При этом будет выбрано первое совпадение. Например, у вас может быть один цвет для светлого режима и другой — для тёмного. На момент написания статьи определить это в манифесте нельзя. См. проблему w3c/manifest № 975 на GitHub.
Переключать цветовую схему операционной системы быстро надоедает, поэтому теперь DevTools в Chrome позволяют эмулировать предпочитаемую цветовую схему пользователя так, что это влияет только на текущую вкладку. Откройте командное меню, начните вводить слово Отрисовка (Rendering) и запустите команду Показать "Отрисовка" (Show Rendering), а затем измените параметр Эмулировать медиафункцию CSS prefers-color-scheme (Emulate CSS media feature prefers-color-scheme).
Как делать скриншоты для prefers-color-scheme с помощью Puppeteer #
Puppeteer — это библиотека Node.js, которая дает высокоуровневый API для управления Chrome (Chromium) по протоколу DevTools. Пакет dark-mode-screenshot представляет собой скрипт Puppeteer, позволяющий делать скриншоты страниц в тёмном и светлом режиме. Его можно запускать по необходимости, а можно добавить в набор тестов для непрерывной интеграции (CI).
Вы могли заметить, что я использую не чистый белый цвет, а слегка более тёмный оттенок — чтобы не было свечения на фоне тёмного контента. Значение вроде rgb(250, 250, 250) отлично подойдет.
Сравнив скриншоты ниже, вы заметите, что изменилась не только основная тема (от тёмного на светлом к светлому на тёмном): главное изображение также выглядит иначе. Мое исследование показало, что большинство опрошенных, работая в тёмном режиме, предпочитают чуть менее яркие изображения. Именно в этом смысле я использую термин перекрашивание.
Главное изображение в тёмном режиме слегка затемненоГлавное изображение в светлом режиме
Перекрасить изображения можно с помощью CSS-фильтра. Я использую селектор, выбирающий все изображения, в URL-адресе которых нет .svg: векторную графику (значки) можно перекрасить по-другому — подробнее об этом в следующем абзаце. Обратите внимание, что я снова использую переменную CSS — чтобы позже фильтр можно было легко изменить.
Перекрашивание нужно только в тёмном режиме, то есть когда активен dark.css, поэтому в light.css соответствующих правил нет.
Настройка интенсивности перекрашивания с помощью JavaScript #
Все мы разные, и у всех разные требования к тёмному режиму. Используя описанный выше способ перекрашивания, интенсивность оттенков серого можно сделать пользовательской настройкой и изменять ее через JavaScript. А установив значение 0%, можно полностью отключить перекрашивание. Помните, что document.documentElement дает ссылку на корневой элемент документа — то есть, на тот же элемент, на который я могу ссылаться посредством псевдокласса CSS :root.
Для векторной графики (в моем случае это значки, на которые я ссылаюсь через элементы <img>) способ перекрашивания другой. Согласно исследованию людей со слабым зрением, инверсия фото пользователям не нравится, но для большинства значков она работает хорошо. Здесь я также использую переменные CSS для задания степени инверсии в обычном состоянии и при наведении курсора — :hover.
Инвертированные значки в тёмном режимеОбычные значки в светлом режиме
Заметьте, что инвертирую значки я тоже только в dark.css, но не в light.css. Кроме того, у :hover интенсивность инверсии для двух случаев различается: в зависимости от выбранного пользователем режима значок отображается или немного темнее, или немного ярче.
Для встроенных SVG вместо фильтров инверсии можно использовать ключевое слово CSS currentColor, которое представляет значение свойства color элемента. Это позволяет применять color для свойств, которые не получают это значение по умолчанию. Удобно, что если currentColor используется как значение атрибутов fill и stroke, оно берет свое значение из унаследованного значения свойства цвета. А что еще лучше, так это то, что такой подход работает и для <svg><use href="…"></svg>, поэтому даже для отдельных ресурсов currentColor может применяться в контексте. Помните, что это работает только для SVG, которые встроены или используются через <use href="…">, но не для SVG, используемых по ссылке src в элементе изображения или иным образом через CSS. Пример смотрите в демонстрации ниже.
Переключение из тёмного режима в светлый и наоборот можно сгладить: у color и background-color есть анимируемые CSS-свойства. Чтобы создать анимацию, достаточно объявить два перехода (transition) для двух свойств. В примере ниже проиллюстрирована общая идея. Как это работает, можно посмотреть в демонстрации.
transition: color var(--duration)var(--timing), background-color var( --duration )var(--timing); }
Выбор художественного оформления в тёмном режиме #
В целом из соображений скорости загрузки я рекомендую работать с prefers-color-scheme исключительно в атрибуте media элементов <link> (а не со встроенными элементами в таблицах стилей). Однако бывают случаи, когда prefers-color-scheme лучше использовать и во встроенных элементах HTML-кода: например, когда нужно задать художественное оформление — то есть, общий визуальный стиль страницы и ее визуальный «язык», то, как она влияет на настроение, противопоставляет элементы и психологически обращается к целевой аудитории.
В нашем случае дизайнер должен решить, какое изображение лучше всего будет работать в конкретном режиме, и может быть недостаточно просто перекрасить изображения. К счастью, элемент <source> показываемого изображения при использовании с <picture> может зависеть от атрибута media. В примере ниже для тёмного режима показано западное полушарие, а для светлого — восточное. Если предпочтение не указано, в остальных случаях также отображается восточное. Понятно, что это сделано исключительно в иллюстративных целях. Чтобы посмотреть, как меняется изображение, попереключайте тёмный режим на устройстве.
Как упоминалось выше в разделе Зачем нужен тёмный режим, основная причина его выбора для большинства пользователей — эстетическая. Поэтому кому-то может быть удобно, чтобы интерфейс операционной системы использовал тёмный режим, а веб-страницы отображались обычным образом. Следовательно, хорошим решением будет учитывать сигнал, отправляемый браузером посредством prefers-color-scheme, но давать пользователям возможность переопределить системную настройку.
Я не сомневаюсь, что вы можете и сами написать соответствующий код, но проще и быстрее использовать готовый элемент (веб-компонент), который я написал специально для этой цели, — <dark-mode-toggle>: он добавляет переключатель тёмного режима (вкл./выкл.) или темы (светлая или тёмная) на страницу, и его можно настроить под себя. В примере ниже этот элемент показан в в действии (ой! 🤫 оказывается, я затащил его и во все остальныепримерывыше).
в светлом режиме {/dark-mode-toggle0} в тёмном режиме {/dark-mode-toggle0}
Понажимайте на элементы управления тёмным режимом справа вверху в демонстрации ниже. Если установить флажок в третьем и четвертом элементах, выбранный режим сохранится, даже если перезагрузить страницу. Так посетители сайта смогут использовать интерфейс системы в тёмном режиме, а сайт просматривать в светлом — и наоборот.
Работать с тёмным режимом и реализовывать его поддержку интересно; это открывает новые дизайнерские возможности. Если раньше кто-то не мог пользоваться вашим сайтом из-за отсутствия тёмного режима, то теперь у вас будет еще один счастливый пользователь. Безусловно, вы обнаружите подводные камни и вам придется тщательно всё протестировать, но в любом случае тёмный режим — это отличная возможность показать, что вы заботитесь обо всех посетителях. Рекомендации в этой статье и удобства вроде специального элемента <dark-mode-toggle> помогут вам с легкостью реализовать продуманный и удобный тёмный режим. Если статья вам пригодилась, твитните об этом мне. Если если предложения, как ее улучшить, — тоже пишите. Спасибо за то, что уделили внимание! 🌒