CSS-in-JS — новый подход к стилизации компонентных веб-приложений. Он помогает преодолеть ряд ограничений и проблем традиционных методов работы с CSS.
Посмотрим, какие проблемы есть в традиционном подходе CSS-in-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 и помогает реализовать нетривиальную стилизацию в простом и понятном формате.
Неуправляемые глобальные стили
Традиционно все стили приложения работают в едином глобальном пространстве имён, что рано или поздно может приводить к конфликтам. Вы можете возразить и привести в пример всевозможные подходы инкапсуляции стилей, например БЭМ. Да, они работают, но не во всех сценариях.
Представим ситуацию: вы разработали компонент кнопки и стилизовали его при помощи класса .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-in-CSS и CSS-in-JS
CSS-in-JS появился во многом благодаря компонентному подходу разработки веб-приложений, поэтому он раскрывается именно в паре с компонентными библиотеками и фреймворками.
CSS-in-JS появился во многом благодаря компонентному подходу разработки веб-приложений, поэтому он раскрывается именно в паре с компонентными библиотеками и фреймворками.
Научитесь применять CSS-in-JS в своих проектах. На курсе мы рассмотрим популярную комбинацию инструментов: React и библиотеку styled-components , а ещё разберём другие библиотеки и реализации CSS-in-JS.