Комментарии 51
Не понял сабжа. Если делаешь универсальное решение, то по дефолту оно и является проблемой. Просто управляем сложностью, и рядом с XButton может быть XButton2 - норм.
Про cva вы умолчали, что наряду с VariantProps-параметрами остается доступный className, куда можно добавить своё накопившееся.
Универсальное решение возможно только для функциональных вариантов кнопок - primary, default и тд. Для вариаций связанных с размерами или наличием иконки внутри - такие правила не задать.
Смысл проблемы как раз таки в наличии четких правил для одних вариантов, и их комбинации с другими вариантами.
Про CVA - я разбираю идею и подход вокруг которой строится система. А не костыли чтобы реализовать что-то. А так классы конечно там можно добавить. Но тогда зачем вообще tailwind если снова все уперлось в стили?
Норм там с иконкой внутри и без. Четвертый tailwind умеет в хитрые штуки типа селекторов потомков. И умеет в data-атрибуты. Уверен, вы видели это в shadcn - я оттудова копипастю и адаптирую под себя.
Так кусочек, когда нужно для xs только иконку, а текст в span прятать и оставлять только img/svg-иконку:adaptive: { true: "[&>span]:hidden sm:[&>span]:inline", false: ""}
Еще в cva есть compoundVariants, когда просто variants не хватает. Здесь размер кнопки + текст/нет:compoundVariants: [ // отступы, если есть span-текст{ adaptive: false, size: "md", class: "has-[>span]:px-3" }, { adaptive: false, size: "lg", class: "has-[>span]:px-4" }, { adaptive: true, size: "md", class: "sm:has-[>span]:px-3" }, { adaptive: true, size: "lg", class: "sm:has-[>span]:px-4" },
Да, все в css переводится. Но работает, и это уже хорошо
ну честно это не выглядит все как стабильное решение для какого-то крупного проекта :-)
Там же они не просто так используют icon-xs" | "icon-sm" | "icon-lg" вместо того чтобы просто совмещать вариант с icon и размер.
Попробуйте совместить primary + outline + xs кнопку -- придется снова придумывать какой-то костыль.
Эти три соединяются в обычном variants. Если можно в прозрачность, то ставится основной цвет text-success-700, а фон задается bg-current/5 сразу для всех вариантов. Вот с variant=solid такой фокус не срабатывает и там уже compoundVariants и длинное "text-success-50 bg-success-700".
Все равно куча кнопок, но скорее из-за логики. Например, кнопки download/upload выделены в отдельные с наследованием ts-пропсов.
А если какая-то редкая замысловатая кнопка, то проще стилизовать на месте, а не мудрить в универсальность. У всех компонент какой-то жизненный цикл. А "одна кнопка для всего" - зло.
я говорил про пример который я приводил из shadcn
дефолтная кнопка + outline - это по сути взаимоисключающие варианты там и их нельзя никак вместе использовать
---
мой пойнт не в том чтобы создавать кучу вариантов кнопок, а в том чтобы создавать модификаторы кнопок и комбинировать их между собой при необходимости. Т.е. список вариантов - не линейный, а идет в двух измерениях.
Простой пример: в текущем исполнении shadcn возможны 48 комбинаций из типов кнопок и размеров.
Если же ты добавляешь чуть больше гибкости:
variant: "default" | "ghost" | "destructive" | "secondary" | "link" "default" size: "default" | "xs" | "sm" | "lg"
icon: yes / no
outline: outlined / solid
то таких комбинаций становится уже 80.
---
С первым вариантом предлагаемая архитектура Tailwind может более-менее справиться. Но серьезные проекты требуют второго.
А ещё есть вариант Микеланджело: отрезать все лишнее- выкинуть ваш специальный тип кнопки оставить только стандартные.
Боюсь продакт и дизайнер будут против 😅
У дизайнера работа - фигню придумывать, иначе ему деньги перестанут платить
Это конечно ваше мнение, но оно в корне - неверное :-)
Давайте факты в студию. На автомобилях в том что да VW стали массово заменять физические кнопки сенсорными, потом одумались, руководство признало «перегибы на местах» обещали вернуть нормальные. В Китае с подачи Тесла стали делать ручки - хрен откроешь снаружи, особенно если электричество отрубилась, и кстати так же хрен откроешь изнутри если очень надо. В результате Китайское правительство выпустило требования: все ручки должны «торчать» и чтобы можно было открывать руками не зависимо от того есть электричество или нет.
Это кто придумывал? Дизигнеры.
Поэтому уже давно есть дизигнеры а есть Промышленный Дизайн - это где нельзя фигню морозить а надо думать о функциональности/безопасности и т.д. И т.п. ниже фото из симулятора A360. Обратите внимание на цвета/формы и т.д.
Это делали с учетом эргономики/безопасности и сотни других требований. Сколько вам надо классов чтобы такие кнопки сделать? Кстати оно работать должно в режиме реального времени а не по пол часа грузиться.


