| |
I've used Tailwind extensively at previous companies and inevitably each one creates an abstraction that's akin to: const headerClasses = [(list of Tailwind classes here)]; <header className={...headerClasses}>...</header>
because the complexity of reading and writing all of the classes is just too much. At that point, you've just reinvented CSS classes. Tailwind fans will tell you to not do this but if multiple companies are independently having the same problem and coming up with the same solution, the onus is not on the user anymore, it's on the creator to fix it. @apply can work but again it's really not recommended by Tailwind itself, for whatever reason. These days I recommend learning CSS really well and then using Vanilla Extract (https://vanilla-extract.style), a CSS in TypeScript library that compiles down to raw CSS, basically using TS as your preprocessor instead of SCSS. For dynamic styles, they have an optional small runtime. They have a Stitches-like API called Recipes that's phenomenal as well, especially for design systems, you can define your variants and what CSS needs to be applied for each one, so you can map your design components 1-to-1 with your code: import { recipe } from '@vanilla-extract/recipes'; export const button = recipe({ base: { borderRadius: 6 }, variants: { color: { neutral: { background: 'whitesmoke' }, brand: { background: 'blueviolet' }, accent: { background: 'slateblue' } }, size: { small: { padding: 12 }, medium: { padding: 16 }, large: { padding: 24 } }, rounded: { true: { borderRadius: 999 } } }, // Applied when multiple variants are set at once compoundVariants: [ { variants: { color: 'neutral', size: 'large' }, style: { background: 'ghostwhite' } } ], defaultVariants: { color: 'accent', size: 'medium' }
}); Impressive that OP still is using ReasonReact, I thought it was all but dead after ReScript. | |
| |
I actually agree with this criticism, and it's the best criticism of Tailwind I've seen so far. I still love using it, and will continue to do so. This isn't enough to stop me from using it. Normally I try to avoid this, and abstract the repeated bundle of tailwind classes in a component instead (it can be a container component or just some useful utility component to avoid having to repeat the same group of classes too much). This can have its own drawbacks, because having too many custom components that you need to remember to use in given situations (which is effectively how most design-systems would work anyway) makes the codebase less approachable. And even still, we'll end up using the above trick (referencing a group of classes bundled into a property of an object somewhere else) from time to time, as well as the obvious option of just repeating classes as necessary. Referencing classes from an imported object is annoying too, because it breaks intellisense. I'm honestly not sure what a better solution is though, because Tailwind has really sped things up for me compared to CSS/Sass/Chakra/Material. | |
| |
Tamagui really aims to solve this problem and some of the usability problems of className by putting style props directly on the props. You can nest components and abstract them as you want and still get the Tailwind style shorthands except as TS typed tokens. No worry about how you reference things or static-ness etc and the final output can be flattened down to just div + css even across module boundaries or when nesting multiple times. | |
| |
Interesting.. so is Tamagui using react under the hood, but "hiding" it from you to some extent? Does it provide some helpers for responsiveness? | |
| |
It’s just React through and through. It has an optional optimizing compiler that really improves performance even across hard to parse areas like nested conditional logic or spreads of items across module boundaries. | |
| |
I believe the solution here is most definitely @apply. I know the Tailwind team seem to be against @apply (and even flirted with removing it), but having used it extensively to build complex (imo) sites and apps, @apply has been unavoidable and actually great to have. The fact is @apply is there and if we're using it and there's enough push-back, I don't see why the Tailwind team wouldn't listen to that or create a workable upgrade path for those who wish to continue to use it. So use the tools you have - don't avoid it just because the core team dislike it. They can't remove it in the version you have installed today (unless they have some magical way to reach into your computer/server) | |
| |
The real answer is in CSS developing a proper supported way to do mixins. I keep being surprised that noone bring this up in these discussions. | |
| |
I do it with emotion (CSS in JS), specifically NOT using styled components and instead using the CSS prop which allows for composing styles more naturally, and it works great. Never felt a need to get my team on Tailwind since it’s already super productive with the right set of mixins. | |
| |
I get you, the problem is that CSS in JS is simply not possible for the vast majority of web developers out there that work on a site render framework that is not JS ;) I think this is the main reason why Tailwind took off so fast. The frontend tooling world forgot the majority of its users. | |
| |
In general, framework owners _want_ you to become tightly coupled to the framework, whereas as a user you want to be able to move away from it more easily. I think this conflict of interests is behind recommendations like this, rather than sound technical reasons. | |
| |
Why would framework owners want you to become tightly coupled to the framework? They're basically just offering a set of solutions that come with some tradeoffs, but they don't intentionally create problems for their users. | |
| |
For framework owners that have a business built on top of their framework, that tight coupling is their "moat" that helps keep you locked in as a paying customer for longer. | |
| |
Yeah I don't understand what the other solution would be. If you want to have reusable styles, eg "btn" class with all your defaults @apply is the obvious choice. Otherwise you'll end up with similar gargantuan CSS class spagetti and/or have to abstract away some generic components because handling the classes is just too much. | |
| |
I was under the impression that you create a button component and apply the atomic styles in that component. That way you don’t don’t need a “btn” class, you just use the Button component anywhere you need a button. | |
| |
What if you need to render your button component as an anchor element? Or as a div with role="button"? You will want to abstract your button styles somehow so that they can be shared between these three (or more) use cases. Sure you could experiment with using a polymorphic `as` prop to render your button component as any element, but if you're using TypeScript that can get complicated quickly, especially if you're then trying to use that component in conjunction with something like next/link or react-router/link. It's simpler to strip default styles from all your components, and then have a button class to add to any element you want to appear as a button. BUT, then what if you want to add some kind of special behavior to your button that involves subcomponents, e.g. a loading state that conditionally renders a spinner inside the button? Or you want to provide convenience props like rendering an icon before or after the button text? Then a class is not enough. If you're building a design system/component library, none of these options are simple and there are always tradeoffs. | |
| |
This assumes you only have one button class, while you might want to reuse several that have minor differences. | |
| |
You could do that but many of the minor stylings might not warrant a component, like a specific hover effect. For quick prototyping though it just is way more convenient to not to constantly break apart small html snippets into their own components. | |
| |
Sure, I've already moved onto CSS in TS solutions like vanilla-extract above though, I like my typechecking in my CSS. | |
| |
> const headerClasses = [(list of Tailwind classes here)]; > <header className={...headerClasses}>...</header>
Why wouldn't they extract the header as a higher-order component? That would make more sense even without Tailwind, and I haven't heard anyone in the Tailwind community advocate against that. | |
| |
I think this is just an example but `header` is a standard HTML element. It's not always necassary to seperate every single element into it's own component. Every time you make a framework component you add overhead to the rendering of the app. | |
| |
> It's not always necassary to seperate every single element into it's own component. It's not necessary but in the given case it's obviously useful as there is more to abstract than just the html element - the styling. | |
| |
> Every time you make a framework component you add overhead to the rendering of the app. not true, separating components by concern can save you a lot of fiddling with memoization | |
| |
Attributify mostly solves readability issue: <button bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600" text="sm white" font="mono light" p="y-2 x-4" border="2 rounded blue-200" >
https://windicss.org/features/attributify.html | |
| |
It solves the repetition and collation into className, yes. But I think the author's main gripe was that component abstraction was made difficult with Tailwind when you want to customize the component from the outside, by dynamically altering the styles through props (as often needed for contextualization in design systems), and not just no-prop components like Tailwind suggests: https://tailwindcss.com/#component-driven | |
| |
> At that point, you've just reinvented CSS classes. Sort of, except instead of context switching between CSS, HTML, JS and your programming language, you can now remove the CSS entirely. Less context switching is good. I'm a Tailwind fan and I don't see a problem with that pattern for some cases. Obviously there are better ways to organize that should be preferred in general, but it's fine to do that here and there. | |
| |
You still have to context switch between thinking about markup vs styling vs behaviour. The language you write the styling in barely makes a difference imo | |
| |
Sure, but you don't have to switch between types of files, syntax, etc. "Barely makes a difference" seem subjective, so YMMV. | |
| |
Tailwind classes are a syntax (especially since you can set arbitrary values e.g. `top-[-113px]`) and they still represent CSS. So really you're using a DSL with its own syntax, functions and directives on top of the CSS you should still understand. The main benefit is speed: you have a library of utility classes that are shorthand for common CSS patterns. If you are pretending that you're not writing CSS, you are forgetting how the browser is actually working -- eventually you may have to leave Tailwind and you'll find you've forgotten how to write good CSS. | |
| |
all kinds of companies make bad choices, i've also seen a bunch of companies use tailwind correctly -> components + @apply are enough to make it very pleasant to work with :) | |
| |
While that may be true, as I mentioned, if many companies repeatedly keep making the same errors (including a sibling commenter here), it is on the creator to fix it, not the user, and even if they do, it could be a bad solution. Personally I've moved off of Tailwind entirely, too many footguns to deal with, and I'm not sure why it's any better than writing CSS, at scale, not prototyping. In a way, it feels like the CSS version of Perl or APL these days. | |
| |
> In a way, it feels like the CSS version of Perl or APL these days. Perl, maybe, but not APL. CSS version of APL would let me style my personal site and blog in less characters than it took me to write this comment :). | |
| |
windicss is a superset of tailwind that lets you define shortcuts and aliases for easier composition, and variant grouping for less typing | |
|
| |
No, I haven't. As I said I've worked in several companies that explicitly have this sort of abstraction. If so, what's the point of using Tailwind? Tailwind fans will say it's atomic classes, but when using them with many devs, it indeed turns into reinventing CSS classes. So, perhaps it's the other way around, that Tailwind is simply not a good tool at scale. | |
| |
As I understand it, Tailwind is essentially CSS 2.0 - or 5.0 I guess - with the warts ironed out (sorry for the unpleasant mixed metaphor): A) CSS / Tailwind: lots of little 'atomic' classes describing individual visual properties. Easy to understand, easy to customize, but slow to read, slow to get started, with plenty of stuff to learn B) CSS frameworks / Tailwind-based components: fewer classes declaratively describing the component's role (eg 'btn-primary'). Quick to get started, elegant to read, but prone to abstraction leakage and trickier to bend to your own exact specifications There's always been a need for both kind of tools for different projects and there will always be, in the same way that e.g. network programming may involve anything from bit-banging commands to high-level protocols. (And you will often go back and forth in the same project: you quickly crap out your first draft using prebuilt components, then it turns into a serious project and you start actually investing time in design and twiddle with the individual properties, and eventually it grows to a large project where you now have to go back to components again for the sake of maintainability, but this time it's your own components with your own accumulated set of properties.) Over the years CSS frameworks kept improving, but CSS was much slower to do so - although it acquired flexbox, grid, etc., the language limitations stayed, and they were bad enough to e.g. spawn SASS/LESS out of a genuine need for better maintainability. Tailwind takes all the stuff that was added to CSS over two decades, like media queries, and makes it part of the core nu-CSS language design. Tailwind saw a ton of hype and adoption because all the developers who had always wanted to go the (A) road now had a well-designed set of simple classes they could use with a lot less hassle, plus a bunch of developers who had adopted (B) because it was the road that had all the momentum suddenly realized that they probably wanted to use (A) once it was made less painful. Many of the flame wars between "pro-Tailwind" and "anti-Tailwind" people were actually disagreement over whether to take the A or the B road. Using @apply, header classes, or component projects like DaisyUI somewhat resembles using a CSS framework like Bootstrap, but by building on top of Tailwind it means that, when your project or resources grow and you want to move from (B) to fully customizing your style in (A), you will be able to write your individual little graphic touches in Tailwind instead of plain CSS. | |
| |
CSS is fine, arguably even good these days. For every new feature in CSS, such as the is, has, not selectors, Tailwind needs to reimplement them in its own way, basically becoming a DSL in the process. I mean, look at the syntax for something like this: [--scroll-offset:56px] lg:[--scroll-offset:44px]
| |
| |
Sure, but still, it's the kind of small-but-incremental QoL improvements that apparently frontend designers really really want in their working week. You linked to a very interesting TS library above, vanilla-extract, and you can probably see that what it does is very similar to what Tailwind does, only plugging into 'tsc' instead of PostCSS? Eg. https://vanilla-extract.style/documentation/styling/#media-q... | |
| |
It's just using camelCased CSS similar to Emotion or styled-components, how is that similar to Tailwind at all, it's not atomic? Unless you simply mean that both Tailwind and VE have a build step, which is, well, vacuously similar. VE also runs PostCSS underneath after it compiles to CSS, so you can use whatever plug-ins you want too. Regarding the QoL, it seems more to me that now Tailwind discovered that they have to support arbitrary CSS as well as every new feature, so they have to manipulate their DSL to fit all of that into their current syntax, ie class names, even if it doesn't fully fit. I'd rather just use CSS at that point, if I'm cramming a bunch of stuff into a class name. | |
| |
I don’t understand. Do you think there is something in the CSS spec that dictates developers must use high-abstraction classes? What do you think CSS classes are? | |
| |
That is likely the gist of it. I love tailwind for prototyping / designing in code, for large code bases, not so much. | |
| |
Tailwind shines when paired with a component based library such as react. If you’re repeating the classes all the time, perhaps create a component for that. | |
| |
My example was within components. Even inside components it's just visual clutter, hence the abstraction I mentioned. | |
| |
This should be called "Don't use Tailwind in a React library for a design system" as this really has nothing to do with Tailwind and is all about integration of Tailwind into a React design system. None of these "negatives" are "negatives" for using it as part of a standard HTML/CSS website outside of React. The comparison between readability of a bunch of divs and web components or react makes no sense, of course <SidebarItem /> is easier to read then <div class="flex-1 bg-blue font-bold, text-md d:none"></div> but nobody writes React like that. Everyone will have a SidebarItem component that spits out that div. | |
| |
I do write like that. Heck, even a mix of both: <SidebarItem marginRight={2} />, using Chakra-UI [1] (not too dissimilar to Tailwind in spirit). Even the best design system needs local overrides to satisfy a product owner's incomprehensible requests, like "can we push that button a few pixels to the left?". [1] https://chakra-ui.com/ | |
| |
The whole point of a design system is not to have local overrides. In a design system. You specify variants of a component. Each one has its own combination of style defined by the design system. You do not set „Gap“ to 2. you would specify smth like „small“, „medium“ or „large“ as props. This is not a tailwind issue. | |
| |
`2` in this case is a design token that is defined in a theme, that would resolve to a length defined in a CSS unit (ideally rem). How would you handle a design change where a size becomes required that fits between "small" and "medium"? These things happen throughout the lifetime of a project. Opaque numerical design tokens may not be the most explicit to understand, but they allow for expansion. | |
| |
> The whole point of a design system is not to have local overrides. Are you unfamiliar with having-a-boss? | |
| |
He is familiar with the boss being the one mandating the design system, and not the one asking "can we push that button a few pixels to the left?" On the places where things work, bosses act like the GP's one. Granted, that doesn't happen on the large majority of places. | |
| |
Right, huge +1 for Chakra. Chakra is basically tailwind but built on react primitives. It feels lot cleaner, and solves most of the issues mentioned. | |
| |
It's amazing for quick UI building. Note however that this API is brought by Styled System [1], which Chakra uses (and exposes), and adds the component library and other niceties on top. My only regret with Chakra is that it's a tad runtime-heavy, and it's not really adapted to static content. I'd love to see some sort of compiler that digests a React tree into rendered HTML + CSS, with minimal JS just for style interactions (is this what Svelte does?) [1] https://styled-system.com/ | |
| |
This was the reply I was looking for. Thank you. I don't know the purpose of misleading title, I guess this is how web works now - capture attention by misleading readers, then criticize the actual layer that causes problems, but hide the fact that the one in chair is at fault for not being a capable user of technology. | |
| |
I'm guilty of implementing <SideBarItem />
as export { SideBarItem = (<div class="flex-1 bg-blue font-bold, text-md d:none"></div>) }
Is that a bad thing? (Genuinely asking for opinions) E: added clarity | |
| |
No, it's not a bad thing. It's how (nearly) everyone using React (or any frontend framework) + Tailwind will structure their code. And I'm not sure the author is arguing against Tailwind's utility in static styling scenarios. I think the article's author would argue that once you move beyond static classes that Tailwind's class building becomes messy. So <SideBarItem padding={4} active={true} /> would be cleaner in the authors mind if the exposed props get applied by some other tooling better suited for dynamic styling instead of simple string manipulation. There is some merit to that argument. Building the class string can be cumbersome in some scenarios. But Tailwind "clicks" for me where other solutions do not. So I do it anyways. | |
| |
It lends itself to copy-pasting everywhere. Styles end up getting over-applied. Markup becomes 3x harder to scan because the signal: noise ratio is horribly skewed. YMMV; I prefer styled/ emotion-styled for primary, reusable blocks, and tailwind for one-off exceptions like a bit of extra margin. | |
| |
If I were to look at your code (or revisiting my own after some time) I'd know what <SideBarItem /> was likely for straight away. But I would need to construct the likely intent of the <div> version in my mind. Of course you could abstract those classes into one that provides semantics; <div class="side-bar-item"></div> | |
| |
I meant SideBarItem's implementation is the tailwind div, but is referenced as SideBarItem like any other React component, not that the tailwind div is pasted all over. :) | |
|
| |
IMO it's not a bad thing at all unless you have that exact div snippet copy-pasted to a thousand other files. | |
| |
I meant SideBarItem's implementation is the tailwind div, but is referenced as SideBarItem like any other React component, not that the tailwind div is pasted all over. :) | |
| |
It's not a bad thing. Or if it's a bad thing, I'm guilty too | |
| |
> "but nobody writes React like that" If it's "possible" people WILL write code like that. That's why I like styled-components, you are FORCED to separate the style definition and then you can say nobody writes code like that. But dozens of style classes mixed with functionality? People DO write code like that, a lot more than I like admitting seeing myself. | |
| |
This is why I have a hard time getting on the Tailwind bandwagon. It feels like a pipeline equivalent to "margin: 1px 0 0 4px;" and other element-specific stylings _everywhere_. CSS brought some opportunity for structure. At least it started out as "define a common style for a thing", and then if you really wanted to make a small deviation you could inherit and override something from the class. Could be that since I'm not a frontender I just don't get it, but to me it feels like "global variables everywhere". | |
| |
As a full stack that is mostly backend, I like the way CSS frameworks allow me to not have to worry too much about design. I originally used CSS "properly" but I found that it was a chore/pain basically recreating these CSS frameworks over and over. It can get messy pretty fast but I find once you sorta get a handle on the frameworks, it becomes easy to parse as long as people aren't doing 10+ classes per element. I've found working without a CSS framework, people will tend to re-invent the wheel 100 times. i.e. I've seen "padded-box" and "box-with-padding" classes. I experience the worst of the worst tho, as I do a lot of refactoring work, so my take is a bit bias haha. | |
| |
> This should be called "Don't use Tailwind in a React library for a design system" That's true. > as this really has nothing to do with Tailwind Well.. it has to do with using Tailwind with React for a design system. :) He does mention that @apply doesn't fix the problems he mentions, which is what Tailwind suggests as the solution for abstraction when you don't use a component framework: https://tailwindcss.com/#component-driven (scroll down to "Not into component frameworks?") | |
| |
that is javascript programmer they think everyone write javascript | |
| |
Is it just me or has the underlying purpose and value of “cascading” in CSS been lost within these abstractions. The only real limitations with CSS imho was just lack of variable support for easily passing in variables to set color schemes and/or dimensions etc. These seemed to be readily fixed with SCSS and the various options for embedding CSS in JS using styled components and the like. Tailwind seems to be all about making it easy to “pixel f*k” your way to getting the design you want in one given place at the expense of having consistency and maintainability. E.g. with a proper CSS template I can ensure all fonts and sizes and colors are consistent across an app. With Tailwind everything starts to be like the 1990s where all your design was hard coded into table and font elements mixed into your markup! This does NOT seem like progress! | |
| |
The underlying purpose for the “cascading” was customizing the look of documents by the user. We seldom have truly static web documents any more and this use case is non-existent even for those. In my experience cascading is simply not a great idea even in itself — you can’t reasonably share part of your design between different components, it causes way too close coupling, breaking some non-intended component on some other page. What can reasonably be shared is variables and a color palette, which you can specify at a single place with tailwind. But my point, design seems to primarily think in components - so just create a component in your framework/webcomponent, design it locally (e.g. by tailwind) and reuse that widget where you want. | |
| |
I don’t think it’s about “documents” versus “components”. It’s about consistency and maintainability. Keep the local styling for the component local to its place in your repository - absolutely. Give it sensible defaults - for sure. But if you’re using it in something complex where the overall design may evolve it’s a maintainability nightmare to hardcode the styling at the component level and/or designing by classname. I get why initial development is faster BUT… if you want to make a simple style change on a 100 component site - do you really want to be editing a load of components for every change down the line? | |
| |
Out of curiosity, have you used Tailwind and does your criticism come from having used it and not experiencing the progress or does your criticism come from reading how it works and not "feeling" it? I don't intend to follow up with discussion that convinces/dissuades/criticizes you in any way, I just ask purely out of curiosity. | |
| |
I'm not the one you're asking but, I messed around with it a bit and it really left a bad taste in my mouth. There is just much hype behind it though, so I bought the book to really get a feel for it. I liked the book. I used it in a project and after going back to code written by a few members of the team, it just felt like an unmaintainable mess. The common response whenever I share this sentiment is one of: * You don't know what you're doing* You're doing it wrong* You're an idiot. It really kinda feels like the old AngularJS 1.x days as those were the typical responses to anyone who didn't fall down and worship it as a framework masterpiece. I've decided for myself, I'll sit this one out. Judging by history, most of the things we were fanatical about at first, we tend to look at with disdain in a few short years. jQuery, AngularJS, Bootstrap, Redux... It would be foolish to think this library/framework won't go the same way. If you use it, and it works for you and your team, that's great. I would never try to tell you NOT to. For me, I'd rather not. | |
| |
I had the chance to use Tailwind recently. I don’t think it’s disputable that in a sense it mocks the “cascading” part of CSS, though I’m not sure I’d hold that against Tailwind. It often felt as taking a shortcut to me, but it could be a worthy tradeoff and so far I have not noticed Tailwind overly complicating maintenance and development of an average project. That said, I don’t think I would choose Tailwind if I could use, e.g., web components with scoped styles or something similarly more in line with the spirit so to speak. | |
| |
Yea I’ve used and I think it’s cool for prototyping quickly but the idea of coming back and making edits to it in production code a year from now terrifies the bejeezus out of me. | |
| |
If you apply it to a component that you reuse then how is it unmaintainable? | |
| |
It depends on how your components get reused. In most organizations I’ve been in components are used in applications that share a brand identity/style. Even if there are different brands consistency matters for each brand’s look/feel. If styling is to be consistent then it’s way more maintenance to change each component to reflect branding changes than it is to have the brand/house style defined in one place and passed into the components. E.g. decide that all the outlines around inputs, certain boxes etc. are going to be wider - that’s something you probably want to be able to change in one place not 20 or 100 down the line. Sure you “could” find and replace for some stuff but that could easily match the wrong stuff if you use tailwind on something big/complex… at least that’s my concern looking at it for stuff beyond quick prototyping. | |
| |
Tailwind is much, much faster than building your own stylesheets with deeper abstractions. In most cases, it is faster to use Tailwind than customizing an existing UI framework. SCSS and CSS in JS are more complex solutions than Tailwind. Maintainability is generally better with Tailwind because you don’t need to remember all of your abstractions and any hidden structures, eg this div.className always needs to be nested a certain way. Onboarding is trivial because Tailwind can be mastered in two afternoons. Tailwind might not be for everyone, but the features it provides allow for rapid development and easy maintenance. The author has issues with Tailwind in React, but these seem mire like React complexity than Tailwind. | |
| |
I’ve liked the CUBE methodology since I first saw it. It takes all the ideas of these utility classes, but combines it with a BEM-lite approach to work with the cascade, not against it. https://cube.fyi | |
| |
Yes! Use Tailwind for the "U" | |
| |
Well, yeah, the entire point of Tailwind is to not use CSS like it was supposed to be used. Some people like this better. It reminds me of people that used to adjust the format of every single text area on Word instead of using styles. And on some contexts, that's even objectively better. But most of the time it's just a bunch of developers arguing against generalization and encapsulation. I don't understand it either. Anyway, just to add a bit: > The only real limitations with CSS imho was just lack of variable support for easily passing in variables to set color schemes and/or dimensions etc. Nowadays CSS has variables support that you can use for that. | |
| |
> Is it just me or has the underlying purpose and value of “cascading” in CSS been lost within these abstractions. For app-like websites CSS cascade offers little and is often harmful as an innocent change to a top layer will unpredictably cascade down to everything below. > at the expense of having consistency and maintainability. Compared to what. Every single website devolves into a nightmare of incomprehensible class names, or one-off CSS-in-JS solutions everywhere. Compared to that the actually consistent names enforced by Tailwind are both consistent and maintainable. | |
| |
I find that using utilities where appropriate is a win, not everything need be extracted into a stylesheet or component. Having a standard library of utilities makes sense because if you don't use one you end up writing the exact same thin since they are mostly one liners. What I don't understand is why you'd want to build a design system component out of utilities much less build everything out of utilities. | |
|
| |
tbh, tailwind feels like the worst thing that happened to frontend dev since coffee script. | |
|
| |
omg. ok, adopting some F features is an interesting idea. but why are they trying to make that thing with syntax happen? | |
| |
hehe, I guess they loved CoffeeScript and/or minimalism. But if I want F# features I'd rather use F# and compile to JS with Fable, or simply use ReScript which is more sound than TypeScript any way. If I should learn a new syntax, why not get a better type engine in the process? | |
| |
Arguably coffeescript was a big push to move JS spec forward into ES6 and beyond. | |
| |
> Is it just me or has the underlying purpose and value of “cascading” in CSS been lost within these abstractions. I think you have a point. I think the cascading part was ignored for containment purposes during authoring, so it would scale and be predictable for larger teams. But ideally, I think we'd want to author using something like utility classes (or repeated style props?), but then compile them away to abstractions that cascade accordingly during runtime (least amount of kB sent over-the-wire as there would be as few as possible duplicated classes in the HTML, but also presuming that browsers using the cascade is more runtime performant than applying lots of atomic utility classes..?). Then we'd get abstractions without the cost of dealing with author-time abstractions (and having to name them). But then again.. there would then be a disconnect between what CSS classes you see when you author and what you see when you inspect the HTML/DOM... At least the utility classes are 1-to-1... | |
| |
There's an ideal and there's reality. In reality, CSS always ended up spaghetti and horrible to maintain. Even with BEM and other proposed solutions. Human nature and all that. | |
| |
I'm not advocating for vanilla CSS and I agree BEM isn't really a solution. But CSS in JS, SCSS/SASS etc. take the things that work about CSS and add variables etc. to give you something that's useful in the modern world while also giving you a way to keep things maintainable. I worry Tailwind is kind of like the 'fast food' for styling. Tastes good in the moment and satisfies the need for quick calories but ain't gonna be healthy in the long term especially if you do too much of it. | |
| |
This is the same repeated criticism that gets repeated over and over and over again about Tailwind. It's misguided and getting a bit tiresome. | |
| |
I agree it has a place for rapidly prototyping a design. But it pukes in the face of the “DRY” principle and wouldn’t keep it around in production. Use it to get the look right, take the styles and abstract to a sensible place in the design system or component hierarchy passing in the values so you can change one thing not a load. Just following the idea that making life easier for yourself six months from now when you don’t have the current project “context” in your head is way better for lowering technical debt. Oh and we will probably be into another fashionable styling framework by then! ;-P | |
| |
It's been explained in many blog posts as well as in the Tailwind documentation itself. It's meant to be used with components (or partials). There's no copy-pasting involved and is very well-suited for production code. And this applies to any utility-based CSS framework. | |
| |
I've been using Tailwind for my component library and I don't agree.For example "It is optimised for writing, but not for reading" is certainly a problem, but this is why I created a component library. To abstract this. Also this is weird: ```const Card = (props) => { const className = "p-" + props.gap.toString(); return <div className={className} />;};``` Why do this? If gap needs to be set, then break apart the Card subcomponents (Card.Title, Card.Description, Card.Footer) and let the consumer handle their odd logic that break the design system guidelines. I have faced issues with Tailwind too, but I would pick this 10 out of 10 times over styled-components and such. | |
| |
FWIW this code would also break with newer versions of Tailwind as it tries to generate a Tailwind class name on the fly. Tailwind needs all class names to be statically analyzable (via very dumb pattern matching) to pare down the infinite list of possible class names to something you can actually write to a CSS file for production. On a related note, the article's example of why Tailwind is clumsier for a Box component misses out on the addition of the `space-y-{number}` and `space-x-{number}` class names, which are equivalent to the "gap" props in its React example. But I think those didn't exist in March 2021 so I don't blame the author. | |
| |
You can provide a whitelist of classes you use dynamically in the config file. | |
| |
Concatenating Tailwind classes is a huge code smell. While you can safe list them, I've personally never done anything where I felt that it was worth the effort compared to just listing all possible classes. | |
|
|
| |
Another (even smaller) alternative is `.join` `const classes = [some, classNames, here, foo ? 'foo' : ''].join(' ');` | |
| |
A good middle ground is to have your linter sort the classnames in a reasonable way. I prefer the “outside in” method. This keeps things readable enough so you can find classes where you expect them. | |
| |
I would like some kind of editor plugin that would collapse all classNames by default. That would help a lot with Tailwind readability. | |
| |
I don't use React or do much front-end work so I might be missing something here but what's the problem? Tailwind is a utility-first framework. If you're having to add so many classes inline that it makes things more difficult you can surely abstract it away within CSS files? I thought the main thrust of Tailwind is that you get a sensible set of utility classes so you can mix and match them how you need? For more complicated design systems can't you combine these utility classes into your own classes in a CSS file? You still retain the advantage of easier to read CSS and easier to read JSX. | |
| |
I am with you. I personally never have understood css to begin with. I like styled-components (et al (emotion and other css in js things)) because it makes distributing components with styles saner, but I really don’t do much front-end. I am always blown away with what raw css can do and would like to learn it, but it doesn’t click for me. When I first heard about tailwind I thought it was awesome sounding as I don’t jive with css, but when I tried it I was dismayed to have YET ANOTHER build step and thing I had to screw with and configure outside of the src code. | |
| |
> @apply is the directive that Tailwind recommend to extract repeated utility patterns. Since it's a static definition, you would only abstract those lists into a CSS file. I don't want to get into much details about it, but it does not solve the problems mentioned before. I would like to know why @apply does not solve the issues in the author's opinion.This is exactly the part where the author should have gone into details. | |
| |
Probably the same as this reasoning: > Even that this snapshot of code-UI is doable in Tailwind, at some level of those components you will find a layer with a bunch of classNames that you need to parse in your head in order to imagine the UI. So he would probably say that @apply just abstracts away the problem but it still exists somewhere that you'd need to parse through to understand the styling. But this doesn't resonate with me. This isn't avoidable in any scenario. Either you have a styled component with a bunch of css-in-js, or a bunch of css, or a bunch of utility classes. In all scenarios there is an implementation that you have to parse. Which basically means it has nothing to do with @apply and everything to do with css vs. utility classes. | |
| |
Pretty much every thread on Tailwind will devolve into debates on CSS development/maintenance practices. My take is that Tailwind enables a style of CSS development more akin to using a dynamically typed language. It's really great for whipping things up and for one offs. And for so many tasks, that's all you need! But of course, there are situations where it becomes unwieldy. I'd wager that a lot of the problems people have with Tailwind are more social in nature. Which are valid problems! It just means that you need some other solution for that. Analogously, many of the benefits of static typing are social in nature; notably in enforcing interfaces between teams. I suspect people are looking for a similar thing for CSS. But that doesn't invalidate the use cases where Tailwind is particularly handy. | |
| |
> Tailwind isn't component-driven, which they claim to be They don't ever claim to be component-driven. They are utility-class library and nothing else. I highly recommend using twin.macro if you are using React. It basically combines tailwind with styled components and helps immensely with readability: https://github.com/ben-rogerson/twin.macro | |
| |
The author makes a couple of valid points, although these aren’t reasons to not use Tailwind. Rather, they’re just the trade-offs you have to sacrifice for the benefits that Tailwind provides. Whether the trade-offs are worth it depend on your use case and your professional opinion. That being said, when looking at Tailwinds problems, you have to ask yourself “compared to what?” Especially that first complaint - Tailwind is hard to change compared to…Bootstrap? Foundation? BEM? MUI? It’s vastly, vastly easier to change Tailwind code than any of those frameworks (IMO). I’m a Tailwind champion not because it’s the perfect solution, but because I’ve found it to be better overall than anything that came before it. | |
| |
Agreed.For me an increasingly important factor is documentation. Good documentation can save so MUCH time and headaches, and tailwind's is so good that I have an easier time accepting its defects and trade-offs on that basis alone. | |
| |
The tooling around Tailwind is so good. The VSCode intellisense plugin auto-suggests tailwind classes for me, which means I don’t even need to reference the docs unless I’m looking for obscure functionality. It’s amazing. | |
| |
I've found VS Code's Tailwind intellisense to be unreliable. Half the time it just doesn't generate anything. | |
| |
Press Ctrl+spacebar when typing to explicitly invoke it | |
| |
Yes!, Jetbrains works very well to. | |
| |
> “compared to what?” Especially that first complaint - Tailwind is hard to change compared to… The author compared it to Chakra UI (in the last code sample; misspelled Charkra). He recommends ThemeUI, Rebass, Stitches and Radix for a design systems, specifically. A recent and very powerful alternative is Tamagui which takes inspiration from all of those. | |
| |
Tamagui actually does solve all four of the mentioned problems in the article. One nice thing is that it does so in a way that allows for avoiding doubling the depth of your component tree by having to do HOC type solutions as many seem to do to work around them. | |
| |
> Tamagui actually does solve all four of the mentioned problems in the article. How? The very first example in Tamagui docs is this: export const Circle = styled(Stack, { backgroundColor: '$background', color: '$color9', borderRadius: 100_000_000, variants: { pin: { top: { position: 'absolute', top: 0, }, }, size: { '...size': (size, { tokens }) => { return { width: tokens.size[size] ?? size, height: tokens.size[size] ?? size, } }, }, } as const, })
It's already worse than Tailwind. And the rest is just a bunch of predefined components that you can do with any library/framework/vanilla CSS. | |
| |
That example doesn't use shorthands to be more clear, and shows the styled utility function which is a more advanced case of wanting to define a group of styles nicely. Which is incidentally fixing a couple of the problems in TFA. If you just want the simple Tailwind experience you can use shorthands only in those, or even closer just import <Stack /> or <Text /> directly and use shorthands with typed tokens. It's there in a few of the first examples. The upside there is they are just regular props that are typed and have object de-structure and re-structure. import { Stack } from 'tamagui' <Stack p="$2" mx="$1" bc="red" />
| |
| |
My hunch is that the author of the article wouldn't like these shortcuts either :) | |
| |
He proposed exactly them as the solution! | |
| |
Started my web dev in bootstrap era. When I found out about tailwind and tried it in one of my side project, I never looked back. In one of my prev companies, I was able convince my engineering manager to use tailwind alongside Antd and it worked flawlessly. Just to make a point here, Antd is an example of design system and I used tailwind as a "utility" from which I can use vast amount of classes without writing seperate custom css for each of my components. | |
| |
We do the same and haven’t had any problems mixing antd and tailwind | |
| |
Please don't follow the author's opinion. RadixUI with its' 50kB tooltip is not something a sane person would use. This is why React websites weight multiple megabytes at times. | |
| |
The author recommends ThemeUI, Rebass, Stitches and Radix for design systems, specifically. They might add undue bundle size, to various degrees. Looks like Radix' tooltip is now at 12.77 kB: https://www.radix-ui.com/docs/primitives/components/tooltip But a recent and very powerful alternative is Tamagui which takes inspiration from all of those. All for the cost of some 20-27kB, apparently with a clear path to come below 8 kB in the future: https://tamagui.dev/blog/version-one#bundle-size-reduction It even has a tooltip feature, using floating-ui, which one may or may not add, and seems to come in at around 23 kB (in excess of tamagui core, when overlapping sub-libraries in @floating-ui/react-dom-interactions are discounted)... https://bundlephobia.com/package/@tamagui/[email protected] It's still a bit, but fortunately it's not in core so it's optional to include. | |
|
| |
Tailwind isn't component-driven, which they claim to be Stopped reading there. It's a utility library, that much has always been blatently clear and obvious. Really poor article. | |
|
|
| |
The way i read that is that "if you are using Tailwind in a component based framework, do e.g. this ...". Which is entirely different thing than saying "Tailwind is component driven". | |
| |
I'm still firmly in the Bootstrap camp. It's boring, well understood by the average website visitor. Allows for rapid development and thereby time to market. I tried Tailwind once, but found the added complexity not worth the trouble. I guess it all hinges on the type of project(s) and runway. | |
| |
This is how we do it. We don't use Tailwind directly on your components (unless necessary and for adjustment only, more on this later). We try to keep Tailwind as an internal implementation detail. The consumer of our components should pass options as `variant="primary"` where `variant: "primary" | "secondary";` BUT it's okay to allow some Tailwind classes for customization on edge cases. Example (by memory, syntax might be wrong): import cn from 'classnames'; // Definition function Button({ fill, isLoading, className }: ButtonProps) { return (<button className={cn( 'relative', 'inline-flex', 'rounded', { 'w-full': fill, 'opacity-50': isLoading, }, className, )} > {children} </button> ); // Usage // Full-width button, with some margin on the left <Button fill className={"ml-2"} /> // Regular button, with loading state <Button isLoading />
You can even go further and use https://github.com/crswll/clb and create your own rebassjs. | |
| |
The author's gripe with using Tailwind with components is more concretely expressed in this follow up thread: https://twitter.com/davesnx/status/1329392089189265408?s=20&... For reference, this is how Tailwind suggests using it with components (aka. "component-driven" as it says, which the author takes issue with): https://tailwindcss.com/#component-driven I think the gripes come from trying to combine the bottom-up approach of Tailwind with the top-down approach of props based styling that JS component libraries typically allows. His point being that Tailwind does not work too well with such solutions for dynamism / contextual style overrides. You could solve it with using clsx or cva though, as seen in this video: "Tru Narla: Building a design system in Next.js with Tailwind" https://www.youtube.com/watch?v=T-Zv73yZ_QI The best way seems to be making styling a completely internal component concern, and not take in style props but simply semantic props like isActive=true and then have the component itself apply styles based on that, like NavItem.js on the Tailwind home page suggests: https://tailwindcss.com/#component-driven This practise is elaborated in this example, that has the same button styled differently by passing in different semantic props: https://youtu.be/T-Zv73yZ_QI?t=570 | |
| |
The guy is not happy because he still has to write CSS from time to time, yet says the classes from a CSS utility library, which prevent him from having to write too much CSS, should be thrown to garbage. Another absolute title article to make you click. | |
| |
Gave up halfway. I try not to nitpick on grammar and spelling, but this article's lack of editing legitimately makes the reading experience unpleasant. | |
| |
i am thinking about a feature that works like a reverse apply. instead of compiling the tailwind classes into the css class that uses apply, it would add the applied tailwind classes to the html class. this way all the tailwind optimizations and workflows would still work, but at least a few of the problems can be mitigated. | |
| |
I'm going the opposite way and switching from CSS-in-JS to Tailwind. While I like the css-in-js way of doing things, React seems to be moving away from runtime css generation, and I'm not sure the ecosystem will catch up (and I'm tired of playing catch up). Sticking to CSS guarantees you won't have any compatibility problems. | |
| |
> Sticking to CSS guarantees you won't have any compatibility problems. Unless you attempt to go cross-platform.. There are many attempts at getting CSS working on React Native for instance, and many have various compatibility problems (especially as they need to keep up with the evolving CSS spec). But if you do go cross-platform, then something like Tamagui is probably a better bet than trying to replicate CSS. You could also try Nativewind, to get the Tailwind benefits (and limitations, like lack of support for animations). | |
| |
I don't need native and web compatibility, so no problems on that front. Tamagui is on my radar, but it's too new for me to give it a try with my limited time budget. | |
| |
Instead, consider using DaisyUI[0] as a design system, which is built above TailwindCSS. As a bonus, it's just styles, so it works with any JavaScript system (or none). 0: https://daisyui.com/ | |
| |
Yes, this is the typical reinvention of the wheel, where junior devs (all for the right purposes) want to learn by implementing their own version. But mixed with the hipster culture of everything new is automatically better and you get discussions like that. | |
| |
tailwind is a library I don't like in many ways, but the criticisms in this post are pretty ridiculous. tailwind is not a library offered with the motto of component driven as the author mentioned. and instead of bloated packages like radix ui I think it's a more sensible solution. at least it gives a chance to follow a more optimal path. I think it is impossible to find an optimal way with the libraries it recommends. you should use everything just as the library intended. | |
| |
> tailwind is not a library offered with the motto of component driven as the author mentioned. I thought Tailwind shined the most when used within components (and why it's become so popular with React).. making components and not CSS the nexus of abstraction. > and instead of bloated packages like radix ui I think it's a more sensible solution. ... I think it is impossible to find an optimal way with the libraries it recommends. The author recommends ThemeUI, Rebass, Stitches and/or Radix for design systems. They might add undue bundle size, to various degrees. But a recent and very powerful (close to as optimal as possible?) alternative is Tamagui which takes inspiration from all of those. All for the cost of some 20-27 kB, apparently with a clear path to come below 8 kB in the future: https://tamagui.dev/blog/version-one#bundle-size-reduction | |
| |
These sound like gripes with React more than tailwind. | |
| |
I think they are gripes from trying to combine the bottom-up approach of Tailwind with the top-down approach of props based styling that JS component libraries typically allows. His point being that Tailwind does not work too well with such solutions for dynamism / contextual style overrides. You could solve it with using clsx or cva though, as seen in this video: "Tru Narla: Building a design system in Next.js with Tailwind" https://www.youtube.com/watch?v=T-Zv73yZ_QI The best way seems to be making styling a completely internal component concern, and not take in style props but simply semantic props like isActive=true and then have the component itself apply styles based on that, like NavItem.js on the Tailwind home page suggests: https://tailwindcss.com/#component-driven This practise is elaborated in this example has the same button styled differently by passing in different semantic props: https://youtu.be/T-Zv73yZ_QI?t=570 | |
| |
Sounds more like it’s a react problem. I use vue.js and I don’t have these issues. | |
| |
or do. It's fine, really. None of us are your mom. | |
|
| |
I have to chuckle a little at these posts that seem to be 'upset' that Tailwind (or some new framework du jour) is not the silver bullet that the hype train has had them believe I love Tailwind and use it daily, have done for almost 6 years now and I haven't experienced any issue I couldn't solve It certainly has never got me thinking to just throw it away or denigrate it publicly. And my solutions have worked well in a team setting - not adding any burden to other devs This feels more like it's at the intersection of React-specific problems and lack of experience with Tailwind and/or CSS in general I can see some of these problems being frustrating, but because the author seems to just be sounding off without much effort to express attempted solutions, I'm flagging because I feel like this is just anti-Tailwind inflammatory BS Edit: Title also needs to indicate this is from 2021 | |
| |
> but because the author seems to just be sounding off without much effort to express attempted solutions, I'm flagging because I feel like this is just anti-Tailwind inflammatory BS On attempted solutions, there was a subsection titled: "What should I use instead of Tailwind for my design system?" He also suggested this solution for Tailwind, if you read it carefully: "If you still like what Tailwind offers, I recommend a similar approach that we do at Draftbit. Create a tiny layer on top of it: Treat all the Tailwind tokens as code and maintain Tailwind scoped inside those components. Abstract those utility components that you found repeated in your code into a more strict version, and minimise Tailwind for your app." The post was arguably not inflammatory, but pointed to specific issues with Tailwind viz-a-viz design systems. The points raised may not be correct(?), but that doesn't mean it's inflammatory BS. > Edit: Title also needs to indicate this is from 2021 Good point. Updated. | |
| |
I said "I feel like"... that's just how reading the article made me feel I think if it was really intent on providing a clear argument, the author would have gone to a little more trouble to show their work and taken us all on the journey As they didn't, I'm not convinced, and the fact that it's made its way to the top of HN suggests there's just a bunch of anti-TW up-voters ready to jump as soon as there's a whiff of some apparently-new reason not to use it Kinda disappointed this article got shared again tbh | |
| |
Updated. You don't need the month, the HNconvention just uses the year in parens and some things rely on that format. | |
|
| |
It's OT enough to have its own site guideline, yes. | |
| |
I'm aware of that site guideline; most articles aren't specifically advising on UI choices though. | |