Продвинутая CSS-темизация: динамические темы с помощью переменных и JavaScript

Продвинутая CSS-темизация: динамические темы с помощью переменных и JavaScript

От автора: в этом руководстве по тому, как создаются в CSS темы, мы будем использовать пользовательские свойства (также известные как переменные CSS) для реализации динамических тем простой HTML-страницы. Мы создадим темные и светлые варианты тем, затем напишем JavaScript, чтобы переключаться между ними, когда пользователь нажимает кнопку.

Как и в обычных языках программирования, переменные используются для хранения значений. В CSS они обычно используются для хранения цветов, названий шрифтов, размеров шрифтов, единиц и т. д. Затем на них можно ссылать и повторно использовать в нескольких местах в таблице стилей. Большинство разработчиков используют термин «переменные CSS», но официальное название — это пользовательские свойства.

Пользовательские свойства CSS позволяют изменять переменные, на которые можно ссылаться в таблице стилей. Раньше это было возможно только с помощью препроцессоров CSS, таких как Sass.

:root и var()

Прежде чем создать наш пример динамической темы, давайте разберемся с основами пользовательских свойств. Пользовательское свойство — это свойство, имя которого начинается с двух дефисов (—), например, —foo. Они определяют переменные, на которые можно ссылаться, используя var(). Рассмотрим этот пример:

:root { --bg-color: #000; --text-color: #fff;
}

Определение пользовательских свойств в селекторе :root означает, что они доступны в глобальном пространстве документа для всех элементов. :root — это псевдо-класс CSS, который соответствует корневому элементу документа — элементу html. Это похоже на селектор html, но с более высокой специфичностью.

Вы можете получить доступ к значению пользовательского свойства :root в любом месте документа:

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

Вы также можете включить с переменной CSS резервное значение. Например:

