Тёмная тема

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

Реализация

1. Как объявить поддерживаемые схемы в HTML

ОБЯЗАТЕЛЬНО. Чтобы предотвратить кратковременное появление контента без темы, добавьте тег <meta> в раздел <head>. Так браузер будет знать, какие темы вы поддерживаете, ещё до начала отрисовки. Хотя тег <meta> помогает избежать FOUC, заранее задавая начальный цвет холста, он не может полностью устранить мерцание во всех браузерах или при всех условиях загрузки.

<!-- MANDATORY: Declare support for both light and dark themes -->
<meta name="color-scheme" content="light dark">

2. Примените цветовую схему для всей страницы к CSS :root или html

ОБЯЗАТЕЛЬНО. Примените свойство color-scheme к элементу html или псевдоклассу :root. Браузеры обращаются к корневому элементу, чтобы определить тему для всей области просмотра, включая корневые полосы прокрутки и исходный фон холста. Если применить его только к body, эти глобальные элементы интерфейса могут остаться в светлом режиме, поскольку body не управляет контекстом рендеринга окна.

/* MANDATORY: Apply color-scheme to :root or html for viewport-wide theming */
:root {
  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;
}

3. Определите токены светлых и темных цветов

Вы можете использовать функцию light-dark(), чтобы определить токены цвета, которые автоматически адаптируются к разным значениям color-scheme.

Рекомендуется также хранить необработанные значения цветов в отдельных дополнительных параметрах, чтобы их было проще комбинировать разными способами (и упростить резервное поведение, если это необходимо).

Чтобы лучше контролировать цвета встроенного интерфейса, например accent-color или scrollbar-color, авторы могут добавлять собственные динамические цвета с помощью дополнительных параметров и/или функции light-dark(). Эта функция автоматически выбирает правильный цвет на основе вычисленного значения color-scheme элемента и устраняет необходимость в избыточных медиазапросах, но не требуется для базовой реализации.

:root {
  --color-brand-light: oklch(45% 0.23 270);
  --color-brand-dark: oklch(85% 0.15 210);
  --color-brand-text-light: white;
  --color-brand-text-dark: oklch(40% 0.23 270);

  --color-brand: light-dark(var(--color-brand-light), var(--color-brand-dark));
  --color-brand-text: light-dark(var(--color-brand-text-light), var(--color-brand-text-dark));

  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;
}

button.primary {
  /* These automatically adapt to color scheme */
  background-color: var(--color-brand);
  color: var(--color-brand-text);
}

НЕОБЯЗАТЕЛЬНО.Доступен ряд системных цветов, которые также автоматически адаптируются к используемой цветовой схеме (и другим цветовым режимам, например принудительным цветам), таким как canvas, canvastext, accentcolor (проверьте поддержку), buttonborder и т. д. Обычно они слишком ограничены, чтобы быть полезными, за исключением очень специфических случаев, когда вам нужно точно соответствовать определенному пользовательскому интерфейсу браузера по умолчанию или в качестве резервных/стандартных вариантов.

НЕОБЯЗАТЕЛЬНО. Настройте пары цветов в соответствии с контекстом

Даже если вы переопределяете системные настройки по умолчанию, может быть полезно использовать запрос медиа prefers-color-scheme, чтобы задать разные пары цветов, которые учитывают цвета браузера и ОС вокруг страницы (или окружающей страницы, если страница используется в качестве iframe).

Например, используйте более тусклую светлую тему, если в системе выбрана тема dark, или более контрастную тёмную тему, если в системе выбрана тема light. Это позволит избежать визуального доминирования страницы над окружающим интерфейсом.

Тонкая настройка интерфейса браузера

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

Настройка цвета акцента

В некоторых элементах интерфейса браузера (например, в установленных флажках или ползунках) используется цвет акцента. По умолчанию используется цвет, заданный в настройках ОС, но вы можете использовать свойство accent-color, чтобы выбрать цвет, который лучше подходит для страницы, например фирменный цвет страницы.

html {
  accent-color: light-dark(var(--color-accent-light), var(--color-accent-dark));
}

Проблемы, о которых следует знать при использовании accent-color

Отрисовка accent-color в браузере может различаться. Для индикаторов, которые обычно отображаются на фоне цвета акцента, например флажков и переключателей, браузеры могут применять разные варианты оформления:

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

Цвета полосы прокрутки

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

