В последних версиях macOS (Mojave) и Windows 10 пользователи смогли включить темный режим на системном уровне. Это хорошо работает, и его легко обнаружить для собственных приложений.

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

Возможно ли, чтобы это обнаружение выполнялось автоматически и на веб-сайтах отображалась тема, учитывающая предпочтения пользователя?

Медиа-запрос CSS ‘prefers-color-scheme' draft

Есть черновик медиазапросов CSS уровня 5, где указано prefers-color-scheme. Он предназначен для определения того, запросил ли пользователь систему использовать светлую или темную цветовую тему.

Это похоже на то, с чем мы можем работать! Однако нам нужно быть в курсе любых изменений в черновике, так как он может измениться в любой момент, поскольку это всего лишь ... черновик. Запрос prefers-color-scheme может иметь три разных значения: light, dark и no-preference.

Поддержка веб-браузера с марта 2019 г.

Текущая поддержка браузера очень ограничена, и она недоступна ни в одном из стабильных выпусков каких-либо поставщиков. Мы можем наслаждаться этим только в Safari Technology Preview версии 12.1 и в Firefox 67.0a1. Что замечательно, так это то, что есть двоичные файлы, которые его поддерживают, поэтому мы можем работать с ним и опробовать его в веб-браузерах. Чтобы узнать о текущей поддержке браузера, посетите https://caniuse.com/#search=prefers-color-scheme.

Почему одного определения CSS недостаточно

Распространенный подход, который я видел до сих пор, - это использовать подход, основанный только на CSS, и переопределять правила CSS для определенных классов при сопоставлении медиа-запроса.
Что-то вроде этого:

/* global.css */
.themed {
  display: block;
  width: 10em;
  height: 10em;
  background: black;
  color: white;
}
@media (prefers-color-scheme: light) {
  .themed {
    background: white;
    color: black;
  }
}

Поскольку это отлично работает для многих случаев использования, существуют методы стилизации, в которых CSS не используется подобным образом. Если, например, для оформления темы используется styled-components, то при изменении темы объект JS заменяется.

Доступ к предпочтительной схеме также полезен для аналитики и более предсказуемых переопределений CSS, а также для более детального контроля над тем, какие элементы должны быть тематическими, а какие нет.

Первоначальный подход JS

Раньше я узнал, что вы можете выполнять обнаружение медиа-запросов, задав для CSS content элемента значение, если медиа-запрос соответствует. Это определенно взлом, но он работает!

Что-то вроде этого:

Поэтому, когда пользователь загружает CSS и медиа-запрос соответствует одной из вышеперечисленных цветовых схем, значение свойства content элемента html устанавливается на «светлый» или «темный».

Тогда возникает вопрос, как нам прочитать значение content элемента html?

Мы можем использовать window.getComputedStyle, например:

И это прекрасно работает! Этот подход подходит для одноразового чтения, но он не является реактивным и автоматически обновляется, когда пользователь меняет цветовую схему системы. Для обновления требуется перезагрузка страницы (или выполнение вышеуказанного чтения с интервалом).

Реактивный подход JS

Как мы можем узнать, когда пользователь меняет цветовую схему системы? Есть ли какие-нибудь события, которые мы можем слушать?

Да это так!

В современных веб-браузерах есть window.matchMedia.

Что замечательно в matchMedia, так это то, что мы можем прикрепить к нему слушателя, который будет вызываться в любое время, когда совпадение изменится.

Слушатель будет вызван с объектом, содержащим информацию, если медиа-запрос начал сопоставление или прекратил сопоставление. Имея эту информацию, мы можем полностью пропустить CSS и просто работать с JS.

Этот подход очень хорошо работает в поддерживаемых веб-браузерах и просто отключается, если window.matchMedia не поддерживается.

Реагировать на крючок

Поскольку мы используем React в neo4j-browser, я написал это как собственный перехватчик React, чтобы упростить повторное использование во всех наших приложениях и вписаться в систему React.

Это немного больше кода, чем в первом коротком доказательстве концепции. У нас улучшено обнаружение ошибок, и мы также удаляем прослушиватели событий, когда перехватчик размонтируется.

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

А затем используйте его так на уровне приложения:

Последняя часть зависит от того, как тематика сделана в вашем приложении. В приведенном выше примере объект данных темы передается поставщику контекста, который делает этот объект доступным во всем приложении React.

Конечный результат

Вот гифка с конечным результатом, и, как видите, он мгновенный.

Последние мысли

Это был забавный эксперимент, сделанный во время так называемого лабораторного дня, который мы проводим в команде UX в Neo4j. Ранние стадии спецификации и (следовательно) отсутствие поддержки браузеров еще не оправдывают превращение этого в какой-либо продукт. Но поддержка может прийти раньше, чем позже.

Кроме того, мы поставляем некоторые продукты на базе Electron, и там есть electron.systemPreferences.isDarkMode() ...

Об авторе

Оскар Хейн - руководитель группы / старший инженер в Neo4j.
Он работает над несколькими приложениями для конечных пользователей и библиотеками кода Neo4j: s, а также является автором двух технических книг.

Следуйте за Оскаром в Twitter: @oskarhane