Первая статья здесь

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

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

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

Важно отметить, что при написании этой статьи я опирался только на свой большой опыт разработки на Svelte. Пару лет назад я пытался прикрутить BEM к React, и мне не удалось это сделать, что меня сильно удивило. Там не было возможностей это сделать так, чтобы и изоляция стилей работала, и можно было менять стили из родительского компонента не ломая все остальные компоненты, и стили были в виде CSS, и классы в HTML можно было указать просто текстом, ровно в таком же виде как и в CSS. На Svelte все это делать легко, там практически пишешь обычный HTML + CSS, потом разбавляешь его логикой, а Svelte делает всю магию по гибкой изоляции стилей. Возможно в React и невозможно по-человечески использовать BEM, и Tailwind спасает ситуацию со стилями, но большого опыта у меня в этом нет.

1. Исходные данные

1.1. Общие для всех архитектур инструменты разработки:
1.1.1) PostCSS
1.1.2) Современный UI фреймворк: React / Vue / Svelte / ...

1.2. Архитектуры:
1.2.1) Ванильный CSS + BEM
1.2.2) Tailwind (православный) со стилями в HTML и без активного использования @apply

1.3. Возможные задачи:
1.3.1) сверстать все страницы с нуля
1.3.2) создать несколько компонентов с разной внутренней логикой, но с очень похожим стилем
1.3.3) создать несколько компонентов с той же логикой, но с небольшими изменениями в стиле
1.3.4) создать несколько страниц с небольшими изменениями в стилях некоторых элементов
1.3.5) создать темы оформления с как можно большими возможностями в изменении стилей элементов
1.3.6) точечные исправления/изменения в верстке
1.3.7) изменить стиль элементов определенной категории

1.4. Возможные изменения в элементах:
1.4.1) изменить все мелкие отступы, цвета, шрифты
1.4.2) скрыть пару элементов
1.4.3) стилизовать некоторые элементы по-другому
1.4.4) поменять градиент или картинку

1.5. Влияющие факторы:
1.5.1) весь проект со строгим гайдлайном
1.5.2) весь проект без строгого гайдлайна
1.5.3) требование Pixel Perfect
1.5.4) 1-2 разработчика на проекте
1.5.5) на проекте несколько человек или большая команда
1.5.6) простой одностраничный сайт или лэндинг
1.5.7) большой сайт
1.5.8) фиксированный проект без будущих изменений
1.5.9) непредсказуемость развития, т.е нельзя заранее сказать, какие изменения и в каких элементах могут произойти. Могут быть только общие предсказания, типа отсутствия тем оформления.

2. Анализ

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

2.1 Сверстать все страницы с нуля

  • Если весь проект со строгим гайдлайном, то проще будет работать с Tailwind. Если нет строгого гайдлайна, то его можно сделать строгим и верстать примерно.

  • При верстке по макету из Figma без строгого гайдлайна проще копировать чистый CSS, чем подбирать классы Tailwind на глаз и по памяти.

  • Pixel Perfect легче делать на ванильном CSS, так как DevTools позволяет легко подгонять размеры и отступы, а затем копировать CSS в код.

  • Если над проектом работают несколько человек, то при использовании Tailwind им не нужно разбираться в общих CSS-классах других разработчиков. Общие классы в этом случае сложнее изменять, что будет время от времени происходить в ходе разработки.

2.2 Создать несколько компонентов с разной внутренней логикой, но с очень похожим стилем

  • Для Tailwind нужно создавать компоненты без общих стилей. Такое легко создать, но сложно менять, если компонентов много.

  • На ванильном CSS можно сделать общие стили и переиспользовать их в разных компонентах. Такие стили легко менять сразу для всех компонентов.

2.3 Создать несколько компонентов с той же логикой, но с небольшими изменениями в стиле

  • В Tailwind изменения в стилях нужно интегрировать в логику, меняя классы в HTML в зависимости от параметров. Параметр компонента - это как модификатор в BEM. Я боюсь представить во что превратиться компонент, если у него будет много различных модификаторов, вносимые изменения которых раскиданы по всему HTML.

  • Ванильный CSS хорошо справляется с этим. Можно менять стиль компонента так же через параметры, но подменяя в коде только один BEM-класс модификатор для контейнера. А стиль модификатора настраивать отдельно в одном месте. Кроме того, ванильный CSS позволяет изменять стили вложенных компонентов без изменения кода этих компонентов.

  • Многие стили можно менять через CSS-переменные. Возможности Tailwind здесь ограничены. Чтобы точно указать, какие стили каких элементов можно менять через переменные, придется использовать ванильный CSS.

