Привет, Хабр! Меня зовут Александр Водолазских. Я живу в Новосибирске и я работаю Frontend Domain Lead в СберМаркете. Сегодня хочу немного поговорить об опыте работы с Tailwind CSS — utility-first CSS framework. Поделюсь болью и радостью, которые возникли при его эксплуатации.
Библиотека Tailwind довольно молодая, первые бета-версии появились в 2017 году. Но по количеству звёзд и форков можно понять, что комьюнити вокруг сложилось солидное. Инструмент предоставляет нам большой набор готовых классов и утилит, которые облегчают и ускоряют стилизацию приложения/сайта. Вы просто пишете короткие имена заранее спроектированных классов: цвета, отступы, позиционирование. Хватает классов и для сайзинга, типографики, анимации бэкграундов и многого другого.
Ниже можно увидеть, как всё это выглядит при реализации на React JS.
Радует и совместимость: всё спокойно собирается и под Next.js, и под Vite, и под Angular, и под Create React App. Более того, есть возможность сборки с помощью PostCSS-плагина, что делает инструмент поистине хорошо имплементируемым в любой системе.
Но есть у Tailwind и недостатки. На самом деле это довольно холиварный инструмент.
Допустим, у вас стартап...
Представим, вы с друзьями решили сделать стартап: арендовали гараж для работы, собрали лучшую команду и подобрали крутой технологический стек. Что касается CSS — вы решили найти модное решение для стилизации, и глаз упал на Tailwind. Всем нравится, коллеги довольны, и ничто не предвещает беды.
Вдруг команда получает деньги от инвестора и уже собирается пилить новые бизнес-фичи, но сталкивается с тем, что для комфортного сожительства с Tailwind его нужно «привести в порядок», иначе всё дальнейшее масштабирование под угрозой. И вообще, поддерживать разросшиеся стили становится трудновато, разработчики каждое утро вытирают скупые слёзы левым рукавом.
Почему масштабировать и поддерживать модное решение для стилизации оказывается грустно:
Фреймворк неинформативен. Понятных названий классов в нём просто нет, а как навигироваться в DOM-дереве при дебаге — вообще не понятно.
Классов очень много, их попросту сложно запомнить, особенно вновь прибывающим молодым разрабам. Мы же растём, помните?
Появляются многострочные портянки из классов.
Неясно, каким образом привести груду написанных стилей в общую систему и избавиться от тотального бардака.
Болей достаточно много, как говорится: «Есть от чего впасть в отчаяние». Хорошая новость состоит в том, что все проблемы фреймворка можно сжать в одну: нужно чётко понимать, как его готовить. Если изначально выстроить аккуратный подход, то возможные боли при масштабировании практически нивелируются. Иными словами, у нашей команды есть тактика, и мы должны её придерживаться.
Пройдёмся по каждому из минусов отдельно.
Tailwind — неинформативное решение
Представим, что мне нужно что-то продебажить в UI. Например, проблема в карточке ритейлера. Я часто начинаю дебаг с того, что открываю DevTools и ищу нужный мне компонент по названию класса.
Вот пример нехитрой навигации в проекте, стилизованном с SCSS. Всё весьма просто и прозрачно.
Tailwind отберёт у вас понятные, читаемые названия классов. Вместо чего-то семантичного вы получите sticky top-0 z-40 w-full и так далее.
Как с этим справиться? Можно просто добавить дата-атрибуты. Да, вы никуда не денетесь от портянки классов. Как «упаковать» их красиво — так и не придумали. Да, останутся непонятные длинные имена, но вы сможете навигироваться по DOM-дереву, сможете искать. Тестировщики скажут вам спасибо, потому что дата-атрибуты очень полезны для автоматизации тестирования.
Ещё одно простое и очевидное решение — красиво разделить код на компоненты. Если у вас React, используем его DevTools, и вот искать что-то по дереву компонентов снова просто и приятно. Если не React — расстраиваемся и переходим на React. Это, конечно, шутка, все инструменты для своих задач.
В Tailwind слишком много классов, и запоминать их трудно
Иногда я слышу что-то в таком стиле: «Инструмент классно подходит для прототипов. Правда, мы с ним раньше не работали, но хотим что-то накидать на коленке». Это не очень хорошая история, на коленке не получится. При старте с нуля много времени уйдёт на то, чтобы понять, какие вообще есть классы, как правильно готовить фреймворк, чтобы работа выстроилась более-менее системно.
Логика здесь простая: чем больше вы пишете, тем лучше запоминается. Набор классов всё же ограничен. Как всегда, тренировка и потраченные часы рабочего времени сделают своё дело.
Помимо этого, для решения проблемы есть волшебное расширение IntelliSense. Вы ставите его в VS Code или любую популярную IDE и получаете удобный Tailwind-саджестор: расширение подсказывает вам возможные варианты. Поначалу приходится часто смотреть документацию и пользоваться дополнительными средствами, чтобы ориентироваться в коде. Но, когда вы напишете 10-20 компонентов, всё дойдёт до состояния полного автоматизма, и проблема отпадёт.
Неминуемо собирается портянка из классов
У вас есть элемент, вы добавляете к нему дата-атрибут — и здесь же куча классов. Если вам нужно сверстать адаптивный интерфейс, где стилевое решение будет сильно отличаться для больших, средних и мелких экранов, задача выглядит уже просто монструозно.
Код становится достаточно читаемым, если использовать библиотеку classNames или что-то подобное.
Мы работаем с ней так: бьём длинный класс на небольшие подклассы, которые семантически объединяем по экранам. Можно дробить классы по другим признакам, например — в зависимости от значений пропсов. Декомпозиция здесь — наше всё. И всегда стоит помнить про компонентный подход: чем аккуратнее и точнее мы дробим, тем меньше громоздких классов у нас будет в будущем.
Однако важно сказать, что по волшебству всё это не происходит. Держите в голове командные договорённости либо ужесточайте правила ESLint или другого линтера. Иначе будет сложно контролировать, чтобы люди писали код правильно. Плагин eslint-plugin-tailwindcss даёт набор правил, который можно использовать для поддержки кода. Он отсортирует и упорядочит классы, запретит использовать неконсистентные значения.
Стилизация в Tailwind непереиспользуемая, плохо масштабируемая и бессистемная
Про переиспользование. В Tailwind есть очень полезная директива @apply, которая добавляет ваши кастомные классы на слой компонентов и позволяет использовать их везде. Это значит, что вы можете написать отдельный класс и использовать в нём набор других классов.
@layer components {
.btn-primary {
@apply py-2 px-4 bg-blue-500 text-white
font-semibold rounded-lg shadow-md
hover:bg-blue-700 focus:outline-none
focus:ring-2 focus:ring-blue-400
focus:ring-opacity-75;
}
}
А теперь вы можете расширить директивой класс любого элемента. Достаточно удобно!
Использование таких apply-классов даёт нам возможность экспортировать целые наборы стилей. Грубо говоря, я могу написать свои компоненты, вынести все стили в общую core-библиотеку и переиспользовать в других своих проектах!
Про масштабируемость. Фреймворк предоставляет пользователю очень гибкий конфиг. Есть tailwind.config.js, который создаётся в корне вашего проекта, когда вы инитите Tailwind. Он даёт возможность настроить всё так, как вам необходимо. С помощью этого конфига вы можете строго ограничить команду разработчиков, дав им доступ лишь к типографике, цветам, отступам, которые предусмотрены именно вашей дизайн-системой. На скрине описан вид поведения boxShadow и цветов.
С трейдоффами выше вполне можно смириться, часть из них разрешается позитивно. Мы либо улучшаем опыт взаимодействия, либо привыкаем.
Однако в Tailwind есть проблемы, с которыми действительно тяжело.
Легко получить неконтролируемую свалку классов
Например, у нас есть такой класс:
Разраб использовал всё, что только можно, скомбинировав:
Кривой и несемантичный дата-атрибут.
Отступы в пикселях, в отрицательных значениях и в rem.
Цвета в текстовых значениях и через hex.
Получилось очень мусорно и не очень читаемо. Вы скажете, что кейс неорганический, а я скажу вам, что в коде стартапов встречается и не такое ???? Поэтому важно договариваться либо ужесточать ESLint. Выше я уже писал об этом, но это так важно, что позволю себе повториться.
Другого решения нет, только так.
Большинство кастомных решений в Tailwind неудобные и сложные
Прежде чем считать это проблемой, задайте себе вопрос, часто ли вам нужно изобретать велосипед. Библиотека классов огромная, и найти что-то можно практически под любые нужды.
Но есть ситуации, когда персональный велосипед вам точно нужен. Например, Tailwind не умеет в clip-path. Инструмент развивается, у него выходят новые версии, но да, в данный момент действительно есть ряд юзкейсов, которые остались без имплементации.
Вы всегда можете написать нативный CSS и закрепить его на утилитах через директиву @layer. Вроде ничего сложного, но давайте представим, что вы каждый раз пишете под любое более-менее кастомное решение такой кусочек. Страшно подумать, во сколько раз вырастут затраты времени и сил. Если стилизация проекта слишком кастомная, лучше подумайте десять раз, стоит ли использовать Tailwind.
Из коробки нет RTL
Встроенного RTL в Tailwind не предусмотрено в принципе. В вашем проекте он может быть и не нужен, но кто знает, из какой страны в стартап придут инвесторы.
Отличное комьюнити фреймворка решило эту проблему библиотекой tailwindcss-rtl. Она даёт дополнительный набор классов, с которыми ваш RTL заведётся без какого-либо труда. Недаром говорят: «Добавьте к любому слову .js — и вы получите какую-то существующую NPM-библиотеку». Здесь комьюнити действительно решает!
Аксессибилити хромает
Допустим, у нас есть div, который должен быть прочитан только скринридером и не должен отображаться в UI. Для этого нам дают класс sr-only, который абсолютно позиционирует элемент, скрывает его и применяет ряд дополнительных стилизаций. На этом всё — больше из коробки ничего в Tailwind просто нет.
Вселяет надежду находящийся на этапе тестирования пакет Tailwind CSS Beta 2 с плюшками аксессибилити вроде атрибутов aria-* разных классов. Они позволяют определять совместимость браузера с тем или иным CSS-решением. Есть и много других полезных фич.
Раз уж мы заговорили про NPM-пакеты, мне будет грустно, если я не скажу, что в связке с React вы можете писать кроссплатформенные стили, используя что-то из этой пары классных пакетов: один, два. UI будет переиспользуемым: мы можем собрать React Native под Web, используя что-то вроде react-native-web, и вкупе со стилизацией на Tailwind получить мощнейшее комбо.
Комьюнити Tailwind даёт нам ещё и готовые UI-киты (например, tailwind-kit). Вы берёте готовый код, копипастите его в свой проект, стилизованный с Tailwind, и всё. Всё работает, никаких дополнительных библиотек не нужно.
Как любой инструмент, Tailwind имеет свои трейдоффы, но он быстрый и кроссплатформенный. Когда вы выйдете на пик кривой обучения, потратив N часов и написав M компонентов, всё станет интуитивно понятно, работа по стилизации пойдёт в удовольствие.
Масштабируем ли код, написанный с Tailwind? Да, но его нужно уметь готовить. Если вы не будете договариваться либо не будете использовать свои жёсткие правила для ESLint, у команды ничего не получится.
Tailwind даёт классное комьюнити: если использовать его силу, можно реализовать практически любую идею. Для меня это решение оказалось удобным и хорошо масштабируемым в рамках проекта. Остальные выводы каждый волен делать самостоятельно. Использовать или нет — решать вам.
Tech-команда СберМаркета ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на YouTube. А также слушай подкаст «Для tech и этих» от наших it-менеджеров.
Комментарии (11)
NickyX3
25.05.2023 12:47+1накидывание через @apply в source twcss не отменяет там исполmзование обычных css свойств. Можно спокойно понаписать что-то типа.
.class { border:1px solid black; ... @apply p-4 m-0 bg-grey-500 }
Просто не надо тащить tw классы в html от слова совсем и будет прямо гораздо проще.
timonbandit
25.05.2023 12:47+4Как по мне, самый большой недостаток Tailwind - это его популярность.
Выбор инструмента по популярности, а не по тому подходит он или нет - вот это проблема.
Если у вас очередная админка или блог - ок.
Но когда у тебя в фигме 20 задизайнеренных интерфейсов и каждый уникален - и еще и tailwind туда - получается помойка из класов что нужны + куча классов из tailwind. Неподдерживаемая куча.NickyX3
25.05.2023 12:47+1Компиляция из источника в tw подразумевает что в результате будут только те css, которые есть в ваших исходниках и не будет никакой помойки. Будет исходник с@apply классов tw/возможно смешанный с нативным css + ваши html/js/whatever "шаблоны", а на выходе кроссбраузерный css только с теми классами, которые реально есть в ваших исходниках и используются
Ilusha
25.05.2023 12:47+3Классы-утилиты для создания структуры - это очень удобно: всякие flex, column и т.д. Если дизайнер верстает по сетке, то p-1, mr-2, gap-3 - это тоже удобно. Плюс все размерности завязаны на общий конфиг.
Проблемы: портянки, миксы обычных классов и утилит, а также apply. Кажется, создатель tailwind считает apply большой ошибкой.
Кмк, tailwind просто заигрался, создавая инструментарий, заменяющий чистый css в 90% случаев. В итоге его нужно учить. Он позиционируется как фреймворк, но как решать реальные проблемы использования не ясно.
Но, в проектах, где нет tailwind, я теперь создаю ему замену в виде нужных мне утилит.
У quasar(vue) есть свой набор css-утилит, и вот их достаточно.
gmtd
25.05.2023 12:47Tailwind даёт классное комьюнити: если использовать его силу, можно реализовать практически любую идею.
А мне кажется, для хорошего простого понятного и мощного инструмента и комьюнити не нужна
Вот взять хотя бы CSS3
vodolazskikh Автор
25.05.2023 12:47Но ведь за любым, даже очень интересно зрелым решением - огромное количество контрибьютеров и евангелистов;)
noodles
25.05.2023 12:47Использование таких apply-классов даёт нам возможность экспортировать целые наборы стилей.
Использование нормальных css-классов даёт возможность экспортировать нам целые наборы свойств.)
Использовать или нет — решать вам.
Как руководитель - запретил его использовать впринципе. Мне нужно чтоб код был поддерживаемым через условно пять лет. И не пришлось бы нанимать синьйора-фронтендера-зайчика который меняет работу каждый год (потому что ему "надо развиваться" (с) ), и который хочет попробовать всё подряд, в том числе всратый таилвинд или микрофронтенд, но не видит результаты своих принятых решений на долгосроке (потому что уже порядочно наговнякал и пора увольнятся.))
vodolazskikh Автор
25.05.2023 12:47Воля ваша. Запрещать что-то просто «потому что» может быть и нормально, но каждой задаче - свои решения. Никто не говорит, что нужно использовать тот или иной фреймворк/библиотеку просто потому что, «хочется попробовать» - априори не хорошее решение. И тут уже всё равно, что подставить вместо tailwind…
Основная мысль - не в том, что инструмент чертовски хорош… а как раз в том, что он полон трейдоффов, но и его можно приготовить нормально и масштабируемо.
Lazytech
Упомяну еще один недостаток Tailwind CSS. Если дизайн-макет подготовлен в Figma, то весь CSS-код фактически придется "конвертировать" в формат Tailwind - по сути, делать лишнюю работу. Для сравнения, при использовании чистого CSS или CSS-фреймворков наподобие Sass/SCSS можно взять почти весь CSS-код из дизайн-макета как есть, с минимальными правками.