Переключатель тёмной темы
Будучи большим поклонником «тёмных тем», или, как их ещё называют, «тёмного режима», я реализовал его почти во всех приложениях и сайтах, которые создавал для себя — начиная с этого блога и заканчивая моим последним проектом, It Changed.
Меня не устраивали решения, которые я находил в интернете, потому что они не сочетали в себе переключатель и медиазапросы. Почему это важно? Прежде всего, у пользователей могут быть заранее заданные предпочтения. Например, в macOS, если выбрать System Settings >> Appearance >> Dark, при посещении сайта браузер передаст (prefers-color-scheme: dark). Однако бывают и ситуации, когда пользователь хочет переключить тему на светлую именно для конкретного сайта.
Чтобы обеспечить лучший UX, это обязательно должно быть сочетанием решений — и, как вы увидите, реализовать это не так уж сложно.
Итак, приступим.
Реализация
Для начала нам нужен какой-то переключатель. Мне очень нравится использовать чекбоксы для целей отладки. Вот простой пример:
<label type="button">
<input type="checkbox" name="theme-switch" class="theme-switch"/>
<span>switch</span>
</label>
Теперь перейдём к части с JavaScript:
const themeSwitches = document.querySelectorAll('.theme-switch');
if (themeSwitches.length > 0) {
themeSwitches.forEach((themeSwitch, i) => {
if (localStorage.getItem('dark-mode') === 'true') {
themeSwitch.checked = true;
} else {
themeSwitch.checked = false;
}
themeSwitch.addEventListener('change', () => {
const { checked } = themeSwitch;
themeSwitches.forEach((el, n) => {
if (n !== i) {
el.checked = checked;
}
});
if (themeSwitch.checked) {
document.documentElement.classList.add('dark');
localStorage.setItem('dark-mode', true);
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('dark-mode', false);
}
});
});
}
Этот код обрабатывает всю логику переключателя и сохраняет его состояние в localStorage. Поскольку селектор доступен только после загрузки страницы, этому коду не нужно выполняться немедленно. Хорошей практикой будет разместить его в вашем JavaScript-файле для отложенного выполнения.
Однако есть часть кода, которая действительно должна выполняться как можно раньше. Вот этот фрагмент:
<script>
if (localStorage.getItem('dark-mode') === 'true' || (!('dark-mode' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.querySelector('html').classList.add('dark');
} else {
document.querySelector('html').classList.remove('dark');
}
</script>
Что делает этот код:
-
Проверяет, установлен ли тёмный режим в
localStorage; -
Если в
localStorageнет настройки тёмного режима, проверяет, предпочитает ли пользователь тёмный режим.
Что это гарантирует:
- Пользователи получают тёмный режим, если включили его вручную во время предыдущих посещений.
- Пользователи получают тёмный режим, если их системные настройки указывают на то, что они его предпочитают.
Дизайн
Поскольку мы реализовали переключатель в виде чекбокса, это открывает широкий простор для дизайна. Он может работать как тумблер, оставаться чекбоксом или превратиться в кнопку — варианты практически безграничны. Из-за этой гибкости я не буду предлагать здесь единое дизайнерское решение. Выберите то, что лучше всего подходит именно вашему случаю.
Ссылки
Вот несколько полезных ссылок:
- Я опубликовал этот код в виде Composer-пакета на Packagist.
- Если хотите внести свой вклад, загляните в проект на GitHub.
На этом всё, друзья :)