Светлая и темная тема с помощью CSS-переменных
Сегодня современное веб-приложение (и не только веб), которым пользуются сотни тысяч людей, невозможно представить без смены светлой и темной темы.
В этой статье мы расскажем, почему это важно и как реализовать смену темы у себя на сайте с помощью нативных CSS-переменных.
Почему темная тема стала популярна
Причин достаточно много. Мы остановимся на наиболее значимых:
1) В ночное время темы со светлым текстом на темном фоне снижают нагрузку на глаза — контраст окружения с экраном устройства становится менее заметным, тем самым пользователю удобно пользоваться приложением. Также темную тему используют как инструмент доступности, это важно людям с ограниченными возможностями здоровья.
2) Большая часть пользователей, согласно исследованию, предпочитают темную тему из-за эстетических соображений. Стоит также помнить, что важным условием внедрения темной темы — это минималистичный дизайн приложения, который не разрушает впечатление при переключении темы.
3) Также темная тема актуальна для тех, кто заботится об экономии заряда батареи смартфона — по исследованию от Android разработчиков, темная тема может экономить до 60% заряда батареи.
Что такое CSS-переменные?
CSS переменные (пользовательские CSS-свойства) - это сущности, определяемые автором CSS, хранящие конкретные значения, которые можно повторно использовать в документе. Они устанавливаются с использованием custom property нотации (например. --main-color: black;) и доступны через функцию var() (например. color: var(--main-color);) .
Более подробно можно почитать тут.
Внедрение
Инициализируем проект
Для простоты работы будем использовать пакет node-sass в связке с React 18.
Для этого мы инициализируем проект с помощью npm и с помощью него же добавляем пакет node-sass для удобства разработки:
npx create-react-apptheme
npm i node-sass
После инициализации проекта немного почистим файловую структуру, оставив в папке src только App.js и index.js.
Создаем файл стилей
Сделаем отдельную папку styles, где создадим файл themes.scss.
Укажем в нем наши стили в псевдоэлементе :root - таким образом, все компоненты приложения смогут воспользоваться нашей переменной:
// src/styles/themes.scss
:root {
--text-color: #121212;
--background: #FFFFFF;
}
Также создадим файл для стилей нашего App.js, чтобы проверить работоспособность стилей: // src/App.scss
* {
margin: 0;
padding: 0;
}
.App {
font-family: sans-serif;
text-align: center;
height: 100vh;
color: var(--text-color);
background-color: var(--background);
>div {
padding: 16px;
}
}
Не забываем импортировать наш файл стилей:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import '../src/styles/theme.scss'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( <App />, );
// src/App.js
import './App.scss';
function App() {
return (
<div className="App">
<div>
Переключатель темы
</div>
</div> );
}
export default App;
Теперь, если открыть консоль разработчика, мы увидим, что вместо конкретных цветов в виде HEX-формата - CSS-переменные, при нажатии на которые нас перебросит к значению этой переменной.
Переключение темы
Теперь нам необходимо реализовать переключение темы.
Если пользователь впервые на сайте, то необходимо будет взять настройки темы браузера, чтобы установить ее по умолчанию для нашего приложения. Однако если пользователь уже был на сайте и ему понравилась тема, отличная от системной, мы должны сохранить эти настройки.
С этой задачей нам помогут справиться хуки React и localStorage.
Создадим ThemeProvider.js в папке providers, который будет отвечать за нашу тему:
// создаем контекст, с помощью которого будем передавать состояние вниз по дереву компонентов
export const ThemeContext = createContext({});
const ThemeProvider = ({ children }) => {
// с помощью хука будем хранить данные о текущей теме
const [ theme, setTheme ] = useState(defineTheme);
useEffect(() => {
// при изменении темы устанавливаем тему в хранилище браузера и атрибут html-тэга
document.documentElement.dataset.theme = theme;
localStorage.setItem('theme', theme);
}, [theme]);
return (
<ThemeContext.Provider value={{theme, setTheme }}>
{children}
</ThemeContext.Provider>
)
};
export default ThemeProvider; Добавляем наш провайдер как обертку над App: // src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import ThemeProvider from "./providers/ThemeProvider";
import'../src/styles/theme.scss'
const root = ReactDOM.createRoot(document.getElementById('root')); root.render(
<ThemeProvider>
<App />
</ThemeProvider>
);
Мы не будем сильно заморачиваться над переключателем темы, поэтому сделаем обычную кнопку, в которой будет инкапсулирована логика переключения темы. // src/components/ThemeSwitcher/ThemeSwitcher.js
import React, { useContext } from 'react';
import { ThemeContext } from "../../providers/ThemeProvider";
const ThemeSwitcher = () => {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
style={{padding: 12, fontWeight: 500}}
>
Сменить тему
</button>
);
};
export default ThemeSwitcher;
Add a button to the App.js component:
// src/App.js
import ThemeSwitcher from "./components/ThemeSwitcher/ThemeSwitcher";
import './App.scss';
function App() {
return (
<div className="App">
<div>
Переключатель темы
</div>
<ThemeSwitcher />
</div>
);
}
export default App;
Добавление стилей для темной темы
Отлично! Теперь при нажатии на кнопку мы переключаем атрибут у html-тэга, но пока стили у нас не меняются. Нужно дописать их для темной темы и внести корректировки в уже имеющиеся для светлой темы.
// src/styles/theme.scss
:root[data-theme='light'] {
--text-color: #121212;
--background: #FFFFFF;
}
:root[data-theme='dark'] {
--text-color: #FFFFFF;
--background: #121212;
}
Все! Вот так просто мы подключили изменение темы в наше приложение, и это решение отлично масштабируется! Код можно посмотреть тут. Заключение
Стоит помнить, что не все инструменты универсальны, и у CSS-переменных тоже есть свои минусы.
Один из них - это работа с прозрачностью. CSS- переменные не перевариваются SCSS-функциями типа darken или lighten. Еще следует понимать, что если планируется добавление индивидуальных стилей для компонента, то это накладывает дополнительные сложности для описания стилей.
Однако CSS-переменные являются мощным инструментом, где важно единообразие стилей. Также они пригодны не только для цветовых свойств, но и для отступов, шрифтов и прочих свойств css, которые часто используются.