$ sudo teach IT

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

После этого урока вы сможете определять предпочитаемую пользователем тему и переключать оформление сайта между светлым и тёмным режимами, меняя всего несколько переменных.

Что уже умеем / что добавим

Вы умеете собирать палитру в CSS-переменных, откликаться на системные настройки медиазапросами (как с prefers-reduced-motion) и применять современные эффекты. Теперь соединим переменные и медиазапрос предпочтения цвета в готовую тёмную тему. В сквозном проекте мы дадим сайту компании полноценный тёмный режим, который включается сам по настройке системы.

Что вы узнаете в рамках статьи

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

Ключевые термины

  • prefers-color-scheme — медиа-функция, сообщающая предпочтение светлой или тёмной темы.
  • light / dark — значения предпочтения (светлая и тёмная тема).
  • Тема оформления — набор значений (обычно переменных), определяющих цвета интерфейса.
  • Переопределение переменных — смена значений переменных для другой темы.

Что такое prefers-color-scheme

prefers-color-scheme — это медиа-функция, которая сообщает, какое оформление предпочитает пользователь в настройках системы. Она принимает значения light (светлая тема) и dark (тёмная). Используют её в медиазапросе, как и знакомые запросы по ширине:

@media (prefers-color-scheme: dark) {
  body {
    background: #111;
    color: #eee;
  }
}

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

Почему тёмную тему строят на переменных

Если все цвета сайта заданы через переменные (а мы к этому пришли в уроке про custom properties), то для тёмной темы не нужно переписывать десятки правил — достаточно поменять значения переменных в одном месте. Компоненты продолжают использовать те же var(--bg), var(--text), но получают новые значения:

:root {
  --bg: #ffffff;
  --text: #222222;
  --card: #f5f7fa;
}

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

Все компоненты ссылаются на переменные, а не на конкретные цвета. Это ключ к теме: сама разметка и правила не знают, светлая сейчас тема или тёмная, — они просто берут «текущий цвет фона» и «текущий цвет текста». Чтобы переключить тему, мы меняем не компоненты, а лишь значения переменных. Именно поэтому урок про переменные был фундаментом, к которому мы шли весь раздел.

Переопределяем палитру для тёмного режима

Теперь соединим переменные и prefers-color-scheme. В медиазапросе тёмной темы переопределяем те же переменные новыми, тёмными значениями:

:root {
  --bg: #ffffff;
  --text: #222222;
  --card: #f5f7fa;
  --accent: #2d6cdf;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14161a;
    --text: #e6e8eb;
    --card: #1e2228;
    --accent: #5b8def;
  }
}

Светлые значения заданы по умолчанию, а внутри @media (prefers-color-scheme: dark) те же переменные получают тёмные значения. Поскольку все компоненты используют var(--bg), var(--text) и так далее, весь сайт перекрашивается разом — без единой правки в самих правилах компонентов. Обратите внимание, что акцент в тёмной теме сделан чуть светлее (#5b8def): яркие цвета на тёмном фоне лучше слегка осветлять, чтобы сохранить контраст и читаемость.

Детали: тени, изображения, контраст

Хорошая тёмная тема — это не просто «инвертировать цвета». Есть нюансы, которые отличают аккуратную реализацию. Тени на тёмном фоне почти не видны, поэтому вместо них для выделения часто используют чуть более светлый фон карточки или тонкую рамку. Слишком чистый белый текст (#fff) на чёрном утомляет глаза — берут мягкий светло-серый (#e6e8eb). Контраст важно сохранять достаточным, чтобы текст читался:

@media (prefers-color-scheme: dark) {
  .card {
    border: 1px solid #2a2f37;
  }
}

В тёмной теме карточка выделяется тонкой рамкой вместо тени. Ещё одна деталь — изображения и логотипы: яркие картинки на тёмном фоне иногда стоит чуть приглушить фильтром, а логотип может понадобиться в светлом варианте. Эти мелочи не обязательны для старта, но именно они превращают «тёмный фон» в продуманную тёмную тему. Начните с переменных палитры, а детали добавляйте по мере необходимости.

Применяем в проекте

Дадим сайту компании полноценную тёмную тему. Мы уже держим палитру в переменных, поэтому добавим тёмные значения в медиазапросе prefers-color-scheme и подправим пару деталей — рамку карточек вместо теней.

:root {
  --bg: #ffffff;
  --text: #222222;
  --card: #f5f7fa;
  --accent: #2d6cdf;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14161a;
    --text: #e6e8eb;
    --card: #1e2228;
    --accent: #5b8def;
  }

  .card {
    border: 1px solid #2a2f37;
    box-shadow: none;
  }
}

.page { background: var(--bg); color: var(--text); }
.card { background: var(--card); }
.btn { background: var(--accent); color: #fff; }

Переключите системную тему на тёмную и обновите страницу: сайт компании сам стал тёмным — глубокий фон, мягкий светлый текст, слегка осветлённый акцент, а карточки выделяются аккуратной рамкой вместо теней. Вернёте светлую тему — сайт снова светлый. Мы не трогали ни один компонент: всё решило переопределение переменных. Это и есть красота архитектуры на переменных — тема меняется в одной точке.

Частые ошибки

  • Жёсткие цвета мимо переменных.
    Если где-то цвет задан напрямую, в тёмной теме он не сменится; все цвета должны идти через переменные.
  • Чистый белый текст на чёрном.
    #fff на #000 слишком резок; берите мягкий светло-серый текст на тёмно-сером фоне.
  • Забыть про контраст.
    Тёмная тема не должна терять читаемость; проверяйте, что текст хорошо виден на новом фоне.
  • Тени как в светлой теме.
    На тёмном фоне тени почти незаметны; для выделения используйте рамку или более светлый фон карточки.

Итоги

Поздравляем — вы завершили четвёртый курс и сделали свой сайт по-настоящему живым! Вы прошли впечатляющий путь: от псевдоклассов состояний и псевдоэлементов к плавным переходам, от первых @keyframes к управляемым, производительным и доступным анимациям с transform, а затем — к современным возможностям CSS: переменным, селектору :has(), функции calc(), эффектам глубины и, наконец, полноценной тёмной теме. Ваш сайт компании теперь откликается на действия, красиво движется, выглядит стильно и уважает предпочтения каждого пользователя — включая тёмный режим.

Вы выросли из мастера статичной вёрстки в создателя динамичных, современных интерфейсов — это большое достижение, и им стоит гордиться! Впереди пятый, заключительный курс линейки: мы поднимемся на уровень архитектуры — освоим инструменты сборки, CSS Modules, дизайн-токены и организацию большого многостраничного проекта, а в конце наведём мост к JavaScript. Но это следующая ступень — а сегодня отпразднуйте: вы оживили свой сайт и заслужили эту победу.

На следующий урок