2.4 Создать несколько страниц с небольшими изменениями в стилях некоторых элементов

  • В Tailwind придется прописать классы с уникальными именами для каждого изменяемого элемента и использовать ванильный CSS для их модификации извне.

  • Если использовать BEM, то все имена элементов уже прописаны, так что для создания новой страницы с немного другим стилем не придется менять вложенные компоненты. Достаточно написать CSS изменяемых стилей в одном месте в корневом компоненте страницы.

2.5 Создать темы оформления с как можно большими возможностями в изменении стилей элементов

  • Tailwind дает ограниченные возможности в создании тем оформления. Если менять что-то, то сразу все на странице: все цвета, все отступы. CSS-переменные внедрять тоже проблематично без ванильного CSS. Создавать классы-утилиты с CSS-переменными можно только глобально, а не для конкретного компонента.

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

2.6 Точечные исправления/изменения в верстке

  • Элементы Tailwind сложно искать. Нельзя так просто по атрибуту class узнать, в каком месте проекта находится соответствующий код. Еще хуже, если таких мест несколько и нужно изменить их все. Сложно также понять, какую роль играет определенный элемент в коде: это контейнер, обертка, независимый блок и т.д. Рефакторинг из-за этого крайне затруднен. Закон Миллера говорит о том, что человек может удерживать во внимании одновременно 5-7 сущностей. Чтобы распознать роль определенного элемента, разработчик должен заполнить весь свой "кошелек внимания", т.к. нужно проанализировать все классы элемента, чтобы понять его роль. Еще сложнее, когда в коде несколько элементов с одной ролью и нужно найти их все.

  • Элементы BEM искать очень легко, если, конечно, BEM-классы написаны в CSS в таком же виде, как в HTML. Нужно просто скопировать имя класса и найти его в проекте. Очень легко найти все места, где этот класс используется. Чтобы изменить стиль нескольких элементов одного вида, нужно отредактировать только один стиль, если, конечно, в верстке соблюдена хоть какая-то структура. А BEM заставляет разработчика думать о структуре верстки. Очень легко по названию класса понять, какую роль имеет элемент. Рефакторить такой код не сложно. "Кошелек Миллера" может вмещать сразу несколько элементов. Также легко поиском найти в файле все элементы с определенной ролью.

2.7 Изменить стиль элементов определенной категории

  • Очень сложно в Tailwind изменить стиль элементов определенной категории, так как Tailwind не предполагает продумывание структуры верстки. Нет структуры - ищи все вручную. Если элементов много, придется каждый искать вручную.

  • BEM заставляет продумывать структуру верстки. Поэтому искать элементы определенной категории можно простым поиском по именам классов.

3. Предварительные итоги

3.1) Сверстать все страницы с нуля

  • Нет строгого гайдлайна + Pixel Perfect => строго ванильный CSS

  • Есть строгий гайдлайн => Tailwind будет удобнее

3.2) Создать несколько компонентов с разной внутренней логикой, но с очень похожим стилем

  • Большой разницы нет

3.3) Создать несколько компонентов с той же логикой, но с небольшими изменениями в стиле

  • Tailwind здесь имеет сильные ограничения и заставляет стиль компонента смешивать с другой логикой

  • Ванильный CSS в этом плане очень гибкий

3.4) Создать несколько страниц с небольшими изменениями в стилях некоторых элементов

  • Tailwind использовать нельзя, только ванильный CSS

3.5) Создать темы оформления с как можно большими возможностями в изменении стилей элементов

  • Tailwind можно использовать для тем оформления, но с большими ограничениями

  • Ванильный CSS практически не имеет ограничений на темы оформления

3.6) Точечные исправления/изменения в верстке

  • Tailwind сложно читать и редактировать.

  • При использовании BEM и именовании всех элементов искать их очень легко, а если в HTML не писать Tailwind-классы, то ориентироваться по коду еще легче.

3.7) Изменить стиль элементов определенной категории

  • В Tailwind верстке это делать очень сложно, особенно если элементов много в разных частях сайта.

  • BEM позволяет легко найти все элементы простым поиском по именам классов.

4. Мои выводы

В плане гибкости разработки Tailwind сильно уступает ванильному CSS + BEM. Некоторые считают, что BEM и Tailwind не противоречат друг другу. Да, их можно технически смешивать вместе, но это разные архитектуры. Смешивая их вместе, мы получаем как плюсы от обоих архитектур, так и минусы. Смешивание разных подходов может привести к тому, что код может превратиться в свалку. Такая смешанная архитектура должна быть хорошо продумана. Но я пока не видел НИ ОДНОЙ статьи по архитектуре Tailwind, чего уж говорить об архитектуре Tailwind + BEM. Зато полно статей по архитектуре для ванильного CSS.
Пока я не понимаю, как на Tailwind решать задачи: 4, 5, 6, 7. Эти минусы Tailwind для меня критичны, а минусы BEM - нет.