:root {
  --color-scrollbar-track: light-dark(#eee, #222);
  --color-scrollbar-thumb: light-dark(#999, #666);
  scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
}

Проблемы, о которых следует знать при использовании scrollbar-color

  • НЕ анимируйте и не преобразовывайте scrollbar-color. Из-за ошибки в WebKit полоса прокрутки мерцает каждый раз, когда меняется scrollbar-color.
  • В macOS свойства scrollbar-color (стандартное) и ::-webkit-scrollbar (устаревшее) по умолчанию игнорируются, поскольку в этой ОС используются нативные полосы прокрутки. Чтобы macOS отображала пользовательские цвета, их НЕОБХОДИМО сочетать с scrollbar-width (например, thin или auto).
  • Даже если применить scrollbar-width, оверлейные полосы прокрутки в macOS по умолчанию отображают дорожку (желоб) как прозрачную. Если дизайн требует видимого цвета фона дорожки в macOS, вы ДОЛЖНЫ применить scrollbar-gutter: stable; к прокручиваемому контейнеру, но обратите внимание, что он появляется только после того, как пользователь наводит указатель мыши на полосу прокрутки.
  • Даже если задано свойство scrollbar-gutter: stable, на устройствах с macOS дорожка может быть прозрачной. Видимость ползунка не должна зависеть от цвета дорожки.

Дополнительные настройки

Большинство пользовательских интерфейсов браузеров предоставляют псевдоэлементы для полной настройки внешнего вида, например:

  • ::placeholder
  • ::spelling-error
  • ::grammar-error
  • ::selection
  • ::search-text
  • ::target-text
  • ::file-selector-button

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

4. Разные изображения для светлой и тёмной темы

Для изображений CSS также можно использовать light-dark(), чтобы задавать разные значения.

ВАЖНО! Оба значения в light-dark() должны быть одного типа. Нельзя использовать цвет и изображение одновременно. Если одно из двух значений должно быть изображением, используйте image(<color>), чтобы обернуть значение цвета.

Определение темы на основе JS

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

Любой JS, считывающий matchMedia("(prefers-color-scheme: dark)").matches, ДОЛЖЕН также обрабатывать событие change, поскольку системные настройки могут измениться в любой момент.

Необязательно: добавление переключателя цветовой схемы

Если вы хотите разрешить пользователям вручную переопределять системные настройки по умолчанию для этого сайта, вы можете реализовать переключатель тёмной темы. Большинству сайтов не нужен постоянный переключатель в интерфейсе. Обычно достаточно просто использовать системную цветовую схему по умолчанию, при необходимости с настройкой в отдельном интерфейсе настроек. Рекомендации по реализации переключателя тёмной темы (постоянного или нет) приведены в разделе Переключатели тёмной темы.

Необязательно: переопределения для отдельных компонентов

Вы можете переопределить глобальную тему для определенных элементов, задав для них color-scheme.

Дополнительную информацию и советы можно найти в разделе Светлые и темные темы для отдельных компонентов.

Известные проблемы

Проблемы, о которых следует знать при использовании цветовой схемы

  • Chrome и Firefox поддерживают color-scheme для элементов iframe: они отображают встроенные страницы в правильной цветовой схеме и корректируют запрос медиа prefers-color-scheme встроенной страницы, чтобы отразить color-scheme контекста встраивания. Safari этого не делает и разрешает prefers-color-scheme в системных настройках даже внутри iframe.
    • Если вы управляете и родительским элементом, и элементом iframe, передайте цветовую схему родительского элемента в iframe явным образом – с помощью параметра URL (?theme=dark) во время создания iframe или с помощью postMessage() (что также позволяет реагировать на изменения во время выполнения). В iframe задайте класс для <html> (и/или color-scheme для :root) на основе этого сигнала, а не prefers-color-scheme.
    • Если вы управляете только встроенной страницей, то в Safari нет надежного способа определить color-scheme контекста встраивания из iframe. Добавьте в API для встраивания явный параметр темы (например, строку запроса или протокол postMessage) и опишите его в документации для разработчиков.

Резервные стратегии

Резервные варианты и поддержка свойства color-scheme в браузерах

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

Чтобы адаптировать сайт к предпочтениям пользователя в старых браузерах, используйте медиазапросы prefers-color-scheme, чтобы задать другие цвета для тёмной темы.

  • Используйте запрос медиа, чтобы переключать дополнительные параметры в :root или html.
  • Не используйте запрос медиа для отдельных компонентов, если только для них не требуется особый тип настройки темной темы, не связанный с цветами.
:root {
  /* Define brand colors for each mode */
  --color-brand-light: #0056b3;
  --color-brand-dark: #00e5ff;
  --color-brand: var(--color-brand-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --color-brand: var(--color-brand-dark);
  }

  /* Ignored in older browsers */
  color-scheme: light dark;
}

button.primary {
    background-color: var(--color-brand);
}

Резервные варианты и поддержка light-dark() в браузерах

Для браузеров, которые поддерживают color-scheme, но пока не поддерживают light-dark(), светлые и темные версии цветов сначала должны быть определены как настраиваемые свойства, а запрос медиа prefers-color-scheme должен использоваться для установки цветов для соответствующего режима, как в примере ниже:

:root {
  /* Define browser UI accent color for each mode */
  --brand-accent-light: #0056b3;
  --brand-accent-dark: #00e5ff;
  --accent-color: var(--brand-accent-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --accent-color: var(--brand-accent-dark);
  }

  /* OPTIONAL: use light-dark() for more control of built-in UI colors */
  @supports (color: light-dark(white, black)) {
    --accent-color: light-dark(var(--brand-accent-light), var(--brand-accent-dark));
  }

  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;

  /* Example inherited color property */
  accent-color: var(--accent-color);
}

pre, code {
  color-scheme: dark;

  /* **Mandatory**: any inherited color properties must be set again, even if to the same design tokens */
  accent-color: var(--accent-color);
}

Резервные варианты и поддержка значений light-dark() для изображений в браузерах

Для браузеров, которые не поддерживают light-dark() для изображений, используйте image-set() в качестве переключателя: присвойте каждому варианту type() из дополнительного параметра и задайте для неактивного варианта неподдерживаемый MIME-тип, чтобы браузер его игнорировал. Активный вариант получает пустое значение, поэтому он является простым вариантом image-set() без type().

Настройте переключатель на устройстве :root. На каждом сайте, где они используются, нужно будет только встроить оба изображения, например light-dark().

:root {
  --light-type: ;
  --dark-type: type("image/do-not-use");

  /* MANDATORY: Fallback for browsers without light-dark() image support */
  @media (prefers-color-scheme: dark) {
    --light-type: type("image/do-not-use");
    --dark-type: ;
  }
}

.hero {
  background-image: image-set(url(hero-light.png) var(--light-type), url(hero-dark.png) var(--dark-type));

  @supports (background-image: light-dark(url("a"), url("b"))) {
    background-image: light-dark(url(hero-light.png), url(hero-dark.png));
  }
}
  • Будет скачано только выбранное изображение.
  • Разрешения, которые по-прежнему работают: image-set(url(a-1x.png) 1x var(--light-type), url(a-2x.png) 2x var(--light-type), url(b-1x.png) 1x var(--dark-type), url(b-2x.png) 2x var(--dark-type)).
  • НЕ используйте type("") для активного варианта. Браузеры считают пустую строку неподдерживаемой и удаляют все варианты.
  • В отличие от light-dark(), переключатель не следует за color-scheme, заданным в поддереве. Ручной переключатель или любое поддерево, которое задает color-scheme, ДОЛЖНО также переключать --light-type и --dark-type.

Альтернативный вариант: один дополнительный параметр на изображение

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

:root {
  --hero-image: url(hero-light.png);

  @media (prefers-color-scheme: dark) {
    --hero-image: url(hero-dark.png);
  }

  @supports (background-image: light-dark(url("a"), url("b"))) {
    --hero-image: light-dark(url(hero-light.png), url(hero-dark.png));
  }
}

.hero {
  background-image: var(--hero-image);
}

Резервные объявления и поддержка функции image() в браузерах

Используйте градиент с одной контрольной точкой и без позиций, например linear-gradient(blue).

Чтобы обеспечить поддержку в большем количестве браузеров, вы можете явно указать 0 100% позиции для одной контрольной точки градиента, например linear-gradient(blue 0 100%).

Резервные варианты и поддержка свойства scrollbar-color в браузерах

Эта функция представляет собой прогрессивное улучшение и не всегда требует резервных объявлений.

Если стиль важен, а целевой набор Baseline пользователя – Baseline Widely Available или более ранний, вам СЛЕДУЕТ включить нестандартные псевдоэлементы ::-webkit-scrollbar в качестве резервных.

Чтобы избежать конфликтов между стандартными свойствами и устаревшими селекторами WebKit в браузерах, которые поддерживают и то, и другое, поместите резервные объявления в блок @supports not (scrollbar-color: auto).

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

/* Legacy fallback for WebKit/Blink browsers */
@supports not (scrollbar-color: auto) {
  .scroller::-webkit-scrollbar {
    /* Must define base size in WebKit for custom colors to be visual */
    width: 12px;
    height: 12px;
  }

  .scroller::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
  }

  .scroller::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
  }
}

Резервные варианты и поддержка свойства accent-color в браузерах

Свойство accent-color – это прогрессивное улучшение. Браузеры, которые не поддерживают это свойство, будут игнорировать его и использовать цвета интерфейса по умолчанию.