Тёмная тема для сайтов на MODX

Всем привет!

Сейчас становится всё более актуально создавать сайты не только адаптивными под разные устройства, но также и с акцентом на использование в разное время суток — то есть с возможностью переключения светлой и тёмной темы.

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

Порывшись некоторое время в интернете, я нашел несколько статей на данную тему, но изучив их повнимательнее, стало понятно, что они или упрощены (не решали в полной мере поставленной задачи), или местами написаны с ошибками. Поэтому было решено сделать свой вариант.

Для корректной работы сначала нам понадобится создать стили CSS, которые будут учитывать системную тему пользователя сайта. Ближе к началу CSS необходимо прописать следующие стили:

html{
    --bg-color: #fff;
    --text-color: #333;
}
@media (prefers-color-scheme: dark) {
    html{
        --bg-color: #333;
        --text-color: #E9EAEC;
    }
}

Мы используем CSS-переменные, которые в дальнейшем вы проставляете для ваших элементов так:

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

При заходе на сайт браузер будет проверять системную цветовую схему и менять цвет фона и текста в соответствии с системной темой пользователя.

Всё это прекрасно, но нам необходимо учесть сценарий, когда пользователь захочет сменить тему вручную, а также сохранить сделанный им выбор. Сперва мы преобразуем наш CSS:

html{
    --bg-color: #fff;
    --text-color: #333;
}
html.dark{
    --bg-color: #333;
    --text-color: #E9EAEC;
}
@media (prefers-color-scheme: dark) {
    html{
        --bg-color: #333;
        --text-color: #E9EAEC;
    }
    html.light{
        --bg-color: #fff;
        --text-color: #333;
    }
}

Теперь нам необходимо создать небольшой скрипт на ванильном JS, можете сохранить его в отдельный файл и подключить внизу шаблона.

<script>
const el = document.documentElement;
const btn = document.getElementById("theme-toggle");
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)");
const userTheme = systemTheme.matches ? "dark" : "light";
const toggleClass = userTheme === "dark" ? "light" : "dark";

// Читаем куки
function getCookie(name) {
	let matches = document.cookie.match(new RegExp(
		"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
	));
	return matches ? decodeURIComponent(matches[1]) : undefined;
}
let userCurrentTheme = getCookie('theme');

// Отслеживаем переключение системной темы пользователем и установим чекбокс без перезагрузки страницы
const setThemeToggle = e => {
  if (e.matches && !userCurrentTheme) {
	btn.checked = true;
  } else if (!userCurrentTheme) {
	btn.checked = false;
  }
}
// setThemeToggle(systemTheme);
systemTheme.addEventListener('change', setThemeToggle);

// Если пользователь переключил тёмную тему вручную или включена тёмная системная тема
// установим чекбокс на переключатель
if (systemTheme.matches == true && !userCurrentTheme || userCurrentTheme === "dark")
	btn.checked = true;

// Чтобы переключатель визуально не дергался при загрузке страницы, отображаем его после выполнения скрипта
document.querySelector(".theme-toggle-wrapper label").style.visibility="visible";

// Отслеживаем переключение темы пользователем
btn.addEventListener("click", function() {
	el.classList.toggle(toggleClass);
	
	if (userCurrentTheme === "light" && el.classList.contains("light"))
		el.classList.remove("light");
  
	// Текущая тема, которую выбрал пользователь
	userCurrentTheme = el.classList.contains(toggleClass) ? toggleClass : userTheme;
	// Запишем в куки выбор пользователя
	document.cookie = "theme=" + userCurrentTheme + ";path=/;";
});
</script>

Теперь нам понадобится добавить переключатель на страницу. Я использовал красивый переключатель, который в основе работает через input type=checkbox, типа такого: ссылка на Codepen

В шаблоне наш переключатель может выглядеть так:

<div class="theme-toggle-wrapper">
<input type="checkbox" id="theme-toggle" style="display: none;"/>
<label for="theme-toggle" style="visibility: hidden;">
...
</label>
</div>

Небольшое пояснение насчёт скрытия/отображения переключателя в строке:

document.querySelector(".theme-toggle-wrapper label").style.visibility="visible";

Можно использовать в шаблоне конструкцию

<input type="checkbox" id="theme-toggle" [[!#COOKIE.theme:is=`dark`:then=`checked`]]/>

Но это не решает проблему «скачков» переключателя, когда у пользователя выбрана тёмная системная тема, а вручную он не делал выбор. При загрузке страницы на долю секунды будет отображаться первоначальное положение переключателя (как в светлой теме), а потом произойдет переключение в «тёмный режим». Именно для этого мы скрываем label через style=visibility: hidden;
Я указал необходимые стили прямо в тегах, но их лучше перенести в CSS. На самом деле не обязательно использовать input type=checkbox, вы же можете использовать любой html-тег или какой-то другой анимированный элемент, нужно будет просто немного отредактировать JS-скрипт в соответствии с вашим выбором.

Теперь, когда наш скрипт будет переключать тему по желанию пользователя, и сохранять его выбор в куки, нам необходимо в шаблоне в тег html везде добавить class="[[!#COOKIE.theme]]" (обязательно некешируемый!):

<html class="[[!#COOKIE.theme]]">

Если в куки записан выбор пользователя — мы сразу проставим соответствующий класс при загрузке страницы и тогда браузер будет учитывать выбор пользователя на сайте, а не системную тему устройства. Обратите внимание! У вас должен быть установлен pdoTools, чтобы работала глобальная переменная [[!#COOKIE.theme]].

Крайне не рекомендую использовать контрастные цвета типа чёрного #000 и белого #fff в тёмных темах. Не выжигайте дисплей и глаза пользователя, лучше использовать мягкие цвета (тёмные оттенки серого для фона и светло серый текст вместо белого). На данную тему есть много информации в интернете и много вариантов цветов и оттенков.

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

Спасибо.
Дмитрий
10 марта 2024, 21:31
modx.pro
3
893
+4

Комментарии: 4

Василий Наумкин
13 марта 2024, 10:34
0
Стоит еще добавить, что если сайт использует современнный CSS фреймворк, то он уже может поддерживать тёмный режим из коробки.

Например, вот как это работает в Bootstrap 5.3.
Дмитрий
15 марта 2024, 01:32
+1
Это, конечно, очевидно. Статья рассчитана на тех, кто не пользуется указанными фреймворками.
    Олег Захаров
    15 марта 2024, 02:33
    0
    Спасибо! Как раз что-то подобное нужно было сделать и Вы прям вовремя.
    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
    4