Темна тема без блимання: перемикач, який запам'ятовує вибір

Кнопка темної теми, що не спалахує білим під час перезавантаження: короткий скрипт у head, light-dark() і color-scheme. Код цілком, знімки на телефоні, планшеті й комп'ютері та перевірка, що спалаху немає.
Ви ввімкнули на сайті темну тему, а під час кожного перезавантаження сторінка на мить спалахує білим і лише потім темніє. Це найчастіша помилка перемикача теми, і лікується вона одним маленьким скриптом у потрібному місці. Нижче — рецепт цілком: код, який можна скопіювати, пояснення, як він працює, і спосіб перевірити результат самому.
Що вийде #
- Тема за замовчуванням збігається з темою системи (світлою чи темною) і працює навіть за вимкненого JavaScript.
- Кнопка перемикає тему, а вибір запам'ятовується в браузері.
- Збережений вибір застосовується до першого малювання: під час перезавантаження немає білого спалаху.



Чому сторінка блимає #
Браузер малює сторінку в міру читання HTML. Якщо вибір користувача застосовує скрипт у кінці сторінки або після завантаження, браузер встигає намалювати світлий варіант, а потім перефарбовує його. Потрібно, щоб вибір був відомий раніше, ніж з'явиться перший піксель, тобто скрипт має стояти на самому початку сторінки, уhead.Крок 1. Оголосіть, що сайт уміє обидві теми #
Покладіть уhead рядок meta і одразу за ним крихітний скрипт. Він читає збережений вибір і, якщо він є, записує його в атрибут data-theme кореневого елемента:
<meta name="color-scheme" content="light dark">
<script>
(function () {
var saved = null;
try { saved = localStorage.getItem('theme'); } catch (e) {}
if (saved === 'light' || saved === 'dark') {
document.documentElement.dataset.theme = saved;
}
})();
</script>try потрібен тому, що в приватному режимі або за забороненого сховища браузер кидає помилку, і без нього вся сторінка лишилася б без теми.Крок 2. Опишіть кольори один раз #
Кольори кожної теми задаються парою значень у функціїlight-dark(): перше для світлої теми, друге для темної. Потрібне обирає властивість color-scheme:
:root {
color-scheme: light dark;
--bg: light-dark(#ffffff, #14161a);
--text: light-dark(#1b1f24, #e6e8eb);
--card: light-dark(#f3f5f8, #1e2228);
--line: light-dark(#d8dee6, #2c333c);
--accent: light-dark(#1a56db, #7aa7ff);
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
body { background: var(--bg); color: var(--text); }data-theme немає, працює color-scheme: light dark: сторінка бере тему системи без жодного рядка JavaScript. Коли скрипт записав вибір користувача, два останні рядки примусово вмикають потрібну тему.Крок 3. Додайте кнопку #
<button id="theme-toggle" type="button" aria-pressed="false"></button>(function () {
var root = document.documentElement;
var button = document.getElementById('theme-toggle');
var names = { dark: 'Темна', light: 'Світла' };
function current() {
return root.dataset.theme ||
(matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
}
function show() {
var now = current();
button.textContent = names[now];
button.setAttribute('aria-pressed', now === 'dark');
}
button.addEventListener('click', function () {
var next = current() === 'dark' ? 'light' : 'dark';
root.dataset.theme = next;
try { localStorage.setItem('theme', next); } catch (e) {}
show();
});
show();
})();Перевірте самі #
Ми перевірили рецепт у браузері на сторінці, де перед кінцем стоїть повільний скрипт (дві секунди), а в сховищі записано вибір «темна» за світлої теми системи. Через 0,8 секунди після початку завантаження: зі скриптом уhead сторінка вже темна, зі скриптом у кінці — біла, і так лишається, доки сторінку не дочитано. Ось що бачить людина в цей момент:

Решту теж перевірено: без JavaScript сторінка бере тему системи, після натискання кнопки й перезавантаження вибір лишається, на 390, 768 і 1280 точок немає бічного скролу.
Повторити можна так:
- Відкрийте сторінку, натисніть кнопку й оберіть темну тему.
- Увімкніть в інструментах розробника уповільнення мережі й перезавантажте сторінку: тло лишається темним із першого кадру.
- Вимкніть JavaScript і очистьте сховище: сторінка знову йде за темою системи.
Типові помилки #
- Скрипт у кінці сторінки. Спалах повертається: вибір відомий надто пізно.
- Немає
color-schemeна:root. Функціяlight-dark()без нього не знає, яке значення взяти. - Немає
tryнавколоlocalStorage. У приватному режимі в частини браузерів сторінка ламається. - Тема лише в JavaScript. Без скрипта відвідувач лишається без темної теми, навіть коли в системі вона ввімкнена. У рецепті це вирішено через
color-scheme. - Не перевірено контраст. Текст і тло мають давати не менше 4,5 до 1, це правило доступності WCAG AA. Перевірте обидві теми, а не одну.
Пока никто не высказался. Будьте первым.