Зачем нужен CSS-in-JS
- 20 ноября 2022
CSS-in-JS — новый подход к стилизации компонентных веб-приложений. Он помогает преодолеть ряд ограничений и проблем традиционных методов работы с CSS.
Посмотрим, какие проблемы есть в традиционном CSS, и как CSS-in-JS их решает.
Проблемы при сложной стилизации
Представим, что нам нужно создать кнопку, которая:
- имеет два стиля,
filled
с заливкой иoutlined
с обводкой; - имеет два размера,
medium
(средняя) иlarge
(большая); - представлена в двух цветах, зелёном и оранжевом;
- может не иметь иконки, а может и иметь.
Этот компонент выглядит примерно так:
function Button(props) {
const className = classNames('button', {
'm-variant-filled': props.variant === 'filled',
'm-variant-outlined': props.variant === 'outlined',
'm-size-medium': props.size === 'medium',
...
});
return <button className={className}>{props.children}</button>;
}
То есть как набор классов-модификаторов, логика управления которыми смешана с основной логикой. При более комплексных компонентах это может значительно усложнить работу.
При помощи CSS-in-JS задача решается в более наглядном формате:
const StyledButton = styled.button`
border-radius: 4px;
cursor: pointer;
${(props) => props.variant === 'filled' && css`/* стили для варианта filled */`}
${(props) => props.variant === 'outlined' && css`/* стили для варианта outlined */`}
${(props) => props.size === 'medium' && css`/* стили для размера medium */`}
...
${(props) => {
// сложная логика формирования стилей, описанная при помощи JS
return css`/* стили, которые сложно описать при помощи обычного CSS */`;
}}
`;
function Button(props) {
return <StyledButton {...props}>{props.children}</StyledButton>
}
Вся стилизация описана в отдельной сущности — StyledButton
. Скругления и курсор есть у всех кнопок без исключений. Остальные стили применяются или не применяются в зависимости от значений пропов, и эта зависимость описана при помощи JavaScript. Основной код компонента (функция Button
) остаётся максимально простым.
Компоненты в современных интерфейсах могут иметь множество параметров и состояний. Часто возможностей CSS недостаточно для реализации подобной логики, и приходится придумывать «велосипеды». А подход CSS-in-JS расширяет CSS при помощи JavaScript и помогает реализовать нетривиальную стилизацию в простом и понятном формате.
💫 Изучите CSS-in-JS
Пройдите профессиональный курс и создайте свой сайт, используя популярную комбинацию: React и styled-components.
Неуправляемые глобальные стили
Традиционно все стили приложения работают в едином глобальном пространстве имён, что рано или поздно может приводить к конфликтам. Вы можете возразить и привести в пример всевозможные подходы инкапсуляции стилей, например БЭМ. Да, они работают, но не во всех сценариях.
Представим ситуацию: вы разработали компонент кнопки и стилизовали его при помощи класса .button
. CSS-правило с селектором по этому классу работает глобально, все кнопки выглядят хорошо, и ничто не предвещает беды. И тут ваш коллега решил добавить кнопку с другим дизайном и стилизовал её тоже при помощи класса .button
. Хорошо, если конфликт стилей обнаружится сразу, но в худшем сценарии все кнопки в проекте могут сломаться.
Подобная проблема может появиться и при подключении сторонней библиотеки компонентов, если кнопки в ней стилизованы при помощи класса .button
. Или наоборот, если вы делаете библиотеку компонентов, ваши стили могут конфликтовать со стилями проектов, где используется библиотека.
В CSS-in-JS стили работают на уровне компонентов и тесно связаны с ними при помощи автоматически генерируемых классов. Для демонстрации этой механики отрендерим компонент из примера выше:
render(<Button>Кнопка</Button>);
В результате выполнения кода получим такой результат:
<style>
.bZkfAO { /* стили кнопки */ }
</style>
<button class="bZkfAO">Кнопка</button>
Стили применяются к компоненту при помощи сгенерированного класса. Такие классы уникальны и не могут дублироваться в вашем или чужом коде, что исключает появление конфликтов в ваших и внешних стилях.
Алгоритм генерации названий классов можно настроить, чтобы сделать их более понятными и описательными.
В нашем примере итоговое CSS-правило с селектором по уникальному классу расположено внутри элемента style на уровне документа. Это самый популярный способ подключения стилей, реализованный во многих инструментах для работы с CSS-in-JS. Но есть и альтернативные решения, о которых мы узнаем ближе к концу курса.
А ещё стили подключаются к документу только при необходимости. Если компонент рендерится, стили есть. Если не рендерится, стилей нет. При удалении кнопки из примера выше CSS-правило пропадает вместе с ней:
<style>
/* тут были стили кнопки */
</style>
<!-- тут была кнопка -->
То есть CSS-in-JS избавляет от ненужных стилей, что особенно полезно для больших и долгоживущих проектов, которые по мере развития подвергаются повторной стилизации. А ещё он автоматизирует создание критических стилей при серверном рендеринге.
CSS и JS нужно хранить раздельно
Любители Vue.js могут пропустить этот раздел.
Обычно стили компонентов описывают в отдельных файлах. Из-за этого файловая структура проекта становится более громоздкой:
components
├── button
│ ├── button.css
│ └── button.jsx
├── checkbox
│ ├── checkbox.css
│ └── checkbox.jsx
├── input
│ ├── input.css
│ └── input.jsx
...
Такой подход вынуждает использовать дополнительные инструменты для подключения стилей. Это особенно критично, если вы делаете библиотеку — вашим пользователям придётся каждый раз настраивать одни и те же дополнительные инструменты. Для более удобной работы со стилями также нужны препроцессоры, постпроцессоры и автопрефиксер.
CSS-in-JS позволяет хранить стили рядом с основным кодом компонента в одном файле (хотя можно и в разных) и не требует настройки дополнительных инструментов — всё работает «из коробки». И файловая структура проекта получается более компактной:
components
├── button.jsx
├── checkbox.jsx
├── input.jsx
...
Вероятно, это основная причина, почему CSS-in-JS называется именно так.
Сравнение CSS и CSS-in-JS
CSS | CSS-in-JS |
Нетривиальная стилизация реализуется сложно | Нетривиальная стилизация реализуется проще при помощи JS |
Стили работаю глобально | Стили работают на уровне конкретных компонентов |
Сложно выделить критические стили и избавиться от ненужных | Удаление ненужных и выделение критических стилей работают автоматически |
Нет возможности хранить стили и остальной код в одних файлах (как минимум в случае React) | Можно хранить стили рядом с остальным кодом в одних файлах |
Для комфортной работы со стилями приходится настраивать ряд дополнительных инструментов | Не нужно настраивать множество дополнительных инструментов для комфортной работы со стилями |
«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.
Читать дальше
300кк в наносекунду
Игра, где нужно забрать своё и продержаться ещё один день.
- 7 марта 2024
9 книг по JavaScript для начинающих в 2024
Все вокруг говорят, что книги — прошлый век. Но вовремя прочитанная хорошая книжка может здорово помочь в изучении нового языка или технологии, а то и вообще целиком объяснить какую-нибудь важную штуку. Например, какие бывают алгоритмы, или зачем нужен рефакторинг. К тому же, хоть фреймворки меняются каждый год, основы обычно долго не меняются.
Мы опросили знакомых разработчиков, узнали, что читают они сами, и предлагаем вам подборку хороших книг по JavaScript.
- 6 марта 2024
Объект URL в JavaScript: полный разбор
Объект URL
в JavaScript представляет URL-адрес и предоставляет удобные методы для работы с ним. Он позволяет анализировать, конструировать и декодировать URL-адреса.
Создать объект URL
можно двумя способами:
Конструктор URL()
— самый распространённый способ, в котором вы передаёте любой URL в виде строки в качестве аргумента.
const url = new URL("https://www.example.com/path?query=123#hash");
Использование window.location
— это глобальный объект в браузерах, который содержит информацию о текущем URL.
const currentUrl = new URL(window.location.href);
- 23 января 2024
Генерация QR-кодов на JS в 4 шага. Node.js + qrcode
Давайте сделаем простой REST API на Node.js и Express, который будет генерировать QR-коды для любой ссылки. Если у вас ещё не установлены Node.js
и npm
, установите их с официального сайта.
- 22 ноября 2023
ChatGPT не справляется
Притворитесь нейросетью и решите 101 задачку по JavaScript как можно быстрее.
- 2 ноября 2023
Знакомство с JavaScript
Теперь, когда вы знаете, как создать структуру веб-страницы с помощью HTML и оформить ее стилями с помощью CSS, пришло время оживить её с помощью JavaScript (JS). JavaScript — это мощный язык программирования, который используется для создания интерактивных и динамических веб-сайтов.
Вы можете добавить JavaScript в ваш HTML-документ двумя способами:
Встроенный JavaScript: непосредственно в HTML-документ, в тегах <script>
:
<script>
alert("Привет, мир!");
</script>
Внешний JavaScript: подключение внешнего .js
файла к HTML-документу:
<script src="script.js"></script>
- 1 ноября 2023
Событие onclick в JS на примерах
Интерактивность — ключевой компонент любого современного сайта. И одним из наиболее часто используемых событий для создания интерактивности является событие onclick
. В этой статье мы подробно разберёмся, что такое событие onclick
, как его использовать и приведем примеры применения.
Событие onclick
— это событие JavaScript, которое активируется, когда пользователь кликает на определенный элемент страницы. Это может быть кнопка, ссылка, изображение или любой другой элемент, на который можно нажать.
- 30 октября 2023
Как перевернуть сайт. Самая короткая инструкция
Не представляем, зачем это может понадобиться, но не могли пройти мимо.
Никакой магии. Мы вызываем JavaScript-функцию rotateBody()
, которая применяет свойство transform
с значением rotate(180deg)
к элементу <body>
. Когда вы нажмете на кнопку «Перевернуть», всё, что находится внутри <body>
будет повернуто на 180 градусов (то есть, встанет вниз головой)
function rotateBody() {
document.body.style.transform = 'rotate(180deg)';
}
<button onclick="rotateBody()">Перевернуть</button>
Но такой код повернёт страницу только один раз. Если нужно, чтобы она возвращалась обратно при втором клике, усложним код:
let isRotated = false;
function rotateBody() {
if (isRotated) {
document.body.style.transform = 'rotate(0deg)';
document.body.style.direction = "ltr";
} else {
document.body.style.transform = 'rotate(180deg)';
document.body.style.direction = "rtl";
}
isRotated = !isRotated;
}
Надеемся, вы прочитали это описание до того, как нажать на кнопку.
- 25 октября 2023
Как узнать геолокацию: Geolocation API
Geolocation API позволяет сайтам запрашивать, а пользователям предоставлять свое местоположение веб-приложениям. Геолокация может использоваться для выбора города в интернет-магазине, отображения пользователя на карте или навигации в ближайший гипермаркет.
Основной метод Geolocation API — getCurrentPosition()
, но есть и другие методы и свойства, которые могут пригодиться.
- 16 октября 2023
Что такое localStorage и как им пользоваться
localStorage
— это место в браузере пользователя, в котором сайты могут сохранять разные данные. Это как ящик для хранения вещей, которые не исчезнут, даже если вы выключите компьютер или закроете браузер.
До localStorage
разработчики часто использовали cookies, но они были не очень удобны: мало места и постоянная передача данных туда-сюда. LocalStorage появился, чтобы сделать процесс более простым и эффективным.
- 12 октября 2023