Тёмная тема
без мигания
Почему тёмная тема часто на секунду мигает белым при загрузке — и как это исправить раз и навсегда.
Почему тёмная тема часто на секунду мигает белым при загрузке — и как это исправить раз и навсегда.
Браузер не ждёт ваш JS, чтобы начать рисовать страницу
Браузер парсит HTML и начинает применять CSS сразу, как только получает файлы — не дожидаясь выполнения JavaScript. Если тема выбирается и применяется скриптом, который подключён обычным способом (в конце body или через defer), пользователь на долю секунды увидит светлую версию страницы, а затем она резко перекрасится в тёмную.
На быстром интернете это почти незаметно. На медленном — раздражающая вспышка, которая портит первое впечатление от сайта.
Пять строк в самом начале <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-ссылок, часть мигания всё равно останется.
Один набор цветов, который переопределяется целиком
Проще всего управлять темой через 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); }Если пользователь ещё не выбирал тему вручную
Если в localStorage ничего не сохранено, разумно ориентироваться на системные настройки ОС через медиа-запрос prefers-color-scheme: dark — это можно сделать прямо в том же anti-FOUC скрипте, добавив проверку перед тем, как решить показывать светлую тему по умолчанию.
Короткие ответы на главное
Браузер начинает рисовать страницу с CSS по умолчанию (светлым), пока JS ещё не выполнился и не применил сохранённую тему — это и создаёт вспышку.
Частично — через prefers-color-scheme для системной темы. Но для темы, выбранной пользователем вручную и сохранённой в localStorage, без раннего JS не обойтись.
Нет, это буквально несколько строк синхронного кода, которые выполняются за доли миллисекунды — незаметно на фоне остальной загрузки.
Настроим переключатель темы, который не мигает и запоминает выбор пользователя.