div { color: var(--text-color, #000); background-color: var(--bg-color, #fff);
}

Если пользовательское свойство не определено, вместо него используется резервное значение. Определение пользовательских свойств внутри селектора CSS, отличного от селектора :root или html, делает переменную доступной для соответствия элементам и их дочерним элементам.

Пользовательские свойства CSS и переменные препроцессора

Препроцессоры CSS, такие как Sass, часто используются для поддержки веб-разработки в интерфейсе. Среди других полезных функций препроцессоров они используют переменные. Но в чем разница между переменными Sass и пользовательскими свойствами CSS?

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

Пользовательские свойства могут быть доступны и изменены через JavaScript. Переменные препроцессора компилируются один раз, и только конечное значение доступно в клиенте.

Написание простой HTML-страницы

Начнем с создания папки для нашего проекта:

$ mkdir css-variables-theming

Затем добавьте в папку проекта файл index.html:

$ cd css-variables-theming
$ touch index.html

Добавьте в него следующий код:

<nav class="navbar">Title</nav>
<div class="container"> <div> <input type="button" value="Light/Dark" id="toggle-theme" /> </div> <h2 class="title">What is Lorem Ipsum?</h2> <p class="content">Lorem Ipsum is simply dummy text of the printing and typesetting industry...</p>
</div>
<footer> Copyright 2018
</footer>

Мы добавляем с помощью тега nav панель навигации, футер, div контейнера, который содержит кнопку (она будет использоваться для переключения между светлой и темной темами) и некоторый условный текст Lorem Ipsum.

Написание базового CSS для нашей HTML-страницы

Теперь давайте создадим нашу страницу. В том же файле, используя в head встроенный тег style, добавьте следующие стили CSS:

<style>
* { margin: 0;
}
html{ height: 100%;
}
body{ height: 100%; font-family: -apple-system, BlinkMacSystemFont"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",sans-serif; display: flex; flex-direction: column;
}
nav{ background: hsl(350, 50%, 50%); padding: 1.3rem; color: hsl(350, 50%, 10%);
}
.container{ flex: 1; background:hsl(350, 50%, 95%); padding: 1rem;
}
p.content{ padding: 0.7rem; font-size: 0.7rem; color: hsl(350, 50%, 50%);
}
.container h2.title{ padding: 1rem; color: hsl(350, 50%, 20%);
}
footer{ background: hsl(350, 93%, 88%); padding: 1rem;
}
input[type=button] { color:hsl(350, 50%, 20%); padding: 0.3rem; font-size: 1rem;
}
</style>

Для определения цветов используется обозначение CSS3 HSL (оттенок, насыщенность, яркость). Оттенок — это угол в окружности цветов, в нашем примере используется 350 для красного цвета. Все цвета страницы используют разные вариации, в которых изменяются сочетания насыщенности (процентное соотношение цвета) и яркости (в процентах).

Использование HSL позволяет нам легко пробовать для темы различные базовые цвета, изменяя только значение оттенка. Мы могли бы также использовать переменную CSS для значения оттенка и переключать цвет темы, изменяя одно значение в таблице стилей или динамически изменяя его с помощью JavaScript. Это скриншот страницы:

Посмотрите соответствующую демо-версию:

Давайте используем переменную CSS для хранения значения оттенка всех цветов страницы. Добавьте глобальную переменную CSS в селектор :root в верхней части тега style:

:root{ --main-hue : 350;
}

Затем мы заменяем все жестко закодированные значения 350 в цветах hsl() на переменную —main-hue. Например, это селектор nav:

nav{ background: hsl(var(--main-hue) , 50%, 50%); padding: 1.3rem; color: hsl(var(--main-hue), 50%, 10%);
}

Теперь, если вы хотите указать любой цвет, отличный от красного, вы можете просто присвоить соответствующее значение —main-hue. Вот несколько примеров:

:root{ --red-hue: 360; --blue-hue: 240; --green-hue: 120; --main-hue : var(--red-hue);
}

Мы определяем три пользовательских свойства для красного, синего и зеленого цветов, а затем присваиваем переменную —red-hue в качестве значения переменной —main-hue. Это скриншот страниц с разными значениями —main-hue:

Пользовательские свойства CSS дают нам несколько преимуществ:

Значение может быть определено в одном месте.

Это значение можно назвать соответствующим образом, чтобы облегчить поддержку кода.

Значение может быть динамически изменено с использованием JavaScript. Например, для —main-hue может быть установлено любое значение от 0 до 360.

Используя JavaScript для динамического задания значения —main-hue из набора предопределенных значений или пользовательского значения для оттенка (оно должно быть от 0 до 360), мы можем предоставить пользователю множество возможностей для настройки темы.

Следующая строка кода установит для —main-hue значение 240 (синий):

document.documentElement.style.setProperty('--main-hue', 240);

Посмотрите следующую демо-версию, в которой показан полный пример, в нем вы можете динамически переключаться между темами красного, синего и зеленого цветов:

Это скриншот страницы:

Добавление темной темы CSS

Теперь давайте создадим темную тему для этой страницы. Для большего контроля над цветами разных объектов нам нужно добавить больше переменных.

Просматривая стили страницы, мы можем заменить все цвета HSL в разных селекторах переменными после определения пользовательских свойств для соответствующих цветов в :root:

:root{ /*...*/ --nav-bg-color: hsl(var(--main-hue) , 50%, 50%); --nav-text-color: hsl(var(--main-hue), 50%, 10%); --container-bg-color: hsl(var(--main-hue) , 50%, 95%); --content-text-color: hsl(var(--main-hue) , 50%, 50%); --title-color: hsl(var(--main-hue) , 50%, 20%); --footer-bg-color: hsl(var(--main-hue) , 93%, 88%); --button-text-color: hsl(var(--main-hue), 50%, 20%);
}

Здесь были использованы соответствующие имена для пользовательских свойств. Например, —nav-bg-color относится к цвету фона панели навигации, а —nav-text-color относится к цвету переднего плана / текста панели навигации. Теперь продублируйте селектор :root с его содержимым, но добавьте атрибут темы со значением dark:

:root[theme='dark']{ /*...*/
}

Эта тема будет активирована, если к элементу html будет добавлен атрибут темы со значением dark.

Теперь мы можем играть со значениями этих переменных вручную, уменьшая значение яркости цветов HSL, чтобы создать темную тему, или мы можем использовать другие методы, такие как фильтры CSS, например, invert() и brightness(), которые обычно используются для настройки отображения изображений, но также могут использоваться с любым другим элементом.

Добавьте следующий код в :root[theme=’dark’]:

:root[theme='dark'] { --red-hue: 360; --blue-hue: 240; --green-hue: 120; --main-hue: var(--blue-hue); --nav-bg-color: hsl(var(--main-hue), 50%, 90%); --nav-text-color: hsl(var(--main-hue), 50%, 10%); --container-bg-color: hsl(var(--main-hue), 50%, 95%); --content-text-color: hsl(var(--main-hue), 50%, 50%); --title-color: hsl(--main-hue, 50%, 20%); --footer-bg-color: hsl(var(--main-hue), 93%, 88%); --button-text-color: hsl(var(--main-hue), 50%, 20%); filter: invert(1) brightness(0.6);
}

Фильтр invert() инвертирует все цвета в выбранных элементах (каждый элемент в этом случае). Значение инверсии может быть указано в процентах или числом. Значение 100% или 1 полностью инвертирует значения оттенка, насыщенности и яркости элемента.

Фильтр brightness() делает элемент ярче или темнее. Значение 0 дает полностью темный элемент. Фильтр invert() делает некоторые элементы очень яркими. Они смягчаются установкой brightness(0.6). Темная тема с разной степенью затемнения:

Переключение тем с помощью JavaScript

Давайте теперь используем JavaScript для переключения между темными и светлыми темами, когда пользователь нажимает кнопку Dark/Light. В index.html добавьте встроенный script перед закрытием body со следующим кодом:

const toggleBtn = document.querySelector("#toggle-theme");
toggleBtn.addEventListener('click', e => { console.log("Switching theme"); if(document.documentElement.hasAttribute('theme')){ document.documentElement.removeAttribute('theme'); } else{ document.documentElement.setAttribute('theme', 'dark'); }
});

Document.documentElement относится к корневому элементу DOM документа, то есть html. Этот код проверяет наличие атрибута theme с помощью метода .hasAttribute() и добавляет атрибут со значением dark, если он не существует, что приводит к переключению на темную тему. В противном случае он удаляет атрибут, что приводит к переключению на светлую тему.

Изменение пользовательских свойств CSS с помощью JavaScript

Используя JavaScript, мы можем получить доступ к пользовательским свойствам и динамически изменять их значения. В нашем примере мы жестко закодировали значение яркости, но это можно изменить динамически. Сначала добавьте в HTML-страницу элемент ввода ползунок ниже кнопки dark/light:

<input type="range" id="darknessSlider" name="darkness" value="1" min="0.3" max="1" step="0.1" />

Ползунок начинается со значения 1, пользователь может уменьшить его до 0,3 с шагом 0,1. Затем добавьте в :root[theme=’dark’] пользовательское свойство для уровня затемненности с начальным значением 1:

:root[theme='dark']{ /*...*/ --theme-darkness: 1;
}

Определите для фильтра brightness это пользовательское свойство вместо жестко заданного значения:

filter: invert(1) brightness(var(--theme-darkness));

Наконец, добавьте следующий код, чтобы синхронизировать значение —theme-darkness со значением ползунка:

const darknessSlider = document.querySelector("#darknessSlider");
darknessSlider.addEventListener('change', (e)=>{ const val = darknessSlider.value document.documentElement.style.setProperty('--theme-darkness', val);
});

Мы прослушиваем событие изменения ползунка и устанавливаем значение —theme-darkness, используя метод setProperty(). Мы также можем применить фильтр brightness к светлой теме. Добавьте пользовательское свойство —theme-darkness в верхней части селектора :root:

:root{ /*...*/ --theme-darkness: 1;
}

Затем добавьте фильтр brightness в нижней части этого же селектора:

:root{ /*...*/ filter: brightness(var(--theme-darkness));
}

Код этого примера вы можете найти в следующей демо-версии:

Вот скриншот темной темы из последнего примера:

Вот скриншот светлой темы:

Заключение

В этой статье мы рассмотрели, как использовать пользовательские свойства CSS для создания тем и динамического переключения между ними. Мы использовали цветовую схему HSL, которая позволяет указывать цвета с оттенками, насыщенностью и яркостью, а также фильтры CSS (invert и brightness), чтобы создать темную версию светлой темы.

Автор: Ahmed Bouchefra

Источник: https://www.sitepoint.com/

Редакция: Команда webformyself.