Урок 38. Теория: prefers-* media: доступность и тема

📁 Раздел: Адаптивная вёрстка ⏱️ Время изучения: ~80 мин 🎯 Сложность: Средняя
#prefers-color-scheme #prefers-reduced-motion #prefers-contrast #prefers-reduced-data #color-scheme

⚡ Кратко

Медиа-запросы prefers-* читают настройки пользователя (заданные в системе или браузере), а не размер экрана. Сайт подстраивается под человека без единой строчки JavaScript.

  • prefers-color-scheme: dark — пользователь выбрал тёмную тему ОС.
  • prefers-reduced-motion: reduce — просьба убрать анимации (вестибулярные расстройства, укачивание).
  • prefers-contrast: more — нужен повышенный контраст текста и границ.
  • color-scheme: light dark — подсказка браузеру: красить скроллбары и поля ввода под обе темы.

Главная идея

Обычный медиа-запрос (min-width) спрашивает у браузера: «какого размера экран?». Запросы prefers-* спрашивают другое: «что предпочитает сам человек?». Эти настройки пользователь задаёт один раз в операционной системе — тёмную тему, уменьшение анимаций, высокий контраст — а каждый сайт может их прочитать и подстроиться. Это и есть доступность на уровне CSS: не навязывать, а уважать выбор пользователя.

Представь умный дом. Жилец один раз настроил под себя: вечером — приглушённый свет (тёмная тема), он плохо переносит мигание — значит без резких световых эффектов (reduced motion), а у пожилого родственника слабое зрение — нужен максимально чёткий, контрастный свет (more contrast). Сайт с prefers-* ведёт себя как такой умный дом: он спрашивает у системы настройки жильца и подстраивается под него, а не заставляет каждого подгонять себя под один общий режим.

Светлая и тёмная тема рядом

Одна и та же карточка, два набора цветов. CSS выбирает набор по системной настройке — пользователю не нужно ничего нажимать:

Кнопка Светлая тема Кнопка Тёмная тема
Одна разметка — две палитры. Переключение управляется системой, а не кодом страницы.

Палитры удобно держать в CSS-переменных и переопределять их внутри медиа-запроса. Цветовые пары «светлая → тёмная»:

фон · #ffffff
фон · #111827
текст · #172033
текст · #f8fafc
акцент · #2458d3
акцент · #93c5fd
Почему акцент светлеет в тёмной теме: насыщенный синий #2458d3 на тёмном фоне «горит» и плохо читается. На тёмном нужен светлее и спокойнее — #93c5fd. Контраст подбирают для каждой темы отдельно.

Как это работает: reduced-motion и contrast

prefers-reduced-motion: reduce — это не каприз, а медицинская необходимость для людей с вестибулярными расстройствами: параллакс, резкие зумы и автопрокрутка могут вызвать головокружение и тошноту. Правильный подход — не вырезать функциональность, а обезвредить движение: свести длительность анимаций и переходов к мгновенным. Интерфейс остаётся тем же, просто без укачивания.

prefers-contrast: more усиливает читаемость для слабовидящих: добавляют видимые границы, делают цвета насыщеннее, убирают полупрозрачность. Здесь нельзя «на глаз» — контраст текста к фону проверяют по WCAG (минимум 4.5:1 для обычного текста).

Когда применять

  • Всегда объявляйте color-scheme на :root — иначе скроллбары и системные поля останутся светлыми в тёмной теме.
  • Отключайте не все эффекты, а только движение, которое может мешать.
  • Контраст проверяйте на реальных цветах, особенно у ссылок, кнопок и карточек.
Частая ошибка: переопределить фон в тёмной теме, но забыть про цвет текста. Тёмный текст на тёмном фоне исчезает. Меняйте пару «фон + текст» вместе.

Базовый CSS

📄 lesson-38/styles.css
:root {
  color-scheme: light dark;
  --bg: #ffffff;
  --text: #172033;
  --accent: #2458d3;
  --space-section: clamp(3rem, 8vw, 7rem);
}

body {
  background: var(--bg);
  color: var(--text);
}

.hero {
  display: grid;
  gap: clamp(1rem, 3vw, 3rem);
  padding-block: var(--space-section);
}

@media (min-width: 64rem) {
  .hero {
    grid-template-columns: 1fr 1fr;
    align-items: center;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #111827;
    --text: #f8fafc;
    --accent: #93c5fd;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms;
    transition-duration: 0.01ms;
    scroll-behavior: auto;
  }
}

@media (prefers-contrast: more) {
  :root {
    --accent: #003cff;
  }

  .card {
    border: 2px solid currentColor;
  }
}

Навигация