CSS-in-JS. Начало

Это пролог к статье о Styled Components. Если вам интересно, как мы дошли до жизни такой, рекомендую начать именно отсюда. Сразу задам тон повествования: я предполагаю, что с JavaScript и React вы уже знакомы. Вам сейчас придётся согласиться, что HTML в JS это нормально, а CSS в JS, как минимум, тоже.

Компонентный подход

Вы все, наверное, слышали понятие «разделение ответственности» (separation of concerns). Для веб-разработки вообще и вёрстки в частности это означало (и до сих пор означает, в целом) следующее: отделение структуры документа от внешнего вида и поведения. HTML отдельно, CSS отдельно, JavaScript отдельно. Но только ли это?

Вот нужна вам кнопка. Что вы делаете? Правильно: определяете её в HTML, составляете стили по её классу в CSS и пишете скрипт:


<button type="button" id="myButton" class="my-button">Нажми меняbutton>


<style>
.my-button {
border: 1px solid cyan;
background: transparent;
color: cyan;
}
style>


<script>
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', () => {
alert('My click!');
});
script>

Прекрасно, кнопка есть, можно использовать где угодно. Но где угодно ли? Она вот, в нашем документе лежит. Для начала, её нужно, как минимум, скопировать, а как максимум – озаботиться уникальным идентификатором, набросать классов для различного оформления, дописать JavaScript обработчики на каждую задачу... а потом всё это ещё и поддерживать в разных местах и таскать из проекта в проект. Да что там, даже в рамках одного проекта занятие муторное.

Давайте немного подумаем. А что, если разделение ответственности – оно не про отделение представления и поведения от структуры, а про разбиение большой задачи на куски поменьше, на...

Компоненты

Каждый компонент решает свою задачу и решает её хорошо: кнопка реагирует на нажатие, поле ввода – откликается на ошибки пользователя, а модальное окно – само определяет, на каком устройстве оно было открыто. Ответственность разделена? Вполне, просто немного иначе:

Separation of concerns
Разделение ответственности

Обратили внимание на цветовую кодировку классического разделения? Она никуда не делась и в компонентном подходе, а буквально размылась в нём: компонент должен полностью отвечать за свою структуру, поведение и внешний вид. Это довольно простой концепт, но многим до сих пор тяжело его принять.

JSX

React стал одной из первых библиотек (гусары, молчать!), принёсшей компоненты в народ. Концепция JSX - HTML код совмещённый с JavaScript - была не новой, но создателям React удалось донести её в понятной форме массе веб-разработчиков.

JSX по определению решает лишь задачу объединения структуры части документа с поведением этой самой части:

export function MyButton(props) {
return (
<button type="button" className="my-button" onClick={props.handleClick}>
Нажми меня
button>
);
}

Вопрос же стилей при этом всё ещё открыт и по-умолчанию классы наше всё. Впрочем, гибкость React и JSX позволяет решить вопрос разными способами.

Vue.js и CSS Modules

Ребята из Vue.js подошли к работе со стилями серьёзней. Давайте посмотрим на простой файловый компонент (.vue):

<template>
<p class="paragraph"> World!p>
template>

<script>
module.exports = {
data: function () {
return {
greeting: 'Hello',
};
},
};
script>

<style scoped>
.paragraph {
font-size: 2em;
text-align: center;
}
style>

Что бросается в глаза? Мне – style scoped. В экосистеме Vue.js используются CSS-модули на основе реализации от PostCSS. Область видимости стилей – текущий компонент, и собирается примерно в такое:

<style>
.paragraph[data-v-f3f3eg9] {
color: red;
}
style>

<template>
<p class="paragraph" data-v-f3f3eg9>hip>
template>

Аналогичный результат можно получить в любом современном фреймворке. В принципе, CSS-модули в совокупности с пользовательскими свойствами – это прекрасная альтернатива CJS.

Погоди, а с CSS-то что не так?

Ну вообще-то, много чего:

CSS is awesome
CSS is awesome

В сухом остатке, никто не мешает так продумать устройство ваших компонентов, чтобы не столкнуться с вышеописанными проблемами, но если половина из них может быть решена на корню иными инструментами – почему бы не попробовать? Встречайте...

CSS в JS

Do it with style

Мы и раньше могли обращаться к стилям DOM-узлов через свойство style, считывая и устанавливая значение свойства style связанного с узлом элемента:

el.style.color = 'white';
el.style.backgroundColor = 'cyan';

В итоге мы получаем генерированный атрибут style, который ещё называют инлайновыми стилями:

<div style="color: white; background-color: cyan">Hello Styles!div>

Минусы? Никаких вам псевдоклассов, никаких вам псевдоэлементов, никакой развитой анимации. Об отсутствии централизованного управления внешним видом множества элементов единовременно и объединения стилей (кажется, это худшая в мире ода CSS-классам) можно забыть. И об автоматических браузерных префиксах тоже. И о кешировании, естественно. И производительность, естественно, минимальная. Плюсы? Это бронебойно и точно работает (пока кто-нибудь !important не наставил).

Красивее как-то можно?

В JSX – можно:

const divStyle = {
color: 'white',
backgroundColor: 'cyan',
};

export function HelloStylesComponent() {
return <div style={divStyle}>Hello React Styles!div>;
}

Минусы, в целом, всё те же. Но тем не менее, в 2014 году об этом рассказали на одной из первых посвящённых реакту конференций, пригласив сообщество к дискуссии.

CSS-in-JS

Подход CJS предполагает, что описанные вами стили – неважно, будут они реализованы на CSS-подобном языке или через JS-объекты – применятся к вашим элементам через обычные классы, которые будут автоматически выстраиваться в нужную вам композицию. Вы получаете всё, что имели в CSS, плюс динамические возможности JavaScript.

В очень грубом приближении, при выполнении или сборке вашего кода будут автоматически созданы классы HTML-элементов и создастся тег