Предлагаем вашему вниманию перевод свежей ознакомительной статьи Cаши Грифа (Sacha Greif), соавтора книги «Discover Meteor», о специфике работы с библиотекой Styled components.
«СSS – странная штука. Его основам можно обучиться за 15 минут, но на то, чтобы найти хороший способ организации стилей, могут уйти годы.



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



Хаотичные листы стилей

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

Эта организационная работа была предоставлена таким методологиям как BEM, которые – хоть и полезны в этом отношении – все же не являются обязательными и не могут быть внедрены более универсально, на уровне языка или инструментов.

Новая волна CSS

Перемотаем на пару лет вперед: новая волна инструментов на базе JavaScript пытается решить эти трудности коренным образом, изменяя сам способ написания CSS.

Styled Components — это одна из таких библиотек, которая быстро привлекла к себе внимание масс из-за присущей ей смеси инноваций с привычностью. Поэтому, если вы используете React (а если нет, советую ознакомиться с моими статьями «Учебный план по JavaScript» и «Введение в React»), вам однозначно стоит взглянуть на эту альтернативу CSS.

Я недавно использовал Styled Components для редизайна личного сайта и хочу поделиться некоторыми моментами, которые я для себя выделил в процессе работы.

Components, Styled

Главное, что нужно иметь в виду при работе с Styled Components – название следует понимать практически буквально. Вы больше не стилизуете элементы HTML или компоненты на основании их класса или HTML-элемента:

<h1 className="title">Hello World</h1>
h1.title{
  font-size: 1.5em;
  color: purple;
}

Вместо этого вы определяете стилизованные компоненты, которые имеют свои собственные инкапсулированные стили. Далее вы свободно используете эти стили по всему коду:

import styled from 'styled-components';
const Title = styled.h1`
  font-size: 1.5em;
  color: purple;
`;
<Title>Hello World</Title>

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

Как сказал один из создателей Styled-components Макс Стойбер:

«Основная идея styled-components заключается в том, чтобы внедрить лучшие практики путем удаления мэппинга между стилями и компонентами».

Снижение сложности

Сначала это кажется нелогичным, ведь весь смысл использования CSS вместо того, чтобы стилизовать HTML-элементы напрямую (помните тэг font?), был в том, чтобы разделить стили и разметку путем введения классов как промежуточного слоя.

Но подобное разъединение также создает много сложностей, и на этом основании можно утверждать, что «настоящий» язык программирования — такой, как JavaScript, — больше подходит для того, чтобы исправить эти трудности, чем CSS.

Props, а не классы

Придерживаясь этой «не-классовой» философии, styled-components используют props вместо классов для всего, что касается кастомизации поведения компонента.

Итак, вместо этого:

<h1 className="title primary">Hello World</h1> // will be blue
h1.title{
  font-size: 1.5em;
  color: purple;
  
  &.primary{
    color: blue;
  }
}

Вы пишете это:

const Title = styled.h1`
  font-size: 1.5em;
  color: ${props => props.primary ? 'blue' : 'purple'};
`;
<Title primary>Hello World</Title> // will be blue

Как видите, styled-components позволяют вам очистить компоненты в React, вынося все, что связано с имплементацией CSS и HTML, за их пределы.

Тем не менее, styled-components CSS – это все-таки тоже CSS. Поэтому такой код тоже вполне приемлем, хотя и не совсем обычен:

const Title = styled.h1`
  font-size: 1.5em;
  color: purple;
  
  &.primary{
    color: blue;
  }
`;
<Title className="primary">Hello World</Title> // will be blue

Это одна из особенностей, которая делает styled-components очень простым для понимания инструментом: когда закрадываются сомнения, вы всегда можете вернуться к тому, что знаете!

Оговорки

Важно также отметить, что styled-components еще молодой проект и некоторые его фишки до сих пор не поддерживаются полностью. Например, если вы хотите повторить стиль родительского компонента в дочернем, на данный момент вам придется воспользоваться CSS классами (по крайней мере, пока не выйдет styled-components v2).

Также сейчас не существует «законного» способа, чтобы проводить предварительный рендеринг на вашем CSS на сервере, хотя это определенно можно сделать путем введения стилей вручную.

Кроме того, styled-components создают свои рандомные имена классов, что может сделать затруднительным использование «инструментов разработчика» вашего браузера для поиска первоначального определения места отображения ваших стилей.

Но явно обнадеживает то, что команда разработчиков в курсе данных проблем и активно работает, чтобы их исправить. Скоро выходит вторая версия, и я ее очень жду!

