CSS-in-JS (или «встроенный CSS») — это когда CSS определяется внутри исходного файла JavaScript. Пакеты обычно используют объекты JavaScript для определения стилей с парами ключ-значение, определяющими свойства. Или они используют встроенные строки шаблона, содержащие исходный код CSS. В свою очередь, они могут либо возвращать встроенные свойства (например, свойство стиля в HTML), либо становиться обычными классами CSS внутри тега стиля или ссылки.

// either JS objects
var rule = css({
  color: 'blue'
})

// or template strings
var rule = css`
  :host { color: blue }
`

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

Встроенные стили и классы

Я не сторонник дополнительных свойств в своих элементах DOM. Я помню, как почувствовал себя сбитым с толку, когда впервые посмотрел на код Angular и увидел целую кучу ng-* вещей в HTML. Это было не то, к чему я привык, когда впервые изучал HTML, и мне было непонятно, зачем оно здесь. Мне нравится, когда мой HTML внутри браузера читаем, и я не чувствую, что встроенные стили помогают в этом.

<!-- inline CSS -->
<button class="Button" style="font-family: inherit; font-size: 14px; font-weight: 600; line-height: 16px; min-height: 32px; text-decoration: none; display: inline-block; margin: 0px; padding: 8px 16px; cursor: pointer; border: 0px; border-radius: 2px; color: rgb(255, 255, 255); background-color: rgb(0, 140, 239); box-sizing: border-box;">
  Hello
</button>

<!-- traditional classes -->
<button class="namespace-button">
  Hello
</button>

<!-- micro classes -->
<button class="f3 b lh-title h2 no-underline pointer br1 b--none white bg-blue">
  Hello
</button>

Я большой поклонник микроклассов, потому что они передают много информации в нескольких символах. Как и в случае с командами Unix, может потребоваться начальная кривая обучения, но как только вы преодолеете трудности, создание новых стилей станет быстрее, чем любой другой подход. Мне не нравятся традиционные классы, потому что они дают очень мало информации по сравнению с ними.

Shadow DOM и селектор :host

Sheetify позволяет определять стили на месте, используя помеченные строки шаблона. Классы внутри него определены глобально и поэтому могут конфликтовать. Это приводит к необходимости разрешать классы с ограниченной областью действия, которые не могут конфликтовать.

Скажем, мы хотим дважды определить класс «.disabled». Простое определение не сработает:

/* all instances of .disabled will turn gray */
.disabled { color: black }
.disabled { color: gray }

В старые времена мы использовали область видимости этих классов с помощью селектора ›, чтобы убедиться, что они происходят из правильного родительского элемента. Но оказалось, что это не только медленно (селекторы оцениваются справа налево), но и неэффективно для программистов. Всякий раз, когда элемент в DOM перемещался, это означало, что CSS должен отражать эту структуру. Итак, БЭМ был придуман, чтобы решить эту проблему:

/* BEM and friends */
.firstElement--disabled { color: black }
.secondElement--disabled { color: gray }

Но назвать вещи трудно. Замечательные ребята из css-modules сообразили, что вместо ручного именования пространств имен с помощью хэшей так же удобно и быстро работать.

Для Sheetify мы решили перегрузить селектор :host, чтобы он был преобразован в хэш стилей. Это означает, что каждый раз, когда вам нужно создать пространство имен, селектор :host позаботится о том, чтобы он не конфликтовал ни с одним селектором в вашем приложении. Селектор :host происходит из мира веб-компонентов, чтобы стилизовать элемент, на котором монтируется веб-компонент. Или что-то вроде того.

var css = require('sheetify')
var html = require('bel')

var prefix = css`
  :host { color: blue}
`
var el = html`
  <button class=${prefix}>click me</button>
`

Время выполнения против времени компиляции

CSS-in-JS можно выполнять либо во время выполнения, либо во время компиляции. Для Sheetify мы решили поддерживать только этап компиляции, поэтому нам не нужно поставлять среду выполнения. Среды выполнения всегда будут иметь накладные расходы, которые всегда медленнее, чем простой CSS.

Среды выполнения позволяют выполнять классные динамические вещи с CSS, но мы решили, что, поскольку мы хотим разрешить листифай импортировать пакеты из npm, это должен быть этап компиляции. Это связано с тем, что функция Node require() не позволяет импортировать файлы, отличные от JSON или .js, и мы не хотим перегружать require(), чтобы магическим образом поддерживать ее. Кроме того, наличие как шага компиляции, так и среды выполнения, вероятно, было плохим компромиссом.

Сочетание стилей

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

Далее следует создание компонентов. Компоненты — это круто, потому что их легко использовать повторно, а изменения в элементе распространяются на весь проект.

Совместное использование стилей (или тем) также может осуществляться путем совместного использования строк из нескольких классов и добавления их к элементам. Вместе с архитектурой, основанной на компонентах, становится легко создавать согласованные компоненты во всем приложении.

А переменные?

Спецификация переменной CSS — это круто, и в целом ее должно быть достаточно. До сих пор у меня не было потребности в чем-то более сложном, и я с трудом представляю себе время, когда мне это понадобится. Ура.

CSS идет вперед

Я не чувствую, что многое еще нужно изменить в Sheetify; поскольку мы не выпускаем среду выполнения, мы уже настолько быстры, насколько это возможно. Sheetify также включает в себя систему плагинов, которую можно использовать для классных вещей, таких как классы дедупликации, классы обрезки и другие классные вещи.

Мы начали работу над проектом под названием базовые элементы, который предоставляет автономные элементы, которые заботятся о сложных вещах, таких как стилизация индикаторов выполнения, модальных окон и тому подобного. Возможно, мы, наконец, прошли полный круг и теперь можем создать модульный Bootstrap. Я надеюсь, что скоро мы все будем создавать элементы, которые можно будет встроить в любой фреймворк и которые будут иметь стили, никогда не конфликтующие. Я думаю, это было бы очень круто.

И это все. Это некоторые из моих мыслей о CSS-in-JS, которые, надеюсь, объясняют некоторые решения, принятые при создании sheetify. Ваше здоровье!

Кросс-пост с https://github.com/yoshuawuyts/writing