Это вы конечно сделали обобщение 😳
Т.е. если дизайнеры VW сделали ошибку, то в дизайн-системе в приложении не должно быть лишних кнопок?
Давайте разделять продукты, которые надо продавать и продукты, которые имеют характерное функциональное предназначение. Первые - будут экспериментировать, чтобы повысить продажи, а вторые - будут придерживаться более консервативного подхода. И ничего мы с этим сделать не можем.
---
Вообще весь этот пост не про нужны ли нам кнопки или нет, а про то как взять готовый фреймворк и адаптировать его под серьезный проект чтобы можно было эффективно работать. В любом случае хоть 1 раз из 1000 добавление нового типа кнопки будет абсолютно оправдано. И я лично с таким сталкивался на проектах. Об этом и пишу.
Я привёл примеры, вы - нет, пока ваше утверждение выглядит мягко говоря легковесным. «У нас есть такие приборы, но мы вам о них не расскажем…»
Те кому "надо продавать" пусть страдают, в чем проблема то? Ну или как вариант, пусть сделают что-то полезное, что будет продавать себя само, без всяких свистелок-перделок.
Как говорится, нормально делай - нормально будет))
С нетерпением ждём Часть 2 с описанием, как построить жизнеспособную дизайн-систему на Tailwind.
Вангую в продолжении будет про @apply, @variant и нейминг классов по BEM / разделение на компоненты.
В итоге окажется что после всех этих усилий мы наконец получаем возможность просто писать нормальный css :) а Tailwind становится просто лишним слоем синтаксического сахара.
Поскольку не нужно переключаться между файлом компонента и файлом стилей, код пишется значительно быстрее.
Серьёзно? и сколько секунд вы наэкономили?
Мне кажется вы не совсем в теме. Сейчас Tailwind - это 80% всех загрузок CSS фреймворков. Фактически сейчас это стандарт, особенно если брать стек связанный с реактом. Все новые UI библиотеки пишутся сейчас поверх него.
То что я привел тут - это не мое мнение. Я то как раз отношусь к нему негативно.
Это мнение разработчиков активно на нем пишущих. И вполне адекватное мнение, потому что если вам не нужно постоянно переключаться между файлами и искать глазами нужную строку, то это экономит много времени и увеличивает скорость разработки. При его использовании вы пишите все нужные классы прямо в шаблоне.
Собственно это и есть концепция, лежащая в его основе и которая сделала его популярным.
Нет, он вообще не поэтому стал популярным. Экономии времени за счёт переключения нет, тк разработчику нужно переключатся и запоминать выдуманный псевдо-язык классов Tailwind.
Я могу
Вы можете сгенерировать новый вариант кнопки через одну функцию в Tailwind?
Если да, то поделитесь пожалуйста
Я могу построить жизнеспособную дизайн-систему, в том числе с помощью Tailwind. Про кнопки - холивар. Проблемы которые вы описали в статье, они же не только utility-first подхода касаются, это последствия плохой архитектуры. А с хорошей архитектурой вы можете строить дизайн систему как поверх Tailwind, так и поверх любого другого подходящего под ваш проект решения, это может быть UI / CSS фреймворк, или вообще отсутствие такового.
Вы исходите из предположения что писать кастомный CSS рядом с Tailwind это плохая практика. Если пересмотрите этот поинт, сразу все станет проще.
Про кнопки - холивар
это не может быть холиваром так как я рассматриваю разные варианты. Холивар - это когда есть два решения основанные на предпочтениях. Если у вас есть свое решение, буду рад его услышать :-)
> Проблемы которые вы описали в статье, они же не только utility-first подхода касаются, это последствия плохой архитектуры
Эти проблемы конкретно Tailwind как основы дизайн-системы.
Я написал как это обычно реализовано в классических фреймворках типа Bootstrap или Ng-Zorro. И потом написал какие есть варианты реализации на базе Tailwind.
> А с хорошей архитектурой вы можете строить дизайн систему как поверх Tailwind
Tailwind - не просто часть а основа архитектуры и вы не можете это не учитывать.
Если вы хотите построить свое решение через CSS и просто использовать некоторые утилитарные классы из Tailwind или некоторые CSS-переменные, то вы не строите систему ПОВЕРХ фреймворка, вы строите ее РЯДОМ. Хорошее решение должно быть расширяемым.
> Если пересмотрите этот поинт, сразу все станет проще
это не мой пойнт, а основная идея которую заложил его создатель
https://adamwathan.me/css-utility-classes-and-separation-of-concerns/
Если вы строите дизайн-систему на базе CSS, то зачем вам там Tailwind?
Мне Tailwind нужен чтобы не было мертвого css кода (удалил layout в html но в style.css оставил) и чтобы не писать в каждом проекте одни и те же утилиты. Семантический css живет в компонентах, там его проще контролировать, обновлять и удалять.
По поводу кнопок я использую подход описанный у вас в статье как "Подход через CSS-переменные". Не вижу повода при этом отказываться от Tailwind, я конечно могу писать каждый раз одни и те же утилиты и найти альтернативы для Purge / Tree-shaking css чтобы не оставлять мертвецов. "Можно, но зачем"?
Мне кажется автор не доконца изучил варианты использования. Можно посмотреть не на китайский копипаст shadcn, а к примеру на daisyUi, они ближе интегрированы с ТВ. Плюс вы забываете что можно определять переменные в arbitrary style, и на их основе строить композиции вариантов. Тогда вообще не важно какие там цвета
shadcn буквально в 5 раз сейчас популярнее чем daisyUi, поэтому и смотрю на него в первую очередь.
Если там есть интересный паттерн организации таких компонентов, то прошу вас поделиться. Конечно же я не могу рассмотреть все возможные варианты.
---
> можно определять переменные в arbitrary style
я рассматриваю системные подходы, на основе которых можно строить сложные дизайн-системы
вариант с arbitrary style выглядит как костыль, который можно использовать, но на нем не построишь что-то стабильное или тем-более читаемое
популярность shadcn - увы как в песни ВИА "Ленениград" "Любит наш народ всякое г*вн*!"
Сравнивать shadcn и daisyUI не получится так как разные уровни задач и "абстракций" одни просто пропылисосили популярные headless js библиотеки и сделали свое, другие пошли в реализацию css компонентов с мимимизацией js логики на базе tailwind.
daisyUI - https://daisyui.com/components/button/ потыкать можно ту же кнопку и в хедере есть тоглер тем.. эти ребята ушли по пути разработки плагинов/утилит. И вполне разумно если нужно сделать свой велосипед можно подчерпнуть их подходы..
Подход с arbitrary style - это что-то похожее на то как делают в mantine, то есть есть токен --button-text-color который используется в компоненте и с помощью вариаций мы можем переопреледить значение этого токена (переменной) где-то в схеме cva к примеру. "Псевдокод" const styles = cva('...', {variants: {variant:{primary: '[--button-text-color:--ui-my-theme-primary]....}}} отсюда получается что кнопка с вариантом primary будет опиратся на токен --button-text-color, который будет определен из темы tailwind, и в случае необоходимости изменить какие-то цветовые решения нам нужно будет только изменить тему в Tailwind.. почему такие сложные конструкции возникает вопрос а проблема увы есть в TW4 связанная с расширением конфигурационных файлов осбоенно когда нужно накладывать несколько тем импортируя их
PS. касаемо читабельности и универсальности, то есть монструозные sass конструкции с миксинами и прочей изотерикой по вашему читаемо? Видя sass я каждый раз вижу когда программируют на Typescript types aka lisp, ничего не понятно но очень интересно, так что "читаемость" - это что-то на "Субъективщине"
популярность shadcn - увы как в песни ВИА "Ленениград" "Любит наш народ всякое г*вн*!"
использовать слова из песни в качестве аргумента - так себе затея. Я могу точно также негативно сказать про Tailwind и игнорировать, что он сейчас 80% рынка занимает просто потому что он мне не нравится. Но какой в этом смысл?
> И вполне разумно если нужно сделать свой велосипед можно подчерпнуть их подходы
мне не нужен свой велосипед, но я рассматриваю варианты можно ли использовать Tailwind как базу для сложных проектов.
> потыкать можно ту же кнопку и в хедере есть тоглер тем
я посмотрел код и могу сказать что они просто вернулись к более классическому паттерну. Tailwind предполагает использование прямо в коде компонента без написания css напрямую. Тут же явно смещается акцент на css классы, просто построено это поверх css переменных из tailwind и немного их кода как в примере
.btn {
@apply inline-flex shrink-0 cursor-pointer flex-nowrap items-center justify-center gap-1.5 text-center align-middle outline-offset-2 select-none;
padding-inline: var(–btn-p);
color: var(–btn-fg);
–tw-prose-links: var(–btn-fg);
height: var(–size);
font-size: var(–fontsize, 0.875rem);
font-weight: 600;
}
т.е. по сути это микс и чтобы серьезно с ней работать и расширять для своих нужд необходимо:
1) потерять в скорость разработки так как идет возврат к стилям
2) знать хорошо синтаксис Tailwind чтобы мешать его со стилями
3) иметь ограничения css которые пока существуют (без sass и less)
"Tailwind предполагает использование прямо в коде компонента без написания css" и "Тут же явно смещается акцент на css классы" - ну так в принципе фундаментально атомарные классы TW это просто css классы. если не хватает "из коробки" никто не запрещает создавать свои "компонентны" в tailwind. У них в документации есть об этом отдельная статья. И пример выше наглядно это показывает.
"2) знать хорошо синтаксис Tailwind чтобы мешать его со стилями" - ну так не зная "синтаксиса" инструментария родить сложный продкут не выйдет не важно TW, SASS, REACT "подставь что угодно" не выйдет. Если не заходит концепция TW то может и не стоит пытаться натягивать сову на сверический объект?
Вспомнил сайт nodejs который сделал для них команда vercel если не ошибаюсь и там как раз таки такой подход (был по крайней мере когда я смотрел исходники) что все стили это css modules в которых внутри сделаны классы через @apply tailwindcss - как по мне сомнительное решение ибо это просто shorthand и увеличение css бандла.
давайте я попробую сформулировать иначе:
Адам Уотан в 2019 выпускает Tailwind и основная идея была следующая:
вы учите синтаксис этого фреймворка, но взамен можете писать стили прямо в компоненте чтобы увеличить скорость разработки. Компонент = CSS класс, CSS классы = CSS свойства.
Да, сейчас вы можете писать свои классы черещ utility чтобы их применять в компонентах. Или через apply использовать классы внутри другого класса. И еще кучу всего. Но основная идея - не поменялась.
Если же мы отходим от этой идеи и пишем опираясь в основном на css как daisyUI, то нафига там Tailwind? Они легко могут писать все на CSS, просто добавив свои утилитарные классы и используя свои CSS переменные. Потому что Tailwind в таком исполнении - еще одна прокладка, которая усложняет читаемость кода и требует изучения. Убрав ее, мы получаем более эффективную систему с точки зрения разработчиков и в целом продукта.
основная идея была следующая: вы учите синтаксис этого фреймворка, но взамен можете писать стили прямо в компоненте чтобы увеличить скорость разработки
Не берусь утверждать, какая идея была основной именно для автора, но я одно время пытался разобраться, что для аудитории TW самое главное. Спрашивал людей, посмотрел много видео на эту тему.
И вот чаще всего говорят "не надо придумывать имена классов" - мол, это большая когнитивная нагрузка, и плюс со временем они теряют актуальность. Автор про это тоже говорил, конечно. То есть придумывать (и потом рефакторить) имена компонентам, переменным, пропсам, типам и дженерикам это норм, а цсс классы резко труднее.
И в этом смысле да, daisyUI - это очень странный продукт, который пытается сидеть на 2 стульях, которые ещё и постепенно разъезжаются друг от друга.
В 2017 году он написал вот такую статью - https://adamwathan.me/css-utility-classes-and-separation-of-concerns/ где собственно и изложил всю идею utility-first фреймворка.
Про нейминг классов - абсолютно точно. Это тоже называется одной из основных причин и увеличения скорости разработки и почему такой подход популярен у разработчиков.
Подход с arbitrary style
да, я понимаю смысл, но это все выгядит временным решением а не паттерн на котором можно построить систему.
> то есть монструозные sass конструкции с миксинами и прочей изотерикой по вашему читаемо?
я буквально посмотрел синтаксис arbitrary style и на него и написал свой комментарий. Можно монструозные конструкции слепить из чего угодно, но не надо обобщать. Есть плохие варианты, а есть хорошо читаемые на sass.
.
Tailwind хайпанул на джунах и стартаперах. Изъяны этой системы видны с первых минут использования, и в скорости прирост так себе. Tailwind и подобные штуки существуют для тех кто не умеет настраивать UI kit'ы готовых UI фреймворков. Большинство фреймворков со старта можно настроить под любые хотелки, но это надо уметь.
А где вариант сделать компонент или параметр компонента?
<ButtonMagicStyle :moreHighlight="true">
Почему всё в css должно упираться?
Я построил несколько дизайн систем используя тейлвинд. Все отлично работает, пользуюсь уже на протяжении нескольких лет. Статью не читал, нету времени спорить. Каждый строит как он хочет.
Если я правильно понял, то вы видите негативные последствия от использования TailwindCSS в долгоживущих проектах с большой дизайн-системой. Но я не понял, как использование TailwindCSS приводит к этим негативным последствиям и какие это последствия.
TailwindCSS - это фреймворк для CSS, который разбивает приложение на слои, заполняет слои base и utilities базовыми классами, а потом позволяет упаковывать utility классы при сборке. Упаковка утилит - это killer feature этого фреймворка: авто-подстановка размеров экрана (`sm`, `md` и т.д.), переменных (`grid-cols-[5]`, `px-[2rem]` и т.д.). Я не представляю себе вёрстку, особенно связанную с блоками (колонки, отступы, выравнивание) без TailwindCSS. Каждый раз, как я работаю над старыми проектами без TailwindCSS, где для каждого чиха нужно прописывать свой класс - я банально мучаюсь.
Тот пример, который вы рассматривали в статье - это создание компонента. Но ведь это не имеет отношение к TailwindCSS. Вы сами решаете, как вы будете выстраивать код внутри компонентов, и какой у этого компонента будет интерфейс.
Я сейчас работаю над небольшим проектом, в котором делаю дизайн систему для программ на Vue.js в стилистике первых Macintosh. Я использую в нём TailwindCSS. Но когда я пишу компонент, большая часть CSS, не связанная с позиционированием, у меня находится в обычных `<style scoped>`. И это не мешает мне использовать сброс стилей из коробки, создавать утилиты и класть их в подготовленный слой, использовать генерацию классов для позиционирования.
В общем и целом TailwindCSS значительно облегчает жизнь. И почему его использование должно приводить к негармоничной дизайн-системе - мне не понятно. Потому что он этому никак не мешает
Если я правильно вас понимаю, то ваш комментарий только подтверждает вывод написанный в заголовке статьи - 'нельзя построить жизнеспособную дизайн-систему на основе Tailwind'.
Потому что то что вы описали - это использование Tailwind как utility framework в проекте, а все остальное создается самим разработчиком. Т.е. вы не используете его как основу, а просто для удобства использования utility классов.
Я же пишу о том что классические фреймворки используются не как допонение к проекту, а как основа дизайн-системы, которую можно легко расширять и дополнять через внутренние механизмы, вместо того чтобы придумывать все самому.
Для token-based design systems не нужен Tailwind, это просто лишний слой абстракции.


Ты не можешь построить жизнеспособную дизайн-систему на Tailwind — Часть 1