Краткое изложение различных подходов к стилизации веб-страницы в соответствии с системной темой пользователя с использованием CSS, HTML и собственного javascript.
Подход 1: Просто CSS — «предпочитает цветовую схему»
Классический метод: определить предпочтение темы пользователя с помощью функции мультимедиа CSS prefers-color-scheme
.
Хотя для каждой темы можно указать совершенно другой набор стилей; использование переменных CSS позволяет меньше повторяться.
/* file: style.css */ /* default: light theme colours */ :root { --bg-colour: #fafafa; /* white */ --font-colour: #212529; /* dark grey*/ --primary-colour: #2196f3; /* blue */ --link-colour: #2962ff; /* blue */ --alt-bg-colour: #fff; /* white */ } /* if system theme is dark */ @media(prefers-color-scheme:dark) { :root { --bg-colour: #000; /* black */ --font-colour: #fff; /* white */ --primary-colour: #90caf9; /* light blue */ --link-colour: #81d4fa; /* light blue */ --alt-bg-colour: #5a5a5a; /* grey */ } } /* main webpage styling */ body { background-colour: var(--bg-colour); color: var(--font-color); } /* ... rest of styling */
Подход 2: HTML и CSS — встраивание нескольких таблиц стилей
Атрибут media
позволяет нам указать тип носителя (в данном случае, светлая/темная системная тема), для которого предназначен целевой ресурс (файл таблицы стилей).
<!-- file: index.html --> <head> <!-- other elements in head--> <!-- stylesheet with dark theme css variables --> <link rel="stylesheet" media="(prefers-color-scheme:dark)" href="dark.css"> <!-- stylesheet with light theme css variables --> <link rel="stylesheet" media="(prefers-color-scheme:light)" href="light.css"> <!-- base stylesheet --> <link rel="stylesheet" href="style.css"> </head> <!-- rest of html --> /* file: light.css */ :root { --bg-colour: #fafafa; /* white */ --font-colour: #212529; /* dark grey*/ --primary-colour: #2196f3; /* blue */ --link-colour: #2962ff; /* blue */ --alt-bg-colour: #fff; /* white */ } /* file: dark.css */ :root { --bg-colour: #000; /* black */ --font-colour: #fff; /* white */ --primary-colour: #90caf9; /* light blue */ --link-colour: #81d4fa; /* light blue */ --alt-bg-colour: #5a5a5a; /* grey */ } /* file: style.css */ body { background-colour: var(--bg-colour); color: var(--font-color); } /* ... rest of styling */
Атрибут media определяет, какую таблицу стилей заполнить переменными CSS, которые будут использоваться в style.css
для стилизации веб-страницы. Для более подробного ознакомления с тем, как работает этот метод, и стратегией загрузки, У Томаса Штайнера есть хорошо написанная статья с более подробной информацией об этом методе.
Подход 3: JS — Изменение стиля/таблицы стилей
3.1 Изменение переменных CSS
Этот подход изменяет переменные CSS, чтобы они соответствовали системной теме пользователя, используя собственный javascript.
<!-- file: index.html --> <head> <!-- other elements in head--> <!-- base stylesheet --> <link rel="stylesheet" href="style.css"> <!-- script --> <script src="script.js"></script> </head> <!-- rest of html --> /* file: style.css */ /* set light theme colours as default */ :root { --bg-colour: #fafafa; --font-colour: #212529; --primary-colour: #2196f3; --link-colour: #2962ff; --alt-contrast-colour: #fff; } /* ... rest of styling */
Чтобы определить, использует ли система пользователя темную тему в javascript, метод заключается в использовании window.matchMedia("(prefers-color-scheme:dark)").matches
, который проверяет, соответствует ли document
(веб-страница) строке медиа-запроса (prefers-color-scheme:dark
— является ли системная тема пользователя темной).
// file: script.js // light theme colours const lightTheme = { "--bg-colour": "#fafafa", "--font-colour": "#212529", "--primary-colour": "#2196f3", "--link-colour": "#2962ff", "--alt-contrast-colour": "#fff" }; // dark theme colours const darkTheme = { "--bg-colour": "#000", "--font-colour": "#fff", "--primary-colour": "#90caf9", "--link-colour": "#81d4fa", "--alt-contrast-colour": "#5a5a5a" }; // root element to be used later to change CSS variables' values let root = document.documentElement; /** * Changes the webpage's colours based on colours object passed in * * @param {object} themeColours object with the CSS variables and their corresponding colour values */ function setTheme(themeColours) { console.log("setting theme: " + themeColours); for (var name in themeColours) { root.style.setProperty(name, themeColours[name]); }; }; // waits for DOM to finish loading document.addEventListener("DOMContentLoaded", function(event) { // initial determination of user's system theme var isDarkTheme = (window.matchMedia("(prefers-color-scheme: dark)")); (isDarkTheme.matches)?setTheme(darkTheme):setTheme(lightTheme); // when user changes system theme, also change webpage colours isDarkTheme.addEventListener('change', (event) => { (isDarkTheme.matches)?setTheme(darkTheme):setTheme(lightTheme); }); });
3.2 Изменение пути к таблице стилей
Таблицы стилей для этого подхода (light.css
, dark.css
, style.css
) такие же, как уже упоминалось для подхода 2.
<!-- file: index.html --> <head> <!-- other elements in head--> <!-- default: light theme css variables --> <link rel="stylesheet" href="light.css" id="theme-colours"> <!-- base stylesheet --> <link rel="stylesheet" href="style.css"> <!-- script --> <script src="script.js"></script> </head> <!-- rest of html --> // file: script.js // theme colours const lightThemeStylesheet = "light.css"; const darkThemeStylesheet = "dark.css"; // gets element for the stylesheet that sets theme colours var themeLink = document.getElementById("theme-colours"); // waits for DOM to finish loading document.addEventListener("DOMContentLoaded", function(event) { // initial determination and setting of user's system theme var isDarkTheme = (window.matchMedia("(prefers-color-scheme: dark)")); // sets themeLink themeLink.href = isDarkTheme.matches ? darkThemeStylesheet : lightThemeStylesheet; // when user changes system theme, also change webpage's stylesheet isDarkTheme.addEventListener('change', (event) => { themeLink.href = isDarkTheme.matches ? darkThemeStylesheet : lightThemeStylesheet; }); });
3.3 Изменение атрибута данных `data-theme`
Этот подход использует атрибуты данных, которые позволяют нам хранить дополнительную информацию в HTML-коде веб-страницы. В этом случае javascript обнаруживает и сохраняет системную тему пользователя в data-theme
.
<!-- file: index.html --> <html lang="en"> <head> <!-- other elements in head--> <!-- base stylesheet --> <link rel="stylesheet" href="style.css"> <!-- script --> <script src="script.js"></script> </head> <!-- rest of html --> </html>
В index.html
у <html>
нет атрибута data-theme
, это не ошибка — по умолчанию для переменных CSS root
установлена светлая тема в style.css
(так что есть стиль по умолчанию, к которому можно вернуться), а атрибут установлен в script.js
.
/* style.css */ /* default: light theme colours */ :root { --bg-colour: #fafafa; --font-colour: #212529; --primary-colour: #2196f3; --link-colour: #2962ff; --alt-contrast-colour: #fff; } /* dark theme colours */ html[data-theme="dark"] { --bg-colour: #000; --font-colour: #fff; --primary-colour: #90caf9; --link-colour: #81d4fa; --alt-contrast-colour: #5a5a5a; } /* ... rest of styling */ // style.js // get html element const htmlEl = document.querySelector("html"); // waits for DOM to finish loading document.addEventListener("DOMContentLoaded", function(event) { // initial determination and setting of user's system theme (data-theme) var isDarkTheme = (window.matchMedia("(prefers-color-scheme: dark)")); htmlEl.dataset.theme = isDarkTheme.matches ? "dark" : "light"; // when user changes system theme, also change webpage's stylesheet isDarkTheme.addEventListener('change', (event) => { htmlEl.dataset.theme = isDarkTheme.matches ? "dark" : "light"; }); });
Дополнительно: разрешение пользователям переопределять тему системы по умолчанию
Предоставление пользователям выбора! Также рекомендуется сохранить выбранную пользователем предпочтительную тему (с localstorage
), чтобы в следующий раз, когда он вернется, ему не нужно было снова устанавливать предпочитаемую тему.
Для этого я добавил переключатель темы (оригинальный код от aliceyt) в правом верхнем углу веб-страницы. Переключатель автоматически устанавливает системную тему пользователя, если пользователь ранее не установил предпочтительную тему.
Также были добавлены некоторые шаги, чтобы изменить цвет панели навигации, изменив ее класс начальной загрузки.
Ссылка на полный код: изменен подход 3.2 (тот же HTML) с дополнительным CSS для переключателя темы.
// file: script.js // waits for DOM to finish loading document.addEventListener("DOMContentLoaded", function(event) { // theme colours const lightThemeStylesheet = "light.css"; const darkThemeStylesheet = "dark.css"; // gets element for the stylesheet that sets theme colours var themeLink = document.getElementById("theme-colours"); // element for theme switch toggle var themeSwitch = document.getElementById("theme-switch"); // element for navbar var navbar = document.getElementById("navbar"); /** * Changes the webpage's colours, theme switch and navbar colour * * @param {string} "light" or "dark" */ function setTheme(theme) { if (theme=="dark") { localStorage.setItem("theme", "dark"); themeLink.href = darkThemeStylesheet; themeSwitch.checked = true; // removes bootstrap light navbar classes and add the dark navbar classes navbar.classList.remove("bg-light", "navbar-light"); navbar.classList.add("bg-dark", "navbar-dark"); } else { localStorage.setItem("theme", "light"); themeLink.href = lightThemeStylesheet; themeSwitch.checked = false; // removes bootstrap dark navbar classes and add the light navbar classes navbar.classList.remove("bg-dark", "navbar-dark"); navbar.classList.add("bg-light", "navbar-light"); } }; // get element of the theme switch toggle var themeSwitch = document.getElementById("theme-switch"); var selectedTheme = localStorage.getItem("theme"); if (!selectedTheme) { // if no theme in local storage, set to system default (window.matchMedia("(prefers-color-scheme: dark)")).matches ? setTheme("dark") : setTheme("light"); } else { setTheme(selectedTheme); } // when user toggles theme switch, change the theme themeSwitch.addEventListener('change', (event) => { (themeSwitch.checked) ? setTheme("dark") : setTheme("light"); }); });
Этот подход можно расширить, чтобы использовать раскрывающийся список/список параметров и предоставить пользователям больше вариантов выбора тем (высокая контрастность и т. д.) — например. добавление таблицы стилей high-contrast.css
и установка ее в качестве выбранной таблицы стилей.
Дополнительные соображения
- Убедитесь, что цвета достаточно контрастны, чтобы текст и интерактивные компоненты, такие как кнопки, выделялись на фоне и оставались видимыми.
- Было бы неплохо также сопоставить изображения с темой. Например. использование более темных изображений, когда тема темная, использование темного фона на изображениях графиков
- Для брендинга рассмотрите возможность использования логотипов, которые либо работают на обе темы, либо имеют вариации, которые можно настроить для разных тем.