Переключение темы на сайте: простая реализация для веб-разработки
Для кого эта статья:
- Веб-разработчики, желающие улучшить свои навыки и внедрить функционал переключения тем на сайтах.
- Дизайнеры и UX-специалисты, интересующиеся современными тенденциями в веб-дизайне и пользовательском опыте.
Люди, изучающие веб-технологии и желающие научиться создавать интерактивные интерфейсы, включая реализации темного режима.
Переключение между светлой и темной темой на сайте уже не просто модный тренд, а необходимый элемент современного веб-дизайна! 🌓 Пользователи всё чаще ожидают возможности настроить внешний вид интерфейса под свои предпочтения — и темный режим становится одним из самых востребованных функционалов. Не имея такой опции, вы рискуете отпугнуть часть аудитории, особенно тех, кто проводит много времени за экраном в вечернее время. В этой статье я расскажу, как реализовать переключатель темы с нуля — от базовой HTML-структуры до полноценного JavaScript-решения с сохранением настроек пользователя.
Хотите освоить профессию веб-разработчика и создавать сайты с продвинутым UI/UX, включая реализацию темного режима и других современных фишек? Курс Обучение веб-разработке от Skypro погружает в мир фронтенда и бэкенда с первых дней обучения. Вы работаете с реальными проектами, осваиваете HTML, CSS, JavaScript и фреймворки под руководством опытных практиков. После обучения вы сможете не только реализовать переключение тем, но и создавать полноценные веб-приложения с нуля! 💻✨
Темная и светлая тема на сайте: преимущества реализации
Прежде чем погрузиться в технические детали, стоит разобраться, почему внедрение переключателя тем — это не просто дань моде, а действительно важный элемент современного веб-дизайна. Тёмный режим (dark mode) и светлый режим (light mode) предоставляют пользователям выбор, который влияет на их комфорт при взаимодействии с сайтом.
Вот основные преимущества реализации переключателя тем:
- Снижение нагрузки на глаза — тёмный фон с светлым текстом уменьшает утомляемость при длительном использовании в условиях низкой освещенности
- Экономия заряда батареи — на устройствах с OLED и AMOLED экранами темная тема может существенно снизить энергопотребление
- Улучшение доступности — некоторым пользователям с определенными особенностями зрения комфортнее работать с конкретным цветовым режимом
- Соответствие системным настройкам — возможность автоматически подстраиваться под предпочтения пользователя в операционной системе
- Повышение лояльности пользователей — забота о комфорте аудитории положительно влияет на их отношение к ресурсу
Давайте посмотрим на статистику использования темного режима среди пользователей различных платформ:
| Платформа/Аудитория | Процент использования тёмной темы | Основные мотивации |
|---|---|---|
| Мобильные устройства | 67% | Экономия заряда, снижение нагрузки на глаза |
| Десктоп | 54% | Комфорт при работе вечером, стильный внешний вид |
| Разработчики | 81% | Продолжительная работа с кодом, снижение утомляемости |
| Читатели контента | 62% | Удобство чтения в разных условиях освещения |
Алексей Петров, фронтенд-разработчик
Я столкнулся с необходимостью добавить тёмную тему на крупном новостном портале после многочисленных запросов от пользователей. Начальство сомневалось в необходимости этой функции, считая её избыточной. Чтобы доказать важность, я провёл A/B тестирование на небольшом сегменте аудитории. Результаты оказались впечатляющими: средняя продолжительность сессии у пользователей с доступом к тёмной теме увеличилась на 23%, особенно в вечерние часы. После внедрения на весь сайт мы получили множество положительных отзывов и даже заметили увеличение глубины просмотра страниц на 15%. Этот опыт убедил меня, что тёмный режим — это не просто модная фишка, а функция, действительно улучшающая взаимодействие с пользователем.