Узнайте больше

Моя цель в этой статье заключалась не в том, чтобы детально объяснить, как работают styled-components, а больше в том, чтобы дать маленькую наводку, чтобы вы сами могли решить, интересно ли вам это.

Если мне удалось пробудить в вас интерес, вот некоторые ресурсы, на которых вы можете узнать о styled-components больше:

  • Макс Стойбер недавно написал статью о смысле styled-components на ресурсе Smashing Magazine
  • Репозиторий styled-components сам по себе имеет обширную документацию
  • Статья Джеми Диксона показывает несколько плюсов использования styled-components
  • Если вы хотите узнать больше о том, как библиотека работает на практике, прочитайте вот эту статью от Макса.

И если вы вдруг захотите углубиться в тему, можете также посмотреть Glamor – другой взгляд на CSS новой волны!»
Поделиться с друзьями
-->

Комментарии (30)


  1. yury-dymov
    14.02.2017 13:44
    +1

    Спасибо за статью, мои 5 центов:


    1) styled-components хорошо поддерживают темы. То есть, в будущем можно будет легко использовать библиотеки компонентов на базе styled-components без необходимость лезть дальше документации, чтобы адаптировать их внешний вид согласно общему стилю сайта
    2) Поддержка SSR сейчас не идеальна
    3) На текущий момент стили считаются в runtime, что очевидно плохо, но ведется разработка babel-плагина, чтобы перенести это из runtime в сборку


    1. EverydayTools
      14.02.2017 13:50

      Спасибо за замечания!


    1. SPAHI4
      14.02.2017 18:29

      1) Хорошо, что в скором времени процент браузеров, поддерживающих css variables будет больше, и для этого не будут нужны такие извращения, как в посте


  1. MrCheater
    14.02.2017 13:48
    +2

    Больше всего удручает, что разработчиков таких либ не заботит Autoprefixer.
    Они в 2017ом опять предлагают писать руками такое:


    background: #1e5799; /* Old browsers */
    background: -moz-linear-gradient(top,  #1e5799 0%, #7db9e8 100%); /* FF3.6-15 */
    background: -webkit-linear-gradient(top,  #1e5799 0%,#7db9e8 100%); /* Chrome10-25,Safari5.1-6 */
    background: linear-gradient(to bottom,  #1e5799 0%,#7db9e8 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1e5799', endColorstr='#7db9e8',GradientType=0 ); /* IE6-9 */

    Замечу, что Autoprefixer тяжелый, в браузер его не стоит тащить. Он должен работать на этапе Compile Time


    1. FlyHighOnTheSky
      14.02.2017 15:17

      PS: Кто ищет шрифт со скриншота — это Operator
      https://geektimes.ru/post/271056/


    1. yury-dymov
      14.02.2017 15:23

      Откуда дровишки?


      https://github.com/styled-components/styled-components/#basic


      (The CSS rules are automatically vendor prefixed, so you don't have to think about it!)

      https://github.com/styled-components/styled-components/pull/286


      1. MrCheater
        14.02.2017 16:09
        +1

        Мне думается, что гонять вот это https://github.com/rofrischmann/inline-style-prefixer на каждое обновление props, еще хуже чем, то, что я предположил сначала.
        Этого вообще не должно быть в клиентском коде. Разработчики просто препроцессинг еще не осили


        1. yury-dymov
          14.02.2017 17:27
          +1

          babel-плагин, который сейчас пишут, должен исправить эту проблему. Мы с Максом обсуждали это в январе


          1. MrCheater
            14.02.2017 17:29
            +1

            Если всё получится, то будет круто


            1. yury-dymov
              14.02.2017 17:33

              Ага, я до конференции был очень скептически настроен, но меня смогли переубедить. Мигрировал с JSS и пока очень доволен. Не без проблем, конечно, но у меня есть ощущение, что скоро styled-components станут стандартом стилизации в Реакте.


        1. SPAHI4
          14.02.2017 18:30
          +1

          Настолько хуже, что эта либа не поддерживает Яндекс браузер, и прочие, инфы о которых нет на caniuse


    1. Aingis
      14.02.2017 16:49

      На самом деле сейчас префиксы нужны для считанных свойств, и это даже не градиенты. Даже для флексбоксов он нужен разве что, если вы заботитесь о старых IE или древних андроидов.


  1. justboris
    14.02.2017 16:48
    +5

    Честное название — Styled React Components.


    Библиотека работает поверх React и без него не запустится


  1. faiwer
    14.02.2017 17:59
    +7

    Хм. Читая уже которую статью по данному направлению, я всё никак не пойму ряд вещей:


    1. Ребята вообще задумываются о предварительной компиляции всего этого добра? Мне кажется на production не должно уходить ни строчки runtime-parse кода. А если задумываются, то какого лешего там делают props-методы? Как это потом вообще можно будет скомпилировать? Почему не строго декларативный подход? Судя по примерам из документации там целые сборные солянки из разных переменных. Бррр.
    2. CSS это каскадные таблицы стилей. Мне кажется, или на каскад забыли от слова "совсем"? Как из вышестоящего компонента повлиять на стили нижестоящих? Только модификаторами? Мне кажется это далеко не всегда верный подход.
    3. Более насущный вопрос ? а что насчёт пере-использования кода? Вроде как хотели убежать от того, что с течением времени, во всех проектах, копится мусорный CSS. Не получается ли здесь, что весь CSS мусорный, из-за тонн копипасты?
    4. Ни в одном примере не увидел листинга реального компонента больше 3-5 строк. Чтобы были разные теги, модификаторы внутренние всякие. Не оборачивать же каждый тег в отдельный компонент. В общем непонятно. Хорошо бы пример дельный.


    1. yury-dymov
      14.02.2017 20:19

      Ок, попробую объяснить


      1. Ребята пишут babel-плагин, который будет брать статические значения props (например из тем) и собирать css в процессе сборки, а не runtime
      2. Основная идея styled-components — разорвать связь между компонентом и его стилем/тегом. Влиять можно несколькими путями, в том числе импортом child и & + >.
      3. Реиспользуйте одни и те же компоненты. К слову те, которые называются по разному, но внутри одинаковые, будут компилиться в одно и тоже будут иметь одно и тоже имя (имя класса — это хеш от его свойств)
      4. Можно оборачивать, можно не оборачивать. Делайте как вам удобнее

      Ребята очень активно отвечают, если им написать. Вообще styled-components сначала вызывает ощущение WTF?! Но при ближайшем рассмотрении там все уже неплохо, а должно стать и вовсе отлично.


      1. faiwer
        14.02.2017 20:34
        +3

        Вообще styled-components сначала вызывает ощущение WTF?! Но при ближайшем рассмотрении там все уже неплохо

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


        1. yury-dymov
          15.02.2017 09:13
          -4

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


          Вот ваш github:
          https://github.com/faiwer


          Вот автора styled-components:
          https://github.com/mxstbr


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


          "Ваши ожидания — это ваши проблемы". Мне кажется очень точное высказывание для Open Source, ведь всегда есть пачка альтернатив для любого package, да и Pull Requests и форки никто не отменял.


          styled-components как и многие OpenSource проекты начались с простой идеи, которая постепенно развивается, обрастает фичами и эволюционирует. Недостатки я осветил выше, а так же и то, что скоро они уйдут в прошлое, ведь проект бурно развивается. Использовать или нет — решать вам.


          Костылей там нет, это больше на progressive по духу похоже.


          1. faiwer
            15.02.2017 09:30
            +5

            Не ожидал на хабре увидеть аргументы в стиле "сперва добейтесь". Юрий, не занимайтесь глупостями. Более того, в разговорах о технологиях, нужно говорить о фактах, о подходах, о преимуществах и недостатках. А не скатываться в личные выпады. У нас же ту не детский сад. Надеюсь.


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


            Критики в сторону CSS в JS, весьма обоснованной, было уже довольно много. С другой стороны и самих подходов вырисовалось уже несколько, насколько я знаю. В данном подходе, на мой взгляд, самым больным местом является то, что от декларативного подхода в стилях отказались. В пользу сиюминутных удобств. И сразу видно, что это создаст проблемы в будущем при предварительной компиляции. Придётся многое менять, возможно сами стили в рабочем коде, возможно весь подход в целом. Это уже сильно настораживает. Да и по остальным пунктам всё не так просто.


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


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


            В частности, вместо этого вашего комментария-наезда, я ожидал увидеть, внятное описание того, что произвольная логика при построении стилей не помешает предварительной компиляции в виду того, что планируется, что… положим, такие конструкции будут оформлены понятным babel-плагину образом, с явным указанием всех возможных зависимостей… и пр. Однако в ответ пошли какие-то войны гитхабов и значимости личностей. Бррр.


            1. yury-dymov
              15.02.2017 11:42
              +1

              Ок, вероятно я погорячился и во многом вы правы. Спасибо за столь развернутый ответ. Я все же уклонюсь от дискуссии о критике и наездах. Я полностью согласен с вами, что "нужно говорить о фактах, о подходах, о преимуществах и недостатках", и именно поэтому расстроился, когда увидел весьма спорное, но категоричное личное впечатление, основанное далеко не на фактах, а также то, что кто-то там что-то с какой-то стати должен. С другой стороны, вы правы, Хабр свободная площадка и каждый пишет то, что считает нужным.


              В styled-components by design другой подход к стилям. Многим он может показаться непривычным, особенно если вы много лет делали что-то вроде BEM'а и потому вызывает отторжение. JSS, aphrodite и еще пачка CSS-IN-JS библиотек говорит, что для реакте в целом старые подходы работают не очень, но новые сложиться еще не успели, что и говорит о массе реализаций и идей, которые сейчас эволюционируют. Мой опыт подтверждает, что идея, лежащая в основе styled-components годная.


              Возвращаясь к вашим вопросам:
              1) known issue, работа ведется
              2) в styled-components другая философия, которая может существенно отличаться от того, что вам привычно. Это не хорошо и не плохо само по себе, только время покажет, какой подход лучше. Делать вывод, что оно неверно, потому что непривычно можно, но чаще всего контрпродуктивно. Для реакт-приложений оно оказывается крайне удобным
              3) ответил
              4) best practices еще не сложились, но найти примеры можно, а еще лучше сходить в gitter и собрать объективный feedback https://gitter.im/styled-components/styled-components. Я использую в своем основном проекте, полет отличный, выложил бы сорсы в качестве примера, но NDA не позволяет


              P.s. статья не моя, однако автора библиотеки знаю лично. Я не имею ничего против объективной критики, но меня расстраивает, когда люди пишут откровенную ересь (это не про ваши вопросы, если что)


              1. faiwer
                15.02.2017 12:12
                +2

                Спс за ссылку. Вижу, что люди:


                • всё-таки используют собственные классы (руками задают className) и привычные каскады стилей (& > .someClass)
                • используют имена тегов в каскадах & > P
                • сборные солянки из строк, содержащих куски css-кода, опираясь на произвольную логику
                • используют какие-то композиции методов даже на уровне template-string handler-ов

                Что остаётся в сухом остатке? Судить сложно. Плюсы пока под вопросом (точнее их вес), а вот эти минусы сильно уж бьют:


                • Определённо будут проблемы с предварительной компиляцией. Решаться явно будут неким ручным указанием всех вариантов всех допустимых props-ов. Хорошо бы без комбинаторного взрыва обойтись. Ух.
                • Переусложнённой схемой в любом не тривиальном случае. Ребята УЖЕ дошли до композиции template-string handler-ов. Видимо иначе очень тесно. Что будет дальше, даже представить боюсь. Т.е. мы взяли сравнительно простой и, главное, декларативный язык CSS, сделали его императивным, отказались от части его плюшек, но зато поместили в тот же файл, что и JS-реализацию контрола.

                Не буду утверждать, что это путь в никуда. Но выглядит он как-минимум не привлекательно. В конце концов, опять же, на мой взгляд, если так хочется держать стили поближе к глазам, то достаточно просто чего-то в таком духе:


                injectCss(`stylus-scss-...-code`);

                Причём без использования интерполяции, конкатенации и пр. штук. Вся магия должна происходить на уровень реализации injectCss. Должно избавить от ряда проблем. И отлично прекомпилироваться.


                Вообще говоря, странные вещи творятся. С одной стороны мы едем в сторону haskel, с его монадами, чистыми функциями, да и вообще функциональным подходом. А с другой стороны изначально декларативные вещи превращаем если уж не в императивные, то явно перенасыщенные логикой и абстракциями. Какой-то лебедь-рак-щука получается.


                Я использую в своем основном проекте

                Т.е. у вас "на живую" стили парсятся? Прямо в runtime в production-е? Проект видимо не очень прихотливый. Или стилей мало? А ведь это дело даже не кешируется.


                1. yury-dymov
                  15.02.2017 12:36

                  Воооот. Теперь это похоже на конструктивный диалог.


                  Часть про "вижу, что люди" я не стану комментировать. Стрелять себе в ногу можно всегда, но это же не повод отказаться от пистолета или ноги. Вместо этого готов обсуждать конкретные юз-кейсы.


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


                  Не получится, если мы вызываем компонент Title и передаем в color переменную


                  const Title = styled.h1`
                    font-size: 1.5em;
                    color: ${props => props.color};
                  `;

                  Получится, так как статический анализ даст нам все варианты


                  const Title = styled.h1`
                    font-size: 1.5em;
                    color: ${props => props.theme[`header${capitalize(props.type)}`] || 'someDefaultColor'};
                  `;
                  
                  const theme = {
                    headerPrimary: 'blue',
                    headerSuccess: 'green',
                    ...
                  }

                  Но в этом нет проблемы. Пишите определенным образом — получайте Performance, хотите больше гибкости — ок, но придется чем-то пожертвовать. В реакте комбинируют классы и inline-стили и ничего — полет нормальный.


                  Performance сейчас очевидно хуже, чем при чистом CSS, JSS, etc. Но проблем это мне не создает. Вообще обсуждать performance без цифр достаточно тяжело. Допускаю, что для ряда проектов на текущем этапе styled-components не подойдут и это нормально.


              1. vintage
                20.02.2017 05:22

                Сколько над вашим проектом работает фронтенд-разработчиков? А сколько верстальщиков? Есть ли у вас архитектор с хотя бы 5 летним стажем разработки? Сколько лет вашему проекту?


                Без ответа на все эти вопросы сложно судить о "нормальности" полёта. Пока что я вижу большие сложности с поддержкой этой лапши. Что, впрочем, свойственно реактовому стеку.


  1. 14types
    15.02.2017 05:33

    С точки зрения SEO, поисковики поймут, что — это то же самое, что и ?


    1. 14types
      15.02.2017 08:39

      Тег title=тегу h1. (затёрло фильтром)


    1. yury-dymov
      15.02.2017 08:56
      +1

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


      <button class="btn btn-primary">Push Me</button>


      будет


      <button class="aQx79Ad">Push Me</button>


  1. tenbits
    15.02.2017 12:02
    +2

    Пример абсолютно не удачный, и мне жалко тех, кто так пишет:


    const Title = styled.h1`
      font-size: 1.5em;
      color: ${props => props.primary ? 'blue' : 'purple'};
    `;

    Просто потому, что это его максимум. Он не расширяеться никак. Вот нам нужны ещё стейты danger, success, как нам условие переписать?
    Немогли бы вы привести пример который хорошо и удобно масштабируется?


    1. yury-dymov
      15.02.2017 12:11

      Это точно :) Можно вот так


      const Title = styled.h1`
        font-size: 1.5em;
        color: ${props => props.theme[`header${capitalize(props.type)}`] || 'someDefaultColor'};
      `;

      theme.js


      const theme = {
        headerPrimary: 'blue',
        headerSuccess: 'green',
        ...
      }

      Usage:


      <Title type="success">Hello World</Title>

      Еще очень здорово все это во Flow обернуть, тогда можно определить тип для prop Type


      type TitleType = "primary" | "success";

      Теперь, если мы напишем, например, "suces" вместо "success", то IDE сразу же сообщит об ошибке


      1. tenbits
        15.02.2017 12:44

        Ясно, спасибо. Немного подправлю и этот пример, потому что — интерполяция в интерполяции, а там ещё и трансформация?! :) Кхэ кхэ ...


        styled.h1`
          font-size: 1.5em;
          color: ${props => props.theme.colors[props.type] || 'someDefaultColor'};
        `;

        И вижу, что мы всё же работаем с каждым конкретным свойством. Если и font size зависит от type, выносите это тоже в тему? Не раздуваются ли темы тогда, что в них слишком много стейтов для разных компонент? Можно как-то именно расширять стили? Пример из вакуума:


        const Title = styled.h1`
          font-size: 1.5em;
          color: someDefaultColor;
        `;
        
        Title = when(props.primary, Title)`
            color: ${ theme.colors.green }
            font-weight: bold;
            font-size: 1.7em;
        `;


        1. yury-dymov
          15.02.2017 12:56

          Ну, если будем последовательными props => props.theme.headers.h1.colors[props.type], но да, такой вариант мне тоже нравится.


          Можно как-то именно расширять стили?

          Да, можно через import


          https://github.com/styled-components/styled-components#overriding-component-styles


  1. vintage
    20.02.2017 05:05
    +1

    Эта организационная работа была предоставлена таким методологиям как BEM, которые – хоть и полезны в этом отношении – все же не являются обязательными и не могут быть внедрены более универсально, на уровне языка или инструментов.

    Могут. Получается весьма удобно. Другое дело, что к реакту с его кривой архитектурой прикрутить автоматическую генерацию бем-токенов сложновато.