Пишите, если есть что сказать. Особенно интересно, как вы решаете задачи, которые я здесь описал, с помощью Tailwind. Может, я еще не знаю каких-то секретов разработки на Tailwind.

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


  1. DreamShaded
    18.11.2023 11:16
    -1

    Очень хорошо, спасибо!


  1. 19Zb84
    18.11.2023 11:16

    А в будущем не хотите сделать сравнение BEM и Web Components ?


    1. NikolayMakhonin Автор
      18.11.2023 11:16

      У меня очень мало опыта в Web Components, и я не знаю как они помогут решить задачи, которые я описал. Они ведь нужны для полной изоляции HTML от внешних стилей. А если они полностью изолированы, то их нельзя изменить из родительского компонента обычным CSS, что уменьшает гибкость.


  1. IvanGanev
    18.11.2023 11:16
    +2

    Почти все ваши сравнения про гибкость. С вашей точки зрения более гибкий инструмент лучше. Ну и понятно что ванильный css будет во всем гибче, ну то есть css в принципе не может не быть гибче, теилвайнд написан на тех же самых css стилях, он ограничивает возможности верстальщика. Его преимущество не в гибкости, а в скорости написания.


    1. NikolayMakhonin Автор
      18.11.2023 11:16

      Насколько быстрее верстать на Tailwind?
      Я думаю здесь стоит уточнить, что Tailwind быстрее только при верстке с нуля, но не когда ее нужно изменить или связать изменения с логикой. Гибкость ведь тоже увеличивает скорость разработки, но на поздних этапах развития проекта.
      Правильно ли я понял, что Tailwind больше подходит для фиксированных проектов: сверстал и забыл?


      1. IvanGanev
        18.11.2023 11:16

        Гибкость снижает скорость разработки, это вечный компромисс. Если бы это было не так то все бы писали на чистом js, и не было бы ни реакта, ни vue, ведь нет ничего гибче чистого js.


        1. Gruzchick
          18.11.2023 11:16

          Реакт нужен, чтобы с DOM работать (вернее не работать ????), когда делаешь проект на реакт то пишешь на чистом js, разве нет?


        1. antonkrechetov
          18.11.2023 11:16

          Гибкость снижает скорость разработки, это вечный компромисс. Если бы это было не так то все бы писали на чистом js, и не было бы ни реакта, ни vue, ведь нет ничего гибче чистого js.

          Эта скорость появляется только из-за переиспользования кода: зачем писать с нуля что-то для чего есть библиотека. С императивными языками это работает. Но о каком переиспользовании речь в случае вёрстки? Дают вам дизайн сайта в jpg и говорят "сверстай" - что ускорит использование tailwind?


  1. flygrounder
    18.11.2023 11:16
    +2

    Основные "минусы" Tailwind можно подытожить как сложность переиспользования в разных контекстах. Появись эта библиотека, когда в моде были Bootstrap и JQuery, её бы никто в здравом уме даже использовать не стал, по той же причине, почему нормальные люди не использовали inline стили. Сейчас почти везде фреймворки с компонентами и авторы Tailwind отдают переиспользование стилей им на откуп. BEM описывает как переиспользовать стили, поэтому получается какое-то сравнений яблок с апельсинами. Интересно было бы увидеть пример проекта на React + Tailwind и React + BEM и посмотреть сравнение на нём


    1. IvanGanev
      18.11.2023 11:16
      +4

      Да, современная фронтенд разработка построена на компонентах, переиспользуются компоненты, а не стили. По этому идея автора зачем-то искать классы теилвайнда она очень странная:

      Элементы Tailwind сложно искать. Нельзя так просто по атрибуту class узнать, в каком месте проекта находится соответствующий код.

      Искать нужно компоненты, а не стили.


      1. NikolayMakhonin Автор
        18.11.2023 11:16

        На svelte не сложно построить проект с переиспользованием и компонентов, и стилей. Я иногда даже пишу отдельные стилевые компоненты без HTML, просто CSS, и импортирую их в разные компоненты с похожим оформлением. Такое разделение стилей и компонентов сильно повышает гибкость.

        Искать нужно компоненты, а не стили.

        == искать нужно не только компоненты, но и конкретные HTML элементы внутри них. На реакте у меня не всегда получалось даже с DevTools определить какой HTML к какому компоненту относится. После того как компонент найден нужно еще потрудиться, чтобы найти в нем соответствующий HTML. Если нужно не просто изменить стиль, а немного перестроить структуру, то нужно еще понять какая роль у каждого HTML элемента, взрывая себе мозг, или плюнуть на это и верстать все с нуля.


        1. IvanGanev
          18.11.2023 11:16

          просто CSS, и импортирую их в разные компоненты с похожим оформлением

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

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

          Если же придерживаться подхода при котором у каждого компонента свои стили то при внесении изменений в эти стили вам нужно проверять только этот один компонент.

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

          == искать нужно не только компоненты, но и конкретные HTML элементы внутри них.

          Я как-то плохо понимаю зачем вообще искать компоненты в хтмл, ну то есть вот у вас есть страница, вы знаете что на этой странице есть нужные вам компоненты, в таком случае можно ведь просто найти эту страницу в исходниках (или шаблон этой страницы), и искать компоненты там.

          Не факт что на странице вы найдете нужный вам компонент, а что если в коде была прописана логика которая при определенных обстоятельствах скрывает нужный вам компонент? Скрывает не стилями, а js, то есть в dom-е этого компонента вообще не будет. И даже зная что такой компонент должен быть на этой странице, вы его там не найдете.

          В исходниках же он точно будет.


    1. NikolayMakhonin Автор
      18.11.2023 11:16
      -1

      У меня есть большой опыт написания проектов на svelte + BEM + PostCSS, есть небольшой опыт работы с несколькими проектами на svelte + Tailwind. Я пришел к выводу что Tailwind лучше переписать на BEM, иначе этот код невозможно поддерживать и развивать, я перестаю понимать даже свой код, написанный на Tailwind, зато хорошо ориентируюсь в BEM коде, написанном очень давно.
      Когда я составлял список задач и проблем к этой статье, я опирался на большой опыт использования довольно мощного UI фреймворка - svelte. Я так же работал с проектами на React + Tailwind. В реакте, как я понял, не так просто применять те простые подходы к стилизации, какие можно применять в svelte. Когда я пытался разобраться со стилизацией в реакте, я не смог найти нормального способа написания стилей с использованием BEM, возможно его и нет. Tailwind в реакте наверное спасает ситуацию, но большого опыта у меня в этом нет.


      1. AlexS94
        18.11.2023 11:16

        Вы смешиваете в одну кучу зачем-то ещё и вопросы относящиеся к использованию фреймворков. Зачем? То что в React есть своя "модная" кухня для работы со стилями это понятно, но никто не мешает его сконфигурирован под то что вам нужно, хоть БЭМ, хоть CSS-in-JS.

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


  1. antonkrechetov
    18.11.2023 11:16

    Пару лет назад я пытался прикрутить BEM к React, и мне не удалось это сделать, что меня сильно удивило.

    Это как? Вроде стилизация в Реакте не отличается от обычного HTML: пишешь jsx-элемент с атрибутом className, подключаешь в этом компоненте css-файл (а то и sass), а там стили для этого класса. Или я что-то путаю?

    Также я не понял вашу мысль насчёт работы с переменными.

    Но для изменения всех цветов на сайте или всех отступов придется заранее прописывать все CSS-переменные и распространять их по всему коду.

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

    Некоторые считают, что BEM и Tailwind не противоречат друг другу. Да, их можно технически смешивать вместе, но это разные архитектуры. ... Такая смешанная архитектура должна быть хорошо продумана. Но я пока не видел НИ ОДНОЙ статьи по архитектуре Tailwind, чего уж говорить об архитектуре Tailwind + BEM.

    Мне это кажется немного сомнительным. БЭМ с элементами несемантической вёрстки можно применять, но конкретно по канонам tailwind же один класс - это в среднем примерно одно css-свойство. Проще написать его в стили блока.


    1. NikolayMakhonin Автор
      18.11.2023 11:16

      пишешь jsx-элемент с атрибутом className

      == я не нашел там автоматической изоляции стилей, или какой-то удобный способ одновременно использовать и BEM и изоляцию стилей. Svelte автоматически добавляет класс .s- к каждому селектору и к каждому html элементу. React, как я понял, такое не может. И если и использовать там BEM, то только как глобальные классы.


    1. NikolayMakhonin Автор
      18.11.2023 11:16

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

      Я уже вижу здесь те же проблемы: если использовать @apply, то это создаст дополнительную работу по конвертации стилей в уме: и когда читаешь код, и когда исправляешь что-то в DevTools и хочешь перенести изменения в код. Если писать Tailwind классы в HTML, то плюс к этому добавляется еще и дополнительная работа по отсеиванию мусора, когда анализируешь HTML. Но возможно Tailwind будет удобен, если весь проект использует utility-first подход к верстке. И на Tailwind можно верстать без дизайнера. В любом случае нужен реальный и большой опыт, чтобы в этом разобраться.


  1. Danil_vod
    18.11.2023 11:16

    Все проблемы которые описал автор решаются с помощью twin macro. Конечно я сравниваю css in js и нативный css, это не совсем корректно. Но если хочешь писать на tailwind мне кажется правильнее всего его использовать с styled components, тогда все проблемы гибкости отойдут, но придут конечно другие проблемы, свалкой стилей, если команда не договорится как они будут подходить к написанию стилей. А так согласен с автором, что если вы пишите сложный по стилям компонент, то tailwind код сложно осмыслить даже несколько пробежек по стилям, хоть они и достаточно декларативные.