От автора: CSS никуда не девается. Множество проектов предпочитают стилизовать документы через JS по недопониманию. В этой статье собраны распространенные мифы об использовании в JavaScript CSS и существующие решения этих проблем.
Статья не нацелена навредить определенным проектам или людям. «CSS в JS» я определяю, как использование styled-components. Сейчас это тренд стилизации компонентов в React.
История CSS и JS
Каскадные таблицы стилей (CSS) были созданы для описания представления документа, написанного с помощью языка разметки. JS был создан как «связующий язык» для связки компонентов, таких как изображения и плагины. Шли годы, JS рос и мутировал под новые примеры использования.
Появление термина Ajax (2005) – важная веха. Именно тогда библиотеки типа Prototype, jQuery, MooTools привлекли огромные сообщества к совместному решению проблем получения данных в фоновом режиме и неоднородности работы кода в разных браузерах. Так была создана новая проблема – как управлять всеми данными?
Перенесемся в 2010. Backbone.js стал стандартом управления состоянием приложения. Почти сразу после этого появились React и Flux. Так началась эра одностраничных приложений (SPA) – приложений, состоящих из компонентов.
Что же с CSS?
В документации к styled-components:
«Проблема чистого CSS заключается в том, что он создавался в эру, когда веб состоял из документов. В 1993 году был создан интернет для обмена по большей части научными документами, и CSS был представлен, как решение стилизации этих документов. Сегодня мы создаем богатые, интерактивные, user-facing приложения, и CSS не предназначен для них.»
Не соглашусь.
CSS эволюционировал под требования современных UI. Количество новых функций, появившихся за последние 10 лет, выходит за рамки разумного (псевдоклассы, псевдоэлементы, CSS переменные, медиа запросы, кейфреймы, комбинаторы, колонки, flex, сетки, вычисляемые значения…).
С точки зрения UI компонент – это изолированный фрагмент документа (button — компонент). CSS придумали для стилизации документов, которые полностью состоят из компонентов. Так в чем же проблема?
Как говорится «используйте правильный инструмент для работы».
Стилизованные компоненты
styled-components позволяют писать CSS в JS с помощью шаблонных литералов из тегов. Они удаляют связь между компонентами и стилями – компонент превращается в стилевую конструкцию низкого уровня.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import React from 'react'; import styled from 'styled-components'; // Create a <Title> react component that renders an <h1> which is // centered, palevioletred and sized at 1.5em const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; `; // Create a <Wrapper> react component that renders a <section> with // some padding and a papayawhip background const Wrapper = styled.section` padding: 4em; background: papayawhip; `; // Use them like any other React component – except they're styled! <Wrapper> <Title>Hello World, this is my first styled component!</Title> </Wrapper> |
Результат:
styled-components сейчас в тренде. С их помощью стилизуют компоненты в React.
Проясним кое-что: styled-components – это просто абстракция высокого уровня поверх CSS. Она лишь парсит ваш CSS, написанный в JS, и создает JSX элементы, связанные с CSS.
Мне не нравится этот ажиотаж, вокруг него много недоразумений.
Я спросил на IRC, Reddit и Discord, почему люди используют styled-components. Я собрал список общих ответов, почему люди предпочитают styled-components. Я называю их мифами.
Миф №1: они решают проблему глобального пространства имен и конфликты стилизации
Я называю эту причину мифом, потому что она звучит так, будто бы эта проблема не была решена ранее. CSS Modules, Shadow DOM и бесконечное множество систем именования (BEM) решили эту проблему намного раньше.
styled-components (как CSS модули) снимает с людей ответственность за именование. Люди делают ошибки, компьютеры делают их меньше.
Это не самая хорошая причина использовать styled-components.
Миф №2: styled-components сокращает код
Часто предлагают такой пример:
1 2 |
<TicketName></TicketName> <div className={styles.ticketName}></div> |
Во-первых, это совсем не важно. Различия незначительны.
Во-вторых, это не правда. Количество символов зависит от имени стиля.
1 2 |
<TinyBitLongerStyleName></TinyBitLongerStyleName> <div className={styles.longerStyleName}></div> |
То же самое применимо к созданию стилей. Это вы увидите ниже в статье (миф №5: так легче стилизовать компоненты по условию). styled-components выигрывает в краткости только при использовании самых базовых компонентов.
Миф №3: styled components заставляет вас больше задумываться о семантике
Неправильный сам посыл. Стили и семантика представляют разные проблемы и требуют разных решений. Процитируем Adam Morse (mrmrs):
«Семантика контента ВООБЩЕ НЕ СВЯЗАНА С ВИЗУАЛЬНЫМИ СТИЛЯМИ. Когда в детстве я играл в лего, я никогда не думал «этот блок для двигателя». Я думал «о, круто, это блок 1×4, я могу сделать с ним, что угодно». Неважно что я собирал, будь-то подводная база для подводников или самолет, я точно знал, как пользоваться лего.»
Все же давайте предположим, что связь есть.
Пример:
1 2 3 4 5 6 |
<PersonList> <PersonListItem> <PersonFirstName>Foo</PersonFirstName> <PersonLastName>Bar</PersonLastName> </PersonListItem> </PersonList> |
Семантика – использование правильных тегов для написания разметки. Вы знаете, какие HTML теги будут рендерить эти компоненты? Не, не знаете.
Сравните с:
1 2 3 4 5 6 |
<ol> <li> <span className={styles.firstName}>Foo</span> <span className={styles.lastName}>Bar</span> </li> </ol> |
Миф №4: так легче расширять стили
В v1 вы можете расширять стили через конструкцию styled(StyledComponent), в v2 представили метод extend для расширения существующих стилей.
1 2 3 4 5 6 |
const Button = styled.button` padding: 10px; `; const TomatoButton = Button.extend` color: #f00; `; |
Отлично. Но это моно делать и в CSS (с помощью CSS module composition или унаследованного Sass миксина @extend).
1 2 3 4 5 6 |
button { padding: 10px; } button.tomato-button { color: #f00; } |
Что легче сделать в JS?
Миф №5: так легче стилизовать компоненты по условию
Суть в том, что вы можете стилизовать компоненты по их свойствам.
1 2 3 |
<Button primary /> <Button secondary /> <Button primary active={true} /> |
В мире React это много значит. По сути ведь, поведение компонентов контролируется через свойства. Можно ли прикреплять значения свойств напрямую в стили, есть ли в этом смысл? Возможно. Но давайте посмотрим на компонент:
1 2 3 4 5 |
styled.Button` background: ${props => props.primary ? '#f00' : props.secondary ? '#0f0' : '#00f'}; color: ${props => props.primary ? '#fff' : props.secondary ? '#fff' : '#000'}; opacity: ${props => props.active ? 1 : 0}; `; |
Создание стилей по условию через JS дает вам большую силу. Но такие стили также сложнее интерпретировать. Сравните с:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
button { background: #00f; opacity: 0; color: #000; } button.primary, button.seconary { color: #fff; } button.primary { background: #f00; } button.secondary { background: #0f0; } button.active { opacity: 1; } |
CSS короче (229 символов против 222) и проще читается (кому как). Более того, в CSS можно еще сильнее укоротить и сгруппировать код через препроцессор.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
button { background: #00f; opacity: 0; color: #000; &.primary, &.seconary { color: #fff; } &.primary { background: #f00; } &.secondary { background: #0f0; } &.active { opacity: 1; } } |
Миф №6: стилизованные компоненты лучше организуют код
Пара людей мне заявили, что им нравятся styled-components, потому что они позволяют писать стили и JS в одном файле.
Я понимаю, что несколько файлов для одного компонента утомляет, однако писать стили и разметку в одном файле – ужасная идея. Это не только усложняет контроль версий, но вам придется долго крутить код на любом компоненте сложнее кнопки.
Если вам обязательно необходимо писать CSS и JS в одном файле, используйте css-literal-loader. Этот инструмент позволяет вытаскивать CSS в момент билда с помощью extract-text-webpack-plugin и использовать стандартные настройки загрузки для обработки CSS.
Миф №7: это улучшает DX. Инструменты потрясающие!
Вы, скорее всего, не использовали styled-components.
Когда со стилями что-то идет не так, ломается все приложение с большим логом ошибки. Сравните с CSS, где ошибка стиля просто неправильно отображает элемент.
У элементов нет распознаваемых className, поэтому в дебаге вам остается лишь переключаться между деревом элементов React и деревом DevTools DOM (в v2 это можно решить с помощью babel-plugin-styled-components).
Нет линтинга (сейчас разрабатывается stylelint plugin).
Невалидные стили просто игнорируются (clear: both; float left; color: #f00; не вернут ошибок и предупреждений; у меня ушло 15 минут в дебаге на прохождение трассировки стека, даже после прочтения исходного кода styled-components). Только после того, как я скопировал код в чат и попросил помочь, кто-то указал мне, что отсутствует :. Вы же заметили, так ведь?)
Подсветка синтаксиса, автозавершение кода и другие тонкости поддерживаются в нескольких IDE. Если вы работаете с правительственными и финансовыми агентствами, есть вероятность, что Atom IDE вам не подойдет.
Миф №8: чем меньше размер, тем больше производительность
Как бы то ни было, styled-components нельзя извлечь в статичный CSS файл (например, //github.com/webpack-contrib/extract-text-webpack-plugin).То есть браузер не начнет интерпретацию стилей, пока styled-components не распарсит их и не добавит в DOM.
Отсутствие файлов говорит о том, что нельзя кэшировать отдельный CSS и JS
Все стилизованные компоненты оборачиваются в дополнительный HoC. Удар по производительности незначительный. По этой же причине я перестал разрабатывать //github.com/gajus/react-css-modules (и создал //github.com/gajus/babel-plugin-react-css-modules).
Из-за HoC рендер на сервере приводит к увеличению разметки в документе
Не заставляйте меня запускать компоненты анимации с помощью динамических значений стилей или кейфреймов
Миф №9: позволяет разрабатывать адаптивные компоненты
Говорится о возможности стилизовать компонент по его окружению, т.е. размерам родительского контейнера, количеству дочерних элементов и т.д.
Во-первых, styled-components вообще с этим не связаны. Это за пределами проекта. Лучше устанавливайте значения style прямо на компонент.
Элементные запросы – интересная проблема, набирающая обороты в обсуждении CSS, в основном в проекте EQCSS и его копиях. Элементные запросы похожи на @media запросы по синтаксису, за исключением того, что элементные запросы работают с определенными элементами.
1 |
@element {selector} and {condition} [ and {condition} ]* { {css} } |
{selector} – CSS селектор, направленный на один или более элементов. Пример — #id или .class
{condition} – состоит из измерения и значения
{css} – может содержать любые валидные CSS правила. Пример #id div { color: red }
Элементные запросы позволяют стилизовать элементы по условиям min-width, max-width, min-height, max-height, min-characters, max-characters, min-children, max-children, min-lines, max-lines, min-scroll-x, max-scoll-x и т.д. (см. //elementqueries.com/).
Когда-нибудь вариация EQCSS появится в существующей CSS спецификации (скрестим пальцы).
Но постойте!
Почти все (если не все) описанное можно решить. Сообществом, изменениями в React или в styled-components. Но в чем смысл? CSS уже везде поддерживается, у него огромное сообщество, и он просто работает.
Суть статьи в том, чтобы отпугнуть читателей от использования CSS и JS или от использования styled-components. У стилизации через styled-components есть отличный пример использования: улучшенная кроссплатформенная поддержка. Не используйте его по неправильным причинам.
Так что же сегодня использовать?
Использовать Shadow DOM v1 (51% поддержки) слишком рано. Используйте CSS с системами именования (рекомендую BEM). Если беспокоитесь о коллизиях с именами классов (или слишком ленивы, чтобы использовать BEM), используйте CSS modules. Если разрабатываете для React, попробуйте babel-plugin-react-css-modules. Если разрабатываете для нативного React, возьмите styled-components.
Автор: Gajus Kuizinas
Источник: //medium.com/
Редакция: Команда webformyself.