Основы переключения темы: структура HTML и CSS
Начнем с создания базовой структуры для переключения между темами. Основа нашей реализации — CSS-переменные, которые позволят легко изменять цветовую схему всего сайта, переопределяя всего несколько значений. 🎨
Сначала создадим простую HTML-структуру с переключателем темы:
<!DOCTYPE html>
<html lang="ru" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Сайт с переключателем темы</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<h1>Мой адаптивный сайт</h1>
<div class="theme-switch-wrapper">
<span>☀️</span>
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
<span>🌙</span>
</div>
</header>
<main>
<p>Контент сайта, который будет отображаться в соответствии с выбранной темой.</p>
</main>
<script src="theme-switcher.js"></script>
</body>
</html>
Теперь определим CSS-переменные для обеих тем и стили переключателя:
:root {
/* Переменные для светлой темы (по умолчанию) */
--primary-color: #007bff;
--font-color: #333333;
--bg-color: #ffffff;
--heading-color: #222222;
--secondary-bg: #f8f9fa;
--border-color: #dee2e6;
}
/* Переменные для темной темы */
[data-theme="dark"] {
--primary-color: #0d6efd;
--font-color: #e1e1e1;
--bg-color: #121212;
--heading-color: #f8f8f8;
--secondary-bg: #1f1f1f;
--border-color: #444444;
}
body {
background-color: var(--bg-color);
color: var(--font-color);
font-family: 'Arial', sans-serif;
transition: all 0.3s ease;
margin: 0;
padding: 0;
}
header {
background-color: var(--secondary-bg);
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border-color);
}
h1 {
color: var(--heading-color);
margin: 0;
}
main {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
/* Стили для переключателя */
.theme-switch-wrapper {
display: flex;
align-items: center;
}
.theme-switch {
display: inline-block;
position: relative;
width: 60px;
height: 34px;
margin: 0 10px;
}
.theme-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
}
input:checked + .slider {
background-color: var(--primary-color);
}
input:checked + .slider:before {
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
Что мы сделали в этом CSS-коде:
- Определили базовые CSS-переменные для светлой темы в селекторе :root
- Создали альтернативные значения для темной темы, которые будут применяться, когда атрибут
data-theme="dark"установлен на HTML-элементе - Добавили transition для плавного переключения между темами
- Стилизовали переключатель в виде слайдера
Такой подход с CSS-переменными особенно удобен, поскольку позволяет централизованно управлять цветовой схемой всего сайта. Вы определяете переменные один раз, а затем используете их в разных частях вашего CSS.
JavaScript для смены темы: код и функциональность
Теперь, когда у нас есть HTML-структура и CSS для обеих тем, нужно добавить JavaScript для обработки переключения темы. Создадим файл theme-switcher.js и реализуем функциональность переключения. 🔄
// Проверяем, сохранена ли тема в localStorage
const currentTheme = localStorage.getItem('theme');
// Получаем переключатель темы
const toggleSwitch = document.querySelector('#checkbox');
// Если в localStorage есть сохранённая тема, применяем её
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
// Если текущая тема темная, устанавливаем переключатель в соответствующее положение
if (currentTheme === 'dark') {
toggleSwitch.checked = true;
}
}
// Функция переключения темы
function switchTheme(e) {
if (e.target.checked) {
// Если переключатель включен, активируем темную тему
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
// Если переключатель выключен, активируем светлую тему
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
// Добавляем обработчик события для переключателя
toggleSwitch.addEventListener('change', switchTheme, false);
Этот JavaScript-код выполняет несколько ключевых функций:
- Проверяет, есть ли сохраненная тема в
localStorage - Применяет сохраненную тему при загрузке страницы
- Устанавливает переключатель в правильное положение в зависимости от текущей темы
- Обрабатывает событие
changeдля переключателя - Меняет атрибут
data-themeна HTML-элементе при переключении - Сохраняет выбранную тему в
localStorage
Мы можем расширить функционал, добавив автоматическое определение предпочтений пользователя на основе системных настроек:
// Проверяем предпочтения пользователя по системным настройкам
function detectPreferredTheme() {
// Проверяем, поддерживает ли браузер определение предпочтительной цветовой схемы
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// Пользователь предпочитает темную тему
document.documentElement.setAttribute('data-theme', 'dark');
toggleSwitch.checked = true;
}
}
// Если в localStorage нет сохранённой темы, определяем по системным настройкам
if (!currentTheme) {
detectPreferredTheme();
}
// Следим за изменениями системных настроек
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
const newTheme = e.matches ? 'dark' : 'light';
// Проверяем, не переопределена ли тема пользователем
if (!localStorage.getItem('theme')) {
document.documentElement.setAttribute('data-theme', newTheme);
toggleSwitch.checked = e.matches;
}
});
Теперь наш переключатель работает ещё умнее — он учитывает не только выбор пользователя, но и системные настройки устройства, с которого просматривается сайт.
| Преимущества JavaScript-подхода | Потенциальные проблемы | Решения |
|---|---|---|
| Мгновенное переключение без перезагрузки страницы | Мерцание при загрузке страницы (FOUC) | Добавить inline-скрипт в head для предварительной установки темы |
| Возможность сохранения предпочтений пользователя | Блокировка localStorage (режим инкогнито) | Использовать try-catch и фолбэк на cookie или системные настройки |
| Интеграция с системными настройками | Несовместимость с устаревшими браузерами | Проверка поддержки matchMedia и полифилы при необходимости |
| Плавные переходы между темами | Проблемы с производительностью при сложных анимациях | Оптимизация CSS-анимаций (transform, opacity вместо всех свойств) |
Марина Соколова, UX-дизайнер
Когда я работала над редизайном интернет-магазина электроники, было много споров о необходимости темной темы. Исследования показали, что 70% пользователей просматривали каталог в вечернее время, и яркий интерфейс вызывал дискомфорт. Мы внедрили переключение тем с анимированным переходом и автоматическим определением времени суток. Это решение не просто улучшило UX, но привело к неожиданному бизнес-эффекту: конверсия вечерних сессий выросла на 18%. Пользователи стали проводить больше времени в каталоге и совершать больше покупок. Особенно приятным сюрпризом стали комментарии в отзывах: "Наконец-то магазин, который заботится о моих глазах!". С тех пор я считаю темную тему не просто модной фишкой, а необходимым компонентом любого современного интерфейса.
Сохранение выбранной темы: работа с localStorage
Мы уже затронули тему сохранения пользовательских предпочтений в предыдущем разделе, но давайте рассмотрим этот аспект более детально. Web Storage API предоставляет два механизма хранения данных: localStorage и sessionStorage. Для сохранения темы оптимально использовать именно localStorage, поскольку данные в нем сохраняются даже после закрытия браузера. 🔒
Давайте расширим наш JavaScript-код для более надежного сохранения и восстановления настроек темы:
// Объект для работы с темой
const themeManager = {
// Ключ для хранения в localStorage
storageKey: 'user-theme-preference',
// Получение текущей темы
getCurrentTheme: function() {
try {
return localStorage.getItem(this.storageKey) ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
} catch (e) {
console.warn('localStorage недоступен:', e);
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
},
// Сохранение темы
saveTheme: function(theme) {
try {
localStorage.setItem(this.storageKey, theme);
return true;
} catch (e) {
console.warn('Не удалось сохранить тему:', e);
return false;
}
},
// Применение темы
applyTheme: function(theme) {
document.documentElement.setAttribute('data-theme', theme);
const toggleSwitch = document.querySelector('#checkbox');
if (toggleSwitch) {
toggleSwitch.checked = (theme === 'dark');
}
},
// Переключение между темами
toggleTheme: function() {
const currentTheme = this.getCurrentTheme();
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
this.saveTheme(newTheme);
this.applyTheme(newTheme);
return newTheme;
},
// Инициализация
init: function() {
// Применяем сохраненную или системную тему
const theme = this.getCurrentTheme();
this.applyTheme(theme);
// Находим переключатель и добавляем обработчик
const toggleSwitch = document.querySelector('#checkbox');
if (toggleSwitch) {
toggleSwitch.addEventListener('change', (e) => {
const theme = e.target.checked ? 'dark' : 'light';
this.saveTheme(theme);
this.applyTheme(theme);
});
}
// Отслеживаем изменение системных настроек
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
// Применяем новую системную тему только если пользователь не выбрал тему сам
if (!localStorage.getItem(this.storageKey)) {
const newTheme = e.matches ? 'dark' : 'light';
this.applyTheme(newTheme);
}
});
}
};
// Инициализируем менеджер тем при загрузке страницы
document.addEventListener('DOMContentLoaded', () => {
themeManager.init();
});
Такой объектно-ориентированный подход имеет несколько преимуществ:
- Централизация логики работы с темой в одном месте
- Обработка ошибок при работе с
localStorage - Возможность легко расширять функциональность
- Улучшенная читаемость кода
- Возможность повторного использования в других проектах
Для улучшения пользовательского опыта важно также избежать "мигания" при загрузке страницы, когда сначала показывается стандартная тема, а затем применяется сохраненная. Для этого можно добавить небольшой скрипт непосредственно в тег <head>:
<!-- Предотвращение мигания при загрузке страницы -->
<script>
(function() {
// Пытаемся получить сохраненную тему
let storedTheme;
try {
storedTheme = localStorage.getItem('user-theme-preference');
} catch (e) {
// В случае ошибки localStorage проверяем системные настройки
storedTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
// Если тема найдена, сразу применяем её
if (storedTheme) {
document.documentElement.setAttribute('data-theme', storedTheme);
}
})();
</script>
Иногда возникает необходимость сохранения дополнительных настроек темы, например, если у вас есть несколько вариантов темной темы или дополнительные настройки интерфейса. В таком случае удобно расширить структуру хранения данных:
// Расширенное сохранение настроек темы
function saveThemeSettings(settings) {
try {
const themeSettings = {
theme: settings.theme || 'light',
fontSize: settings.fontSize || 'medium',
contrast: settings.contrast || 'normal',
lastUpdated: new Date().toISOString()
};
localStorage.setItem('theme-settings', JSON.stringify(themeSettings));
return true;
} catch (e) {
console.warn('Не удалось сохранить настройки темы:', e);
return false;
}
}
// Получение настроек темы
function getThemeSettings() {
try {
const settings = localStorage.getItem('theme-settings');
return settings ? JSON.parse(settings) : null;
} catch (e) {
console.warn('Не удалось получить настройки темы:', e);
return null;
}
}
Такой подход позволит вам создать более продвинутую систему настройки интерфейса для ваших пользователей, при этом сохраняя все их предпочтения между сессиями.
Готовые решения для реализации темного режима на сайте
Если вам нужно быстро внедрить темный режим на сайт без разработки собственного решения с нуля, существуют готовые библиотеки и фреймворки, которые значительно упростят эту задачу. 📦
Рассмотрим несколько популярных решений для различных типов проектов:
- DarkModeJS – легковесная библиотека для быстрого внедрения темного режима
- Dark Reader API – позволяет интегрироваться с популярным браузерным расширением
- Night Eye API – комплексное решение для управления темной темой
- Bootstrap 5 – встроенная поддержка темной темы в популярном CSS-фреймворке
- Tailwind CSS Dark Mode – простая реализация через классы в utility-first фреймворке
Давайте рассмотрим примеры использования некоторых из этих решений:
1. DarkModeJS
<!-- Подключение библиотеки -->
<script src="https://cdn.jsdelivr.net/npm/darkmodejs@1.5.7/lib/darkmode-js.min.js"></script>
<script>
// Инициализация
const options = {
bottom: '64px', // положение переключателя
right: 'unset',
left: '32px',
time: '0.5s', // время анимации
mixColor: '#fff', // цвет микширования
backgroundColor: '#fff', // цвет фона
buttonColorDark: '#100f2c', // цвет кнопки в темном режиме
buttonColorLight: '#fff', // цвет кнопки в светлом режиме
saveInCookies: true, // сохранение выбора в cookies
label: '🌓', // метка на кнопке
autoMatchOsTheme: true // автоматическое определение темы ОС
}
const darkmode = new Darkmode(options);
darkmode.showWidget();
</script>
2. Tailwind CSS
Добавьте в файл tailwind.config.js:
module.exports = {
darkMode: 'class', // или 'media' для определения по системным настройкам
// остальная конфигурация
}
Затем используйте в HTML:
<div class="bg-white dark:bg-gray-800 text-black dark:text-white">
Этот элемент изменит цвета при переключении темы
</div>
<button id="theme-toggle" class="p-2 rounded-lg">
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<!-- Иконка луны -->
</svg>
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<!-- Иконка солнца -->
</svg>
</button>
<script>
// Переключение темы в Tailwind
const themeToggleBtn = document.getElementById('theme-toggle');
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
// Показываем правильную иконку при загрузке
if (localStorage.getItem('color-theme') === 'dark' ||
(!localStorage.getItem('color-theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
themeToggleLightIcon.classList.remove('hidden');
document.documentElement.classList.add('dark');
} else {
themeToggleDarkIcon.classList.remove('hidden');
document.documentElement.classList.remove('dark');
}
// Обработчик клика
themeToggleBtn.addEventListener('click', function() {
themeToggleDarkIcon.classList.toggle('hidden');
themeToggleLightIcon.classList.toggle('hidden');
if (localStorage.getItem('color-theme')) {
if (localStorage.getItem('color-theme') === 'light') {
document.documentElement.classList.add('dark');
localStorage.setItem('color-theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('color-theme', 'light');
}
} else {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.setItem('color-theme', 'light');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('color-theme', 'dark');
}
}
});
</script>
Вот сравнение готовых решений для разных технологических стеков:
| Решение | Тип проекта | Преимущества | Недостатки |
|---|---|---|---|
| DarkModeJS | Любой сайт без фреймворков | Простая интеграция, минимум кода | Ограниченная кастомизация |
| Tailwind CSS | Проекты на Tailwind | Легко интегрируется в систему классов | Требует Tailwind CSS в проекте |
| CSS Variables | Любой современный сайт | Полный контроль, отсутствие зависимостей | Требует больше ручной работы |
| React Context | React приложения | Глобальное состояние для всего приложения | Специфично для React |
| Vue Composition API | Vue.js проекты | Реактивность из коробки | Только для Vue-приложений |
Для проектов на React особенно удобно использовать Context API:
// ThemeContext.js
import React, { createContext, useState, useEffect } from 'react';
export const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
// Проверяем localStorage и системные настройки
const getInitialTheme = () => {
const savedTheme = localStorage.getItem('theme');
return savedTheme || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
};
const [theme, setTheme] = useState(getInitialTheme);
// Функция переключения темы
const toggleTheme = () => {
setTheme(prevTheme => {
const newTheme = prevTheme === 'light' ? 'dark' : 'light';
localStorage.setItem('theme', newTheme);
return newTheme;
});
};
// Применяем тему к корневому элементу
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
Затем вы можете использовать контекст в любом компоненте:
// ThemeToggle.js
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
const ThemeToggle = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme} className="theme-toggle">
{theme === 'light' ? '🌙' : '☀️'}
</button>
);
};
export default ThemeToggle;
Какое бы решение вы ни выбрали, важно протестировать его на различных устройствах и в разных браузерах, чтобы убедиться, что переключение темы работает корректно для всех ваших пользователей.
Добавление переключателя темы на сайт — это не просто модная фишка, а важный шаг к повышению доступности и улучшению пользовательского опыта. Используя CSS-переменные, localStorage и современные API браузеров, вы можете создать элегантное решение, которое будет работать на всех устройствах и учитывать предпочтения пользователей. Помните, что хороший темный режим — это не просто инверсия цветов, а тщательно продуманная альтернативная цветовая схема, которая сохраняет визуальную иерархию и уровень контрастности. Внедрив переключение между темами с учетом всех лучших практик, вы не только повысите удовлетворенность пользователей, но и продемонстрируете современный подход к веб-разработке.