Блог · Дизайн
17 октября 2026

Тёмная тема
без мигания

Почему тёмная тема часто на секунду мигает белым при загрузке — и как это исправить раз и навсегда.

Коротко
  • Мигание («flash of unstyled content», FOUC) возникает, потому что браузер рисует страницу до того, как JS применит сохранённую тему
  • Решение — крошечный синхронный скрипт в самом начале <head>, до загрузки любого CSS
  • Тема хранится в localStorage и применяется как атрибут data-theme на <html> ещё до первой отрисовки
  • Системную тему (по настройкам ОС) можно учитывать через prefers-color-scheme как запасной вариант
01 / Причина

Откуда берётся мигание

Браузер не ждёт ваш JS, чтобы начать рисовать страницу

Браузер парсит HTML и начинает применять CSS сразу, как только получает файлы — не дожидаясь выполнения JavaScript. Если тема выбирается и применяется скриптом, который подключён обычным способом (в конце body или через defer), пользователь на долю секунды увидит светлую версию страницы, а затем она резко перекрасится в тёмную.

На быстром интернете это почти незаметно. На медленном — раздражающая вспышка, которая портит первое впечатление от сайта.

02 / Решение

Anti-FOUC скрипт

Пять строк в самом начале <head>

Решение — вставить синхронный (без async/defer) скрипт первым делом в <head>, до подключения CSS. Он читает localStorage и сразу проставляет атрибут на <html> — браузер применит тёмные стили сразу при первой отрисовке, вспышки не будет.

<!-- первая строка внутри <head>, до CSS -->
<script>
(function(){
  try {
    var t = localStorage.getItem('theme');
    if (t === 'dark') document.documentElement.setAttribute('data-theme', 'dark');
  } catch(e) {}
})();
</script>

Важно: скрипт должен быть именно синхронным и именно в начале <head> — если поставить его ниже, после первых CSS-ссылок, часть мигания всё равно останется.

03 / CSS

CSS-переменные под тему

Один набор цветов, который переопределяется целиком

Проще всего управлять темой через CSS custom properties: базовые цвета объявляются в :root, а для тёмной темы — переопределяются в блоке с селектором [data-theme="dark"]. Так не нужно дублировать все стили дважды — меняются только значения переменных.

:root { --bg: #fff; --text: #191b1e; }
[data-theme="dark"] {
  --bg: #0f1012;
  --text: #eceef0;
}
body { background: var(--bg); color: var(--text); }
04 / Системная тема

Учитываем системную тему по умолчанию

Если пользователь ещё не выбирал тему вручную

Если в localStorage ничего не сохранено, разумно ориентироваться на системные настройки ОС через медиа-запрос prefers-color-scheme: dark — это можно сделать прямо в том же anti-FOUC скрипте, добавив проверку перед тем, как решить показывать светлую тему по умолчанию.

  • Пользователь с явно выбранной темой — всегда видит её, независимо от системных настроек
  • Пользователь без выбора — видит тему, соответствующую его системе
05 / FAQ

Частые вопросы

Короткие ответы на главное

Почему вообще возникает мигание светлой темой?

Браузер начинает рисовать страницу с CSS по умолчанию (светлым), пока JS ещё не выполнился и не применил сохранённую тему — это и создаёт вспышку.

Можно ли решить проблему только через CSS?

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

Замедляет ли anti-FOUC скрипт загрузку страницы?

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

Хотите тёмную тему без багов?

Настроим переключатель темы, который не мигает и запоминает выбор пользователя.