Tailwind is a leaky abstraction
jakelazaroff.com
jakelazaroff.com
And for that typical CSS usecase, I find Tailwind way better both for quickly iterating and for hopping into code someone else wrote. I love not having to bounce between and cross-reference 2 (or more!) files (HTML + CSS) for a description of a single layout.
I started using Tailwind at my job I started 2.5 years ago and after the learning curve of memorizing the commonly used class names I have yet to run into a situation where I wished we weren't using it. I also started adopting it in all my personal projects because it makes me so much faster at getting work done and the code is easier to maintain.
I really feel like people start with their conclusion that they don't like it because it's weird and foreign and then look for excuses to justify why they think it's bad.
Oh wow. Literally this is the exact argument I use for not using tailwind - not having my styling logic split between classes in my HTML and tailwind’s own files implementing the styles (which appears in DevTools and most other places).
Not saying either of us is right or wrong it’s just odd how different people perceive things.
Does your framework do CSS in it’s component files? If not, maybe that’s the problem?
And for the record, tailwind was never meant to be used exclusively. Most arguments against it are centered around edge cases which are tiresome with tailwind... But they're usually pretty silly as you were never meant to abstain from using css if it makes sense.
And yes, that means you'll still have to check multiple files if something behaves differently then you'd expect, it's just less frequent then without a utility css framework
Yes, you will have to know what the classes do, but where that isn't already intuitive it's just a one-off lookup, and then you know.
HTML + CSS can simply not be parsed without hoping between multiple files. Unless you inline all of your CSS, of course.
You have styling split between the classes and the actual CSS tailwind implements, which every team I know that has used tailwind absolutely does have to look at.
I'm personally scared of forgetting CSS if I use Tailwind extensively. I also don't like the aesthetic of all the classnames being bunched together, but Tailwind does have value.
If you decided to do a project in vanilla CSS, I'm sure the style names would come back very quickly.
I don't think Tailwind is the place to start learning CSS. Tailwind is just a more efficient way to write CSS, and you should be aware of what's going on under the hood. It doesn't mean if you're writing your CSS in Tailwind you're never gonna look at normal CSS. There's plenty of times when I'm iterating on a design and I'll hop into the "Styles" section of my console and directly edit the element.style{} section, writing normal CSS to mock something out before translating it back into Tailwind in my editor.
So I certainly haven't forgotten normal CSS names. Not to mention the Tailwind names generally match up pretty closely with the underlying CSS names. It's only minor translations to go between the two, and maybe a special case here or there. The documentation[1] is very good with a quick search can type CSS or Tailwind names into to find what you want, and it'll show both the Tailwind name and the CSS attributes/values it outputs so I tend to keep that open in a tab while I work.
As someone who has been writing CSS for approximately a billion years (actual number more like 13 years) I'm a fan of Tailwind and don't see how it's a 'beginner' thing at all. It solves real-world problems building web applications, and actually many of those problems only come up when you're building large, long-lived applications with many contributing developers which is precisely the kind of thing beginners aren't in charge of.
For example, building your own type system or JS framework would require a lot more code and man-hours than using margin: 4px vs m-2.
With Tailwind, whatever the actual merits, the biggest points of traction (IMHO) are the frankly excellent example sites[1]. You can click through numerous examples, all load quickly and without ads, that give you numerous variations of components, layouts, and even full pages. You can spin up a vanilla HTML or React app and ship good looking pages very quickly, esp. if you are bad at design.
[1]: https://truerev.com
> Tailwind allows them to add a few classnames and the page looks great
How is this the case any more than "raw CSS allows them to add a few styles and the page looks great"? You can't just haphazardly throw styles onto the page and it'll magically look good cause it was made in Tailwind. You design the exact same way you would if you were writing raw CSS, it's just you're putting the styles in the HTML class tags instead of modifying the CSS directly.
A person who's bad at CSS is not gonna be any better with Tailwind.
What it does is ensuring Locality of Behaviour, and css properties not leaking in other component of a web application.
It's essentially the same as css scoped to a single component.
If you copy the examples in the Tailwind docs it looks like your ripped off Stripe. If you copy the examples in the MDN css docs it looks like you ripped off a decade old govt site.
And then they end up with dozens (and in big project hundreds) of bespoke CSS class names that no one can remember or keep track of, and just add new ones to the ever expanding file ;)
I used to sort of like CSS-in-JS, but on larger projects it also quickly devolves into "I know we have a design system, no idea what we have there, here are one-off CSS styles for this particular component"
Everything is with its trade-offs alas sigh
What are the benefits you're getting from CSS-in-JS?
Emotion isn't without it's flaws though. The company that created Emotion doesn't use it anymore because of runtime overhead[2] so Tailwind may be the future after all.
[1] https://emotion.sh/docs/introduction
[2] https://www.infoq.com/news/2022/10/prefer-build-time-css-js/...
<div class="flex gap-x-2">
which translates to display: flex;
column-gap: 0.5rem;
[1]: https://tailwindcss.com/docs/space#limitations[2]: https://tailwindcss.com/docs/gap
Edit: formatting
At least from this article, I get the impression the author used tailwind for a bit and kept running into these issues where he had to go in and fix it manually in CSS. After enough of these, you get annoyed.
If they're typical things that you'd expect a library to solve, sure. If they're "I want my element to rotate in 3D" then most developers would think they're just working outside of the scope of the library.
Which... is asinine. One is simply a redacted and simplified expression of the most common implementation of the other case.
Tailwind falls into the same bucket - in my opinion. Like most frameworks, it's not there to handle your niche and complex use-cases. It's there to provide bumpers that push you into a pit of success for common use cases.
When you need to do something that's not common - use the escape hatches that are built in. That's not leaking - that's giving you the flexibility to fall back to a custom implementation.
The goal just isn't "abstraction" from css. It's common-sense, local, and consistent styling for common cases. I can build most lego sets from single square blocks - but it's nice to have a bigger selection available. Does that mean I need to understand those bigger pieces to make sure they fit, don't have weird gaps, and line up? Absolutely. Does that mean I shouldn't use them? No, probably not. But it does mean that sometimes I still need single square blocks.
Let’s look at typescript, or sass. There is (afaik) no construct in the underlying js or css that cannot be represented using them.
You could conceivably never learn javascript, learn typescript and basically be fine.
So when people say “leaky abstraction” they seem to be assuming that tailwind, which is a framework will allow you to represent every possible css output.
…which it can’t do, obviously, and was never intended to do.
All I’m pointing out is that the expectation that it could might not be quite so daft when viewed from the lens of “I expected tailwind to be a framework in the same way sass is” or perhaps “the same way this framework that compiles using sass is”.
It’s like if I was using vue, and found that the template language can’t express complex logic without writing js (h() expressions) and not using the template language at all.
It’s wrong to say it’s a leaky abstraction; but it’s maybe not completely asinine to say, it a bit rubbish that you have to do that as soon as you hit any non trivial use case…
> Like most frameworks, it's not there to handle your niche and complex use-cases. It's there to provide bumpers that push you into a pit of success for common use cases.
…but as you say. Quite so. Im just saying I can see the frustration people encounter when they have the naive expectation that those edge cases are rare enough they’ll never hit them, and then immediately run into them.
I don't love Tailwind, but I can understand the spot it's trying to fit in, and I value the things it's trying to offer (locality, consistency, sane guidelines).
I think the sweet spot for that value proposition is projects where the designs don't need to be pixel perfect or extra fancy - they just need to work, quickly. I've used it successfully several times in those cases. Not to mention personal projects where just having a decent template made that I can tweak is great.
I would probably not use it anywhere you have real UI constraints or concerns, and you care a lot about specific animations or exact pixel placement. I tend to reach for styled-components in those spots these days. Since it gives me a fair share of the same value proposition (locality, consistency) but removes the guidelines/bumpers.
They play pretty nicely together (with some constraints), though - again, use the different lego blocks where they make sense. Stop trying to treat everything like a tailwind shaped nail you have to beat in with just that hammer.
You can play this "leaky abstraction" game with almost any concept, without the ability to reach down an abstraction level you lose flexibility. Its not a smell to be slightly leaky.
All abstractions are leaky; some are useful
Also, it makes me sad to see people so eager to throw away separation of concerns :( Yes everything describing it is on one screen but now your HTML is filled up with hundreds of words of awful classnames and markup that should have been put elsewhere!
I don’t think it has ever lived up to expectations, HTML+CSS in that way is simply broken. You can’t really write CSS without hard-coding the HTML structure, so you might as well do it in one file.
I don't see the issue with 'separate files', do people not use split view mode in their IDE?
edit- this comment in another thread: https://news.ycombinator.com/item?id=33789859
The problem with reusing CSS comes, when the style you’ve previously created, is almost, but not exactly perfect for the current component. Changing it to better fit you current need risks breaking existing layouts, and so you are left with overriding it. But when you do that, you eventually end up with a lot of override classes just to change one or two things. So, what do you name these classes? If you name them for what they are intended for (ie. “semantically”), you are bound to end up with a lot of differently named classes, that all do the same or nearly the same thing, because it turns out that we commonly want to override the default styling in very similar ways. You end up with dozens of one-rule classes that all just set float to right or display to none. Alternatively, you name these overrides for what they do. This way, whenever you need to float something right, you just add the `float-right` class to the element. You can now eliminate all those redundant semantically names override classes. But if you go back to the classes you started with, it also turns out that those would have been a lot more reusable if they’d contained fewer rules to begin with. This makes sense, because more rules = more specific, and fewer rules means more general. At this point, I’m sure I don’t have to point out to you, that you’re well on your way to reinventing Tailwind.
I'm my experience the _vast_ majority of CSS never gets reused. Sure if all you are building is marketing websites or landing pages then I suppose some generic "cards" or whatever will get you pretty far, but for anything even moderately more complex... no chance
Maybe it would've been possible to build tooling around using CSS files that created the same benefits, but now we just don't have to any more.
Yikers this comment sets the stage for a real bad game of phone tag! I think you're mistaking TailwindUI[0], the set of proprietary UI component cheat codes based on top of TailwindCSS, with TailwindCSS[1], the actual CSS library being discussed here.
Having a neatly encapsulated component that does everything is fine in most setups. Your main challenge will be not growing these components too big.
(( I really dislike Tailwind btw, mostly because I am just translating CSS into something else all the time which feels like a waste of mental effort; and the long lines of cryptic classnames is just bizarre to gain such a big popularity. I loathe it, it's a maintenance hell. I imagine all frontend devs liking this grew up using Bootstrap or something and mostly do small projects ))
This type of drama or negative style of writing needs to stop. Be objective, there is good and bad everywhere.
Practically none. And any designer is prone to reuse the same spacings from project to project. So you bring the language file with you. Something like this :
:root { --spacing-sm: 1rem; --spacing-md: 2rem; --size-tall: 32px; ... }
And you can do some pretty nice things with these pure CSS tokens. Without any hassle or dependency.
Say I have a button component with different variants, these variants change styles across multiple elements in the component.
Using scss and BEM this is incredibly simple, add a parent class and I can modify nested elements easily. But using tailwind I have to make an mess of things to get this working or recreate the component.
I’d love to hear how anyone solves this issue.
This completely misses the point of Tailwind. The point is not to hide the complexity of CSS, but to provide access from the markup to enough of capabilities of CSS that you don't have to edit your stylesheets 95% of the time, when you alter the styling of a document.
When Tailwind does fall short because it doesn't provide the exact CSS feature that is needed to solve the problem, it still provides a template for how to solve the problem: Using one-rule utility classes, that are named for what they do, not for how they are meant to be used.
This makes Tailwind seem like a lot of machinery (in the sense of bundle size, running code, and pseudo-language) just to avoid opening a file at dev time.
Layouts are often "collaborations" - the effect of multiple items interacting. You'd still need to open multiple files to understand what was going on. Or read the DOM in the browser. When I read component code, I almost always prefer it when the style _isn't_ in the same file as the JS. It takes up space on the screen and gets in the way of reading about the behaviours.
I don't get the propertied benefits. Granted, I've still not used it.
Locality is one of the most important things in code. If I'm reading line 38 of foo.js and something elsewhere in the app effects that line, I want that something to be on line 37 of foo.js, not line 158 of bar.js.
The problem with Tailwind isn't the co-locating of HTML and CSS. It's that it does it by adding an entire new layer of complexity that's basically a copy paste of an large standardised API with the names of everything changed. It's barely even an abstraction.
It’s not about avoiding opening a file. It’s about avoiding the risk and/or cost that comes from editing a stylesheet that applies globally. If you change existing rules, you risk breaking the layout somewhere you didn’t anticipate. Or, to avoid that, you treat the stylesheet as append-only, leaving you eventually with enormous files and selectors of ever increasing specificity.
Tailwind (or Tachyons) makes for ugly markup, but it solves a real problem, and it does it better than anything else.
If every selector in a file begins with the component class, isn't it the same as writing styles in the component JS file ? I mean, the only real benefit I see is the forced encapsulation in JS compared to "soft" encapsulation in CSS. But, it's something so trivial to learn and practice that the soft part shouldn't be an issue. And if it is, then it is time to learn some rigour, no ?
Given that, the separation becomes more of a pointless burden than actually doing anything useful.
The most magical moment for me when I started using Tailwind was realizing you can simply cut-and-paste an arbitrary block of HTML from one place to another and have it just render exactly as you'd expect. You don't have to additionally copy-paste a block of CSS, and then fudge around with the selectors because in the previous place it expected to be in some parent container or what have you.
The content will be inserted from an API or with a templating system.
since that is not the case, tail wind is great, even the biggest complaint about tailwind, bloated html size seems like something you can just compile away.
This is, of course, XSLT, which was supposed to take over the web at one point.
I sort of liked it at first. I even added the opportunity for my users to modify the CSS to add new styles, and share these to other members of the community via a configuration page. It was a neat thing but not many people bothered and in the end it made everything more complicated (and I guess I was really scared of vulnerabilities the whole time, what can go wrong with letting your users set the stylesheet.)
Worse, I think this new approach broke the web as it was: an explosion of flash-layout or photoshop--or-fireworks-cut-layout websites. It used to be beautiful. Now the web is just a series of websites styled by CSS. It's sad.
phones (and their screen size + apple killing flash) really killed the beauty of the web IMO. Now everything's an app.
We're not living in that future.
But the CSS ecosystem is still useful, and still adapting to our needs today. Tailwind might be considered one of those adaptations.
I think one could argue that the raison-d'être of tailwind is to hide the Cascading part of CSS, and along with it a lot of the complexity of actually using CSS.
And the problem with editing style sheets when dealing with document style, is?
Yes. Tailwind CSS is just CSS:
> A utility-first CSS framework
It is not a leaky abstraction, because it isn't an abstraction. It's a only a special syntax for applying predefined blocks of CSS to HTML elements.
If fact it’s a genuinely good idea to do so. Grid first, using paddings and gaps can do almost everything you can draw 1:1 without hacks.
No margins, no clear fixes, no floats needed.
<div className="px-4"></div>
versus doing .mycomponent__wrapper {
padding: 0 1rem; // Don't forget the time to look up whether it's vertical | horizontal or horizontal | vertical because I forget all the time
}
then <div className="mycomponent__wrapper"></div>
and toggling between files, and making sure the team knows my preferred CSS layout structure in the general case.
Plus, the solution for more complicated CSS is to just write it as CSS and to add the class directly like you would before. The point of tailwind is that I don't actually like or care about knowing CSS trivia, I just happen to know it. There's no value to me in doing things directly in CSS unless it's complicated enough to justify going to that layer. A helper layer is perfectly fine.0 vertical margin when centering horizontally, so Y must be the first axis.
Current gen frameworks will put the styling inside the component so styling is literally a matter of scrolling down.
<div class=“login”>
… .login {
padding: 12px 6px;
}
Yes you need to know the order I’ll give you that. Still less overhead than adding tailwind.Now if there is a go-to-definition for CSS classes, that would be quite useful. But I appreciate that I don't have to even think about putting a name to my styles with Tailwind.
.login, input.username, input.password, .instructions, .error
If it’s a sea of nested divs, so it’s hard to find the right element (a common pattern among “I just know react” developers), then fix the sea of nested divs.Also means you have to think of a name for every component you want to apply style to which is surprisingly tedious.
padding and margin go `top right bottom left` just like in a clock. So it always start at top and go clockwise. The two argument shortcut is just `top right` with bottom=top and left=right.
This is simply not the case. In order to use Tailwind, you need to know names of Tailwind classes, which are not part of the CSS standard. To say that Tailwind is just CSS is like saying that Ramda is just javascript.
Your classes in your project are not part of the CSS standard either, by your definition.
Tailwind gives you CSS classes that you can use. Yes, you do need to learn them. But that doesn't mean it isn't CSS.
Yes, but my classes, by definition, are defined by me. The behavior of classes of a css framework is defined by someone else, who exposes an api for you to interact with these definitions. An api that you need to learn, in addition to just CSS.
It is no different from React, Lodash, Rxjs, etc. being "just javascript". They provide an additional DSL on top of the language itself.
How would you define abstraction then?
I don't think tailwind really sets out to abstract away CSS, so that criticism doesn't mean much.
With tailwind you still need to know CSS. On top of that, you need to know how tailwind maps to CSS. From that perspective it's harder than CSS.
But of course, we don't connect styles directly to elements that much in practice because we need a way to organize and group styles. Hence connecting styles to classes and classes to elements.
In practice you end up with a styling language expressed as classes on elements.
But the app specific, ad hoc ones tend to bet messy, inconsistent, poorly documented, have many holes, etc.
Tailwind just gives you a well-thought-out, well documented, common class styling language. It's also nicely customizable, so you can use its tooling for your app-level stuff as well.
It does leave an app-level organizational hole, IMO -- that is, where do you express your app-level styling decisions. Tailwind has some features for this, but the grouping/sharing aspect is weak. But "component"-oriented frameworks fill the gap very nicely, making them a nice complement to tailwind.
I used it recently for a project with a custom design and it worked out very well.
I don't need to scroll up to find my css-in-js definitions, I don't need to change windows to find the CSS modules, it's all there, in one place.
A bigger project with multiple pages having full on Tailwind css classes peppered everywhere looks to be a nightmare.
Does anyone have experience jumping into an existing larger project with legacy tailwind all over the place? Would you do it again?
In fact, I'd expect it to be more maintainable, exactly because it removes the indirection of CSS classes. They're an abstraction that don't make much sense if you're already using components, and the very point of that abstraction -being able to reuse styles- is the very reason it quickly becomes append-only. With Tailwind, you can safely delete styles, knowing it won't affect styles elsewhere in your project.
Tailwind is a different way of writing CSS, with some guard-rails coming in from the design system consistency. Tailwind also provides sane defaults (rem over px, for instance).
If someone's bad at writing or thinking in CSS, chances are, they'd be bad at Tailwind too.
I've found Tailwind helps me focus on writing the CSS that matters. But just having Tailwind CSS in a project won't automagically fill one's gap in CSS knowledge.
This isn't bootstrap, where one can use some col-* utilities and magically get a grid layout that just works across browsers.
Tailwind is a bit lower level, and to achieve similar responsive layout, you still have to know how to do that with CSS grid, what the breakpoints are etc.
Where Tailwind aids here, is by providing utility classes that help with original CSS grid intuition. For instance, `grid-cols-2 sm:grid-cols-4 md:grid-cols-6` would be hard to achieve by hand, writing vanilla CSS or styled-components.
> A bigger project with multiple pages having full on Tailwind css classes peppered everywhere looks to be a nightmare.
Ideally, you would be using a framework to help organize code better. Most likely React or Vue or something similar.
In which case, you'd already have components.
There are more than one way to achieve same look and feel, with CSS. Similarly, one can apply some discipline with using Tailwind's utility classes, when building anything.
Two heuristics that have worked for me, are:
- Never use margin if you can, use flex-box and grid with gaps instead. Placing a children node is parent's responsibility.
- Write symmetric CSS. Prefer px over pl or pr, mx over ml or mr etc.
Using margins make it hard to extract some React markup as a reusable component. Using symmetric CSS gives you automatic RTL compliance without using any of the CSS logical properties.
Sure, there'd be times when a UI design cannot be implemented without breaking some of these. But in most cases I've encountered at work, building consistent UIs, have been easy for me and my team following these.
Happy to go into details with code examples, if anyone's interested.
Padding > Margin can also be a good idea, even if working vertically. Padding cannot collapse which is usually what you want. Obv, ymmv.
We had to use `rtl:` directive only in one component, where we were animating translation property, of a switch toggle. `translation-x-` is RTL specific, so we've to apply negative translation if `dir="rtl"` has been set.
Otherwise, this symmetric-utilities-only approach worked really well across dropdowns, modals, tooltips, buttons, inputs etc., even when we integrated these components in different existing products across multiple teams.
I hope one day Tailwind CSS team release an update with underlying CSS for px-, mx-* being replaced with CSS logical operators for corresponding properties. Not sure where the browser support for the same is currently.
Good idea, but importantly only applies to flex and grid layout, not paragraph-like blocks of content.
It is not a good practice to use flexbox for layout, especially if there are nested flexboxes. It takes a lot of time to recalculate such a layout, which can be seen when resizing a desktop browser, or when advertisements load on the page and the layout shifts.
”Write symmetric CSS. Prefer px over pl or pr, mx over ml or mr etc.”
Usually but not always. For example, text boxes can look more visually symmetric with less padding on the ragged side, while interaction-heavy mobile layouts may want to have a larger padding on the right to give the user something to safely scroll.
What does this mean? What's the difference between the two?
Container blocks contain other container blocks or content blocks, but never both at the same time.
Content blocks are paragraph-like content and may contain blocks (headings, media) and inline content (leading and body text, captions).
Example hierarchy: layout > main content area > hero element > positioned container > content block > heading
So far I just miss CSS modules. Having my .tsx and .scss files both visible in split screen wasn't so bad.
As soon as you need some customizable componentes, e.g. a Button, you have to write much code with Tailwind.
Or what about a consistent usability and UI over your App, you must edit hundreds of files, if you change some basic output.
Every other UI framework or library that I’ve used felt like leaky abstractions. You always have to learn both how the library works AND how css works.
Tailwind isn’t even an abstraction, it’s just a layer of convenience to be used where helpful and ignored where not. The thing I love about Tailwind is that it feels like CSS. In fact, I feel like I know CSS better for having used Tailwind.
But the author finishes by saying "Maybe you really want to avoid coming up with names." And yes, I really do want to avoid inventing names and I'll tell you why.
If I have two projects both written in Tailwind, it's trivial to switch between them and make confident adjustments. On the other hand, if I have two projects with `.btn.btn-small`, there could be an infinite number of parent styles affecting that button in unpredictable ways. I don't just need to understand what `.btn` and `.btn-small` do, I need to understand _all_ of the parent selectors which could affect that button. The moment you write the equivalent of `body.single-page .entry-content .btn.btn-small {}`, your code is essentially unmaintainable.
All abstractions are reductive – the mercator projection sucks compared to a three-dimensional globe – but some abstractions are useful and Tailwind is very, very useful to me.
But why would you do that?
That's awful CSS. And nothing calls for it. There also are tools for linting and analyzing CSS that would flag this atrocity. Everything is there for us to use CSS in a good, simple way.
For most of the work a frontend dev does Tailwind is just a nice shorthand library that makes life a bit easier for basic stuff. In that regard it's no different to Bootstrap, Bulma, Skeleton, etc. If you want to use it for absolutely everything, and you expect it to have every possible design element included, then you'll be disappointed. Just like you will with every other library.
90% of the time the abstraction works nicely. You can think in CSS and mentally convert to Tailwind pretty easily, or you can just learn Tailwind and occasionally have to delve into the docs when you're using quite niche styling.
It's not perfect, but that's not a reasonable criticism. No library is.
Tailwind's @apply just lets you apply Tailwind design tokens to CSS classes. It's useful for keeping designs looking consistent if you're already heavily invested in Tailwind. This is probably not much of an advantage unless you're working at a very large organization with many teams and web properties. And there are other CSS-native ways of enforcing design token consistency without Tailwind too. Open Props is a good example (https://open-props.style/).
@apply is also a great lazy way to just slap a string of Tailwind classes from mark-up into a class. It doesn't sound proper but it works in the context of iterative development.
but tangentially to this, that's why I like tailwind, its really not all that prescriptive but a tool that speeds up my development un-equivocally more than Sass ever has.
The more you repeat yourself the better it compresses
I haven't actually tried using this feature yet though (haven't had time to upgrade tailwind in the project where I've been using it). I suspect writing your CSS by hand could still be more efficient in per-byte output, but not enough to justify the productivity loss from not using tailwind
Now Tailwind scans for which classes are used as you add them and builds an output from that. So if you only used `m-auto` the only CSS it would output in the final build is
.m-auto {
margin: auto;
}In development I like to generate ALL classes that are possibly available because it allows me to play around in the inspector and everything is available. But tailwind has that workflow in mind too with with Just-In-Time compilation. (I still prefer my way)
Anyhow, on a production site that I work on the final file size is 316KB (35kb gzipped). With a bit of tweaking the config I could get that down to around 250kb.
But writing CSS -- usually with SCSS, sometimes bootstrap, sometimes just totally DIY -- I have _never_ managed to keep my CSS well organized. It always eventually becomes an unholy spaghetti mess, including overrides of overrides.
So my interest in tailwind or "utility" approach generally (I have started using bootstrap utility classes more), is not to avoid having to know CSS, but to avoid having to write CSS that I don't know how to keep from turning into an unmaintainable unperformant mess.
(It makes me feel ashamed. I feel like I'm pretty good at writing maintainable code normally. But when it comes to CSS, I have never managed to learn how.)
I do see how you have to know more in some ways, and have two levels of abstraction to debug or reverse engineer when looking at existing code -- tailwind and the underlying CSS, which, yes, pokes through the abstraction. It does seem like describing tailwind as a "language" basically right. That "cost" may well be worth it though.
I was recommending it to a friend who does wordpress development, but it occurred to me that it might not be useful (or not as useful anyway) in that environment.
For React, all the alternatives I've used have really sucked.
The WP theme templating system has partial file includes, so creating "components" is certainly possible.
Adam's argument relies on your CSS being tied to your markup in some way. If they are related, then the code should be co-located as well. Splitting things into separate files/names creates the scenario we've all found ourselves in where you are afraid to delete "old" CSS because you don't know if someone else has started depending on that behavior. By colocating the CSS with the HTML, you are now 100% sure that deleting that bit of "CSS" will not impact anything else.
This is arguably a good thing! It's long been a principle that code should be easy to delete. Tailwind enables you to "delete" CSS with confidence, in addition to its other main feature as a Token/Design/Style guide enforcer.
0: https://adamwathan.me/css-utility-classes-and-separation-of-...
The common technique of defining components encapsulating related markup and CSS makes repetitions in the output pages harmless because they are write-only, but it only moves the problem one abstraction step up (families of components with related CSS).
Why?
This means that any stylesheet I use is inevitably going to affect, at most, one React component. And so I can call every actual container simply .container without worrying about conflicts or namespacing.
The call sign of a happy Tailwind user is someone who doesn't want to use Bootstrap anymore, switch tabs constantly to their CSS file, ever have to learn how the fuck animations work, and who instantly falls asleep when trying to learn the proper way to do CSS, even after having to deal with it for 20 years. SCSS? God no, just give me something where I don't have to think and I can make it instantly do what I want. I'll try random stuff for an hour until it works before I have to try and understand and take in something new, the resistance is real. I don't care if it's ugly, I don't care if it's MORE WORK, because for ME, it's not, and I can read the classes quickly and know what is going on and adjust it in real time alongside my content. Do I augment with regular CSS where needed? Yes, because the limitations can be frustrating, but I've only got like a handful of extra classes I need.
I'm one who hates to say I like it, because I hate the hype around it, but by god it works so well for me compared to anything else I have to do and I'm able to deploy and edit sites that look decent without being or having access to a designer, and those sites make money for me and pay my living.
Can I ever hope to compare to a proper designer or someone who has a solid command and understanding over what to me amounts to the most boring and droll thing ever: CSS? No way, they are wizards and they should hate this impure garbage, but for the rest of us it just works good and gets results with the least amount of thinking, which is all we really wanted.
I got shit to do, and it's the fastest, least resistant way to let me do it in the way I know how. It's not for everyone, but it fits the way I currently work, think, and how I value tradeoffs in speed, effort, debt, cleanliness and results. YMMV.
No abstraction is going to address 100% of the underlying concept, but that doesn’t mean it’s not useful. I’m on a team that is currently fawning over Tailwind. I am extremely skeptical, but they really like the idea of being able to wholesale delete legacy styles, and they like the idea of not having to learn all the intricacies of some css oddities.
Tailwind reminds me of Coffeescript: embraced as a language until ECMAscript woke up and started fixing some of the languages deficiencies.
I am skeptical of Tailwind. Personally I wouldn’t use it over vanilla CSS, but it’s gotta be solving some problem to be as popular as it is. I think that problem is that CSS kinda sucks to learn.
I'm a fan of Tailwind, and use it a lot. There's no way you can use it effectively without knowing CSS. Contrary to the original articles statements, it's not really an abstraction. It has some very very minor abstractions like `space-between`, but these are the exception rather then the rule. Most of Tailwind lines up 100% with one specific rule in CSS. You have to know CSS to use it.
I like the old school approach of having a clean and semantically correct HTML without any styling information. And then implement the "theme(s)" in CSS.
Makes just more sense to me and goes along with the idea of CSS - separating style and content.
But the recent adoption of tailwind make me think a lot.
This becomes even harder when you refactor/reuse things.
It is then worse when you want to maintain.
With an approach like tailwind, with non-specific css, the coupling is ensured, the css is re-usable, the patterns are re-usable, re-factorable with a single copy paste.
I don't use tailwind yet, but just using exclusively utility classes changed my ease in writing, refactoring, re-using and maintaining code by an order of magnitude.
(I am no frontender, though I was a "full stack developer" up to CSS 2.0)
For example, you can't specify hover, focus etc. style attributes. You can't specify screen sizes either. Plus there are few Tailwind classes that cover several attributes.
Plus many of those styles are just unwieldy. For example, `drop-shadow` is `filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));`
> You still need to know CSS.
Yes, that's been the position of the Tailwind team from the get-go. In the author's defense, when users compare it to Bootstrap, that doesn't help.
The author isn't wrong I don't think with his observations, but to many people using Tailwind, I think the "downsides" are worth it for the massive gains in other areas.
One thing I believe will be changing in v4 (tried finding relevant tweets but came up short) next year, is the a more concise syntax to eliminate much of the repetition the author identified around nested selectors. For me personally, I would probably see those types of usage as an anti-pattern or a code smell. If you need extra classes on a structured list like in the example, I would opt to add that logic to the templating loop rather than use CSS to select every 4th option inside of every 2nd other option.
A lot of people seem to like tailwind though so I am probably missing something.
Yeah I agree with you there. If you're writing tailwind you have to constantly have the tailwind docs site open.
To use your "rounded" thing as an example:
Design systems should have their own definitions of what a "rounded" thing is. The designs also probably have different types of border-radius based on if it's a container element, or a button, or a pill button, etc. So you define those as different types of "rounded". Small, medium, large. Round, rounder, roundest. They could be whatever you want. This way, your team doesn't have to remember that 10px = round, designers or PMs just say "round" and you use the "round" utility class.
Or if they don't like styling they can say "hmm what about 'rounder'?" and you use the rounder class.
That's what Tailwind aims to solve is to 1) define a common language across the team and 2) make that easily available for the FE dev to implement versus trying to cross-reference what "round" is.
Anyone who builds UI for a living, ideally already having a well-developed mental model for how CSS works, immediately understands Tailwind's power in my opinion. Anything merits critique but this article just feels like rationalizing a preexisting bias.
Just chiming in to say I'm an outlier in this respect. I've been doing front-end focused dev for well over a decade, using every system imaginable, and I'm yet to see the appeal of Tailwind. I have tried it, and many of my use cases are supposed to be what it's best at (rapid prototyping etc), but I still dislike it in many aspects. That's fine though, if it works for other people then all the power to them.
It's likely that because many of the things I'm building are relatively bespoke, and therefore require breaking out of Tailwinds boundaries (like the authors examples), that I haven't gelled with it yet.
I've been using Tailwind for all my recent projects and it is an absolute gift. Yes, it doesn't take away the need to learn CSS, but it takes away a lot of the tedium in producing behaviors that are expected in modern websites. I used the lovely clay[1] library a lot to handcraft CSS before I started using Tailwind and I don't think I want to go back.
No
The benefit is it makes writing CSS faster. Writing CSS in the markup just makes more sense. People say that markup and style should be separate to adhere to "separation of concerns". But in fact, markup and style should be coupled, because they both pertain to the UI, visuals, and aesthetics.
As a counter: I see a lot of bad markup ordering when you turn off stylesheets or use a TUI browser. You should be laying out your markup in a logical sense (within some reason) where it's easy to parse without styles and navigate with a keyboard—decoupled from styles. CSS grid and the `order` property let us easily move around elements for visual ordering and that this can (and likely should) happen shows that the data layer (HTML) has separate concerns from the visual presentation layer (CSS). Yes I want my first button tabbed to in your modal to be the primary action button even when it is displayed to the right of the cancel button and that's a heck of a lot easier to order it ‘correctly’ and style it in a different order for aesthetics or better visual parsing.
Yes! Yes it is! And that doesn't matter a single hoot! Go and write your complex CSS to do your perspective shenanigans, that's absolutely fine. Don't be a purist who MUST HAVE UTILITY CLASSES AND ONLY UTILITY CLASSES. And you can't, and shouldn't, write tailwind while being ignorant of CSS.
You've set up a strawman and knocked it's head off, but I can clearly see you boxed a scarecrow.
Tailwind is not a CSS revolution, it's just a slightly easier way of writing (and learning!) responsive/interactive CSS. That's it. The hype is just that it actually fulfills that brief.
Yes? It's not meant to solve 100% of your css tasks, for me it's more like 90%.
I use Tailwind a lot, and I end up breaking out of it a lot. I don't think there's a designated 'correct' way to use Tailwind. It's a shorthand. When I need to do something that's easy in the shorthand, it saves me time. When I need to do something more specific, like applying some styles to all the anchor tags or something, well then yeah of course I'm not going to use Tailwind.
I think the hype machine is probably to blame. Many would probably come to these conclusions on their own, but they've heard people say TW is SO amazing, that they feel they must be missing something.
Basically an approach that leverages the advantages of utilities and blocks and embracing the `Cascading` in CSS instead of working around it, like BEM et al. likes to do.
The biggest benefit is that you get a fairly well thought out API to work with. In the case of Tailwind, this a pretty flexible and good set of defaults that works for 95% of use cases. You can focus more on building classes for cases specific to your site, and not spend time rebuilding undifferentiated layout utilities.
A) Some experienced front end devs have a real issue with it, and B) People who use CSS in passing, like me, tend to really enjoy it.
I want an abstraction over CSS, it makes me life easier. I'm not anywhere advanced in front end as the OP, so maybe that is the difference. But I realize it is what it is - utility classes. I don't get the issue here really - just dip down to CSS if Tailwind isn't doing what you want.
I prefer SCSS, having used both as well as Bootstrap, just because I don't think Tailwind adds much more than it takes away.
CSS has slowly been creeping up on the frameworks too, providing native layout implementations that erode the benefit of frameworks.
But there's data in the widespread uptake of Tailwind. CSS purists need to understand why it has been so widely adopted, rather than just saying it's the wrong way to do things.
Because the truth is, in a typical development organisation, SCSS turns to mush just as quickly and easily as Tailwind does. Perhaps you can argue the mush is better because it's separated from the HTML instead of exploding class attributes.
But since Tailwind has a dev speed advantage, it's still ahead.
There's also data in what became the most popular CSS framework: BEM. This was specifically designed to contrain the cascade depth of CSS, one of its biggest problems from a maintainability perspective.
There's a saying that "one should program INTO a language, not WITH it", which I think rings true here.
It's not about Tailwind vs whatever. It's about what are the actual problems we are encountering with styling, especially at a birds eye perspective of years and organizations, rather than me and now, and how would we address those from first principles.
Not: pick a book off a shelf and call it gospel.
I find css-as-props is better than a huge string of classes, as it much more readable and statically analyzable. I wonder if anyone is working on a successor to styled-system (though it works fine).
I'll take tailwind over Styled any day
Bootstrap got trashed regularly for being the Times New Roman of web development and any mention of it online would summon a howling pack of CSS purists. However, back in it's heyday, you could join a team and immediately start to make an impact because you knew the CSS class names and what they did. You still had to have someone around who knew CSS extensively to work out some corner cases. However, it definitely let the juniors get up to speed and when they hit one of those corners, provided a great mentoring opportunity to help them grow their knowledge of CSS.
I have no idea what point you're trying to make.
I was refuting this as it is not really the same, and pointing to you something (a UI library built with Tailwind) which is more logically comparable to Bootstrap.
You seem to have missed some words. Please point out how Tailwind is not a tool that makes working with CSS more standardized and simpler for someone just sitting down to your code base?
Literally the entire original comment was just that Tailwind fills much the same role as Bootstrap did, just with far more capabilities. Thus, it engenders the same flame wars that were fought over Bootstrap. You responded by saying, "It's a superset of Bootstrap."
Which is what I was saying by "Very fancy Bootstrap"?
Whenever a huge hype wave hits HN, I now just wait for the huge catches to appear... especially if it has anything to do with web platform stuff.
If something hangs around for a long time like Go, Rust, etc. and I see really serious non-hype-driven people buying in, that's when I figure there might be something to it.
If you know CSS, you have huge problem to use a tool like Tailwind, cause you always feel like "how does this save me work?" it doesn't.
I get why other people like it and I'll go with it flow when other people have started a project with it, but I feel much less productive when I have to go remember its syntax and behaviors instead of...CSS.
The best css projects I ever worked on were led by people who were just good at css. They didn't write a lot of shared styles. That's my main complaint about poorly written js or css: code that depends on other code without good or self-evident reason. The argument "it's easier to write" imo doesn't hold up because you read code 10x more than you write it. Good code optimizes for reading not writing.
I'm partial to css that relies on primitives like the `rem` unit and css-modules.
It absolutely does. Why would you speak for everyone who "knows CSS"?
PS: I know why. Because they built it without thinking about the users (web devs). Instead they were thinking about all the f-ing edge cases almost no one cares about.
Can you give an example or explain more about this?
What seemed to help us (and I don't condone this for everyone, it just worked for us) was to go more extreme: No CSS without a bulletproof defense in a PR. The only exception we needed to break in our large scale platform was to style an SVG progress indicator with complex gradients. Angular's behavior around [ngClass] not just adding classes but removing them also was a bit tricky. Personally the behavior in whole feels like a bug but I know it isn't.
Some of his points are valid about maintainability and complexity, but that can be resolved by building smaller components to share markup and classes. All depends on the use-case.
Disclaimer: Maintainer of Atomizer.
Yes sometimes there is janky corners you will find (notably, the multiple copies of modifiers for elements that have a lot of (for example) hover or responsive properties) but for 99% of everything it works exceptionally well and has contributed to a dramatically faster app than the prior last gen product with an extremely tight style guide enforced by Tailwind's config.
It's not new, interesting, difficult, or complex. I don't have much opinion on whether it's actually good or not (I happily use modular CSS), but the subject keeps popping up over and over.
I've found this useful when I notice that I am reusing styles for things that repeat again and again, like form fields or buttons.
https://tailwindcss.com/docs/reusing-styles#extracting-class...
TW classes are a lot more concise and provide a "design system" vs raw values and browsers are good at applying classes vs parsing inline styles for every element.
It seems to be mostly a matter of where the performance penalty is paid.
Browsers either parse CSS (mostly) "ahead" of HTML (tailwind CSS classes), or as they parse HTML (inline CSS): they still got to parse a very similar amount of CSS, except if you've got the same "rule" applied in a number of places (though in that case, element-based CSS rules probably win-out).
Do you perhaps know of any benchmarks where the actual real-world effect is visible?
> parse HTML (inline CSS): they still got to parse a very similar amount of CSS,
I have no idea how it works but I imagine there is a difference to
parse class.css
parse el1.html
apply class to el1
parse el2.html
apply class to el2
vs parse el1.html
parse el1.css
apply style to el1
parse el2.html
parse el2.css
apply style to el2
Honestly the argument pro/con performance is irrelevant to me, writing this 100 times class="text-lg"
is immeasurably more ergonomic than writing this 100 times style="font-size: 1.125rem; line-height: 1.75rem;"
Or even worse class="shadow"
and style="box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"
and that's only one style.I do get the terseness point, but unless you are completely happy with Tailwind definitions, it makes code less maintainable rather than more IMHO: instead of having a familiar place to look up the "shadow" definition (if that's not really doing what you want) in your own CSS or HTML file, you need to look at tailwind CSS inside your dependencies, or inside a debugging console in your browser.
I am also unhappy about any code where you'd have to write even `class="text-lg"` a 100 times: perhaps I am still living in the foolish dream of semantic markup, and I would hope you can structure your document in a way where styling is context-dependent: so perhaps you put class="text-lg" on an "top-level" element, and then only adjust nested element text sizes where needed.
You can't. So you fall back to classes and CSS and now your styles are split in the HTML AND css. Tailwind, in a very basic form is exactly this solved.
Imagine you have a React component you render once for each element of a JSON array:
products.map((product) => (
<li style={{color: "rgb(180 83 9)"}}>{product.name}</li>
))
which would be the equivalent in tailwind of doing products.map((product) => (
<li className="text-amber-700">{product.name}</li>
))
In this case, the `class="text-amber-700` attribute isn't going to be much different in size than `style="color: rgb(180 83 9)"`, but tailwind has other utility classes such as ".grid-cols-3" which would be equivalent to an inline style of "grid-template-columns: repeat(3, minmax(0, 1fr));"It also lets you set up a minification pipeline which turn the "text-amber-700" example above into something like
.xJ2Xa9 {
color: rgb(180 83 9);
}
...
<li class="xJ2Xa9">Awesome product #1</li>
...
I'm sure you could minify inline CSS similarly, but I suspect the tooling there isn't as polished. li.product {
color: amber;
}
...
<li class="product">Awesome product #1</li>
...As for the mentioned nested selectors (which I'm a huge fan of), in the next version I'd like to see grouped nested selectors. So instead of:
[&>div]:mt-4 [&>div]:bg-white [&>div]:px-2
You could do something like: [&>div]:(mt-4 bg-white px-2)
Which I think would improve readability and significantly cut down on class bloat.Presumably the next dev you will hire will be a frontend engineer? CSS is a core competency for a frontend engineer, whereas Tailwind is an add-on. If I were choosing a frontend engineer for my team, I would make sure there core competencies are up to snuff.
class="rounded shadow p-4 pb-1 bg-white"
than: class="blog__rounded-box blog__rounded-box--padding-fix"
But I get your point. It's the same as the article, right? You still need to understand CSS to use Tailwind, so why bother? I guess each to their own. Maybe we'll regret this in a few years, but it works very well and feels much more maintainable for us now. I've certainly worked on enough projects (and, mercy!) had to pick up enough projects where the 'vanilla' CSS was absolute spaghetti!> I've certainly worked on enough projects (and, mercy!) had to pick up enough projects where the 'vanilla' CSS was absolute spaghetti!
And I am very guilty of having written such spaghetti in my first big project :-) But authoring FE code as components, and encapsulating styles inside of components (with either CSS modules, or scoped styles) has helped tremendously. Perhaps web components will save us, by allowing full style encapsulation inside the shadow DOM, thus eliminating CSS-at-scale headaches altogether.
Of course! That's the Tailwind add-on, right? I guess the key being you can move between projects that use Tailwind and know it's a rem unit of padding all round and .25rem padding at the bottom. And when you come back to it 3 months later, you still know.
> And I am very guilty of having written such spaghetti in my first big project :-)
Oh, me too and not just the first! It's amazing how quickly things can turn to pasta when you're rushing to hit deadlines! :-/ But I think you're right, with good practice everything gets a bit more maintainable. Let's hope we get there eventually!
I don't think tailwind should be used everywhere. Sort of like a CRUD framework, it could really get in the way of doing bespoke or atypical things the underlying language can do but the framework or library explicitly wasn't intended for. Breaking convention can become a hurdle.
The reality is that the vast majority of API work (for example) does benefit from a CRUD framework of some sort, be it lightweight or otherwise. CSS is great, but do we need its full potential at every corner of the UI? Probably not. What we need, usually, is a consistent set of conventions we can shovel out an get good, repeatable, familiar results which benefit both devs and users in the long term.
Tailwind supports custom classes for exactly this reason. You need an escape hatch because it isn't the holy grail of all CSS projects. It's a helpful tool with well-known limitations.
If I need to batch out projects in my shop, I will build jigs and find the right blades and cutters for my machines. Then I'll rip through the various settings to get the pieces I need very quickly. This is like Tailwind in the workshop. If I need absolute control, I need various hand tools and a lot more time. Can I create something awesome? Absolutely. Is it inherently better? Not really. It depends entirely on what is needed in the end. Generally speaking, most of us just want a dumb box to put some stuff in. Tailwind will do that expertly, just as my table saw and router with jigs will.
Asking your tools to do something they weren't intended to do will cause headaches, yes, but it becomes more an issue of user error than tooling problems at that point.
What’s number 2 right now?
writing: "p-1 md:p-3 md:text-h1 hover:md:scale-95 hover:underline hover:text-blue hover:scale-105" is super annoying compared to something like:
"p-1 text-h2 md:[p-3 text-h1] hover:[underline text-blue scale-105 md:scale-95]"
While length is still kinda long, it is far more clear what each modifier is impacting. Plus a pattern like that would discourage this mistake:
"p-1 md:text-h1 hover:underline hover:text-blue md:p-3 hover:scale-105"
Where quick work causes out-of-order classes that will produce the same result but are harder to debug due to the split off modified class.
If you could compute the names dynamically it wouldn’t be an issue, but alas.
Tailwind is really amazing, and I was a vocal naysayer for a long long time before eating the “mindset shift” cost. Easily one of the biggest productivity boosts I’ve seen in years.
It might use parens and commas though.
Example: `hover:(bg-blue,text-5xl,px-8)`
I've used pretty much every styling variation you can imagine, CSS-in-jS (styled components, emotion, component libraries), vanilla CSS, and I after landing on tailwind . I can honestly say I have never had so much enjoyment and velocity building User interfaces.
If its missing a fragment you want, then you can add that fragment - either as a custom class, or as a tailwind "managed" class.
Managed classes basically mean a bit of magic (compilation) around sizing, etc is applied.
The compiler then strips out all the stuff that doesnt get used.
The "leakiness" that the author mentions seems to be focused around elements "injecting" styles into their child components. They then go on to suggest alternative underlying css that may be better.
Maybe this should instead be an issue/PR raised against the respective managed classes rather than a criticism of the framework as a whole?
I don't understand any sort of DX/UX that requires horizontal scrolling.
It is very rare that a DX/UX wouldn't be made better by designing it smartly to scroll vertically instead.
You could have a React `<Button>` component's styling go like this, with clsx and Tailwind:
``` <Button type="button" className={clsx([ "inline-flex items-center", // how its children nodes should be laid out "px-3 py-2", "bg-gradient-to-r from-blue-500 to-indigo-500", // background color stuff "rounded-md", // border radius "text-white", // text color of children nodes "outline-none hover:ring-4 focus:ring-4 ring-blue-500/40", // hover focus behavior "disabled:hover:ring-0 disabled:cursor-not-allowed" // disabled state "disabled:bg-gradient-to-r disabled:from-blue-400 disabled:to-indigo-400" ])} {...props} > Click Me </Button> ```
Imgur link on how it looks like in the editor: https://imgur.com/r0aF9oU
Here's a similar button component implemented with horizontal utility classes: https://play.tailwindcss.com/5sbxDmVvsZ.
There are other benefits to using a library like clsx. Since clsx accepts array of strings and returns a joined string based on conditional, output of one clsx call can be consumed by another clsx call.
References: - [1]: https://www.npmjs.com/package/clsx
It is true that I can have divs with a ridiculously long class, but that’s the moment that I know is time to make, say, an input component or a card or whatever and move up one layer of abstraction.
There are trade offs for sure but I’ve never been more productive or had less headaches implementing a design than with tailwind.
Eg .Btn for a bunch of styles that style a button element, then .Btn .—-large to make it large, etc, which uses more TW styles in my scss file to make it work. It saves me a ton of time and allows me to use the same template across many projects; if a color needs to change or sizes are different t I swap it out with css vars
border-radi
...for example, the plugin doesn't suggest... rounded
...because I didn't type anything close to matching that.It seems like the only way to benefit from Tailwind without having to switch between your file and the docs is to invest time memorizing the many, many, many shortcut classes that don't start with the same word or letters as the actual CSS property name you already know.
When you only work with atomic css for a short period of time and come at it with something to prove like the author, chances are your thinking about it isn't mature and fluid/adaptive.
You don't have to only use single-purpose classes, nor is your work limited to the api surface of Tailwind.
What we do when we bump into dead ends, like the author's reference to `perspective`, is Write CSS. Tailwind makes _extending_ itself as easy if not way easier than other comparable libs (and with _much_ less duplication).
This really was an interesting read. It shows the same thing can be seen as good and bad by different folks.
Link: https://www.unsungnovelty.org/posts/05/2022/explaining-what-...
Actually, `.ml-2` has the same specificity as `.space-x-2 > * + *`, both (0,1,0) according to https://www.w3.org/TR/selectors-4/#specificity-rules. This means that whichever is defined later wins.
That is not what abstraction means. If it is a "language" (as you describe) that essentially maps 1 to 1 of what the CSS itself does, it isn't actually abstracting anything away. The key trait of abstraction is information loss. Tailwind keeps you connected to the CSS you are using more so even than CSS in stylesheets does.
Instead of defining rules in style sheets they're associated directly to the element, which reduces the issue of having styles cascade all the way down.
The way people maintain CSS in ".css files" is really broken. Whenever something need to change quickly, a rule is appended with !important;
Tailwind introduces an approach that fixes that.
I think that’s the main failing of the Tailwind crowd, they want to “Tailwind all the things”, use their shiny new hammer for everything, even for things where it’s clearly not the best tool, like crochet.
Also, I really don't think tailwind is a replacement for regular CSS. I've found more success mixing them. "Promoting" a series of 10 utility classes to a semantic class name when needed.
But eventually people started pushing their agendas into the code/styles and we end we a bloated solution that works for everyone except those that want things to go back to slim. If tailwind continues to please the crowd, it is going to get bloated just like every other major attempt to murder Bootstrap did: RIP UIkit, Bulma, Skeleton, etc.
If you've gotten to the point where you're complaining about Tailwind CSS being a leaky abstraction, shouldn't the answer be obvious? Just use CSS.
You can easily add your own custom classes. You immediately jump to a complicated problem and say Tailwind is bad because it doesn't handle this edge case well. (btw, its not trying to handle it)
Tailwind is just another language to write CSS, at the same abstraction level as plain CSS, but with the benefit of being terse and in the same file as the markup.
Tailwind actually removes a large part of CSS's abstractions (The "Cascading" part), which gives the huge benefit of being able to copy paste code and know exactly how it will look.
Actually GUI Design is challenging, many people dont find Desktop GUIs intuitive.
I don't know how to calculate the amount of possible shapes one can have with RGB on a 3x3 grid.
In CSS, there are lots of shortcuts: `margin: 0 56px 10em`, `background: #ffffff url("img_tree.png") no-repeat right top;`, `font: italic small-caps bold 12px/30px Georgia, serif;`, etc.
You often have to reference the order and style of the rules. You cannot use them in all cases, and you may need to merge these shortcuts with regular rules.
But to call shortcuts a leaky abstraction that you hate is simply stupid.
Tailwind is a bunch of shortcut CSS rules. You may have to reference the docs as you get used to it, you cannot do everything with it, and you should mix it with other CSS.
But on the other hand, it makes your code much more readable, consistent, and easy to get out the door. Well, well worth the effort to integrate. Not worth bowing down to.
Shameless, but relevant plug: Tailwind had, IMO, one major shortcoming, and that was no support for children/descendant/children. That was kinda fixed with arbitrary selectors, but I still recommend my tailwind-children[0] library to everyone.
It serves well, even in large code-bases, when you aren't doing bespoke transforms and object selection.
I've built large UIs using it and its magic. For the most part what you want is consistent spacing and reasonable defaults; Tailwind nails this.
Tailwind is not for beginners, it's a dependency reversal for professionals.
Tailwind is not replacing CSS, it's embracing the realities of component libraries.
Tailwind is a paradigm shift...
Working with Tailwind CSS every day for 2 years - https://news.ycombinator.com/item?id=33787719 - Nov 2022 (117 comments)
there is some repetition, but it will keep things simple.
same thing for spacing example, have a margin prop on a flex box container and apply it on each container then wrap them into a flex container.
it will be easier compose to compose vs css class variation.
For instance: What is tailwind? Why is it important? What ecosystem does it live in and what problem does it solve?
The author looks more like: I heard leaky abstraction, tailwind is an abstraction, therefor it is leaky. Then shows "problems" with Tailwind (tailwind wasn't supposed to solve) that are not leaky.
Also several of the commentators here misunderstand leaky abstraction
"This is what I call a leaky abstraction. TCP attempts to provide a complete abstraction of an underlying unreliable network, but sometimes, the network leaks through the abstraction and you feel the things that the abstraction can’t quite protect you from."
Tailwind is super effective at inlining the common, basic CSS tasks. Further, Tailwind does nothing to prevent you being able to write good ol' CSS.
The examples OP have picked feel niche, contrived, and likely solved in better/simpler ways. I would never dream of writing something like this:
"lg:[&:nth-child(3)]:hover:underline lg:[&:nth-child(3)]:hover:font-bold lg:[&:nth-child(3)]:hover:text-blue-600 lg:[&:nth-child(3)]:hover:opacity-100"
That's just bad CSS. That's not bad Tailwind.
A fair conclusion.
Find me one that is not.
But after a couple of projects using Tailwind, I actually think it's a really solid way to implement styling that scales well as a project/team grows. It's maintainable because it's tightly coupled to markup (like styled APIs), has great first class support for design tokens and once you learn the syntax it can let you express specific css rules in a way that's more clear and concise.
I don't know if it'll have much staying power as the front end community continues to explore how we want to define styles. But what is clear from Tailwind's success is people want high levels of performance, styles tightly coupled to markup, a first-class way to utilise theme tokens etc.
For now if I was to start a new project, I'd likely pick Tailwind unless there was a good reason not to.
In reality, I don't think that's necessarily a bad thing. When Rails came out, people ranted about how PHP was bad practices because it mixed display and business logic. Look at what the frontend has become now with React and Nuxt and the like, basically just PHP all over again. Is that bad? Well, people seem to like it, so I don't think so.
In other words, I totally agree with this article but I've given up trying to make any sense out of the frontend world. Tailwind is for people who don't want to learn CSS[1], which seems like a frighteningly large amount of web devs. But, ultimately, that's OK. I will continue to happily ignore it.
My hope is that will some CSS changes landing soon (in particular :has and nesting selectors) we will see more respect given to CSS as a declarative language for defining UI, instead of just some boilerplate to make your page look pretty.
[1]: see replies below, this is wrong :). nisegami said it much better: Tailwind is for people who don't want to use CSS as it was intended. It's like a simplified execution model for it.
I wholeheartedly disagree with this. You need to understand CSS to be able to use tailwind. Tailwind's class names are close (most of the time identical, even!) to the pure css equivalents. Furthermore, concepts like flex and grid need to be understood to produce what you want.
This reveals a fundamental misunderstanding on your part about Tailwind. Tailwind classes map very directly onto CSS properties. You can't use Tailwind effectively without knowing about the corresponding CSS ideas (although you can be unaware of how exactly to apply them in pure CSS).
Rather, tailwind is for people who don't want to _use_ CSS as it was intended to be used. Named classes and stylesheets just don't mesh with how my brain processes these things.
I should have probably said "it's for people who don't want to deal with cascading", but IMO cascading is CSS's most powerful property. I can see how it gets confusing though.
I think that when :has becomes viable, we will open to the door to CSS-driven declarative UIs as opposed to imperative JS components, but this is pure speculation :-). I believe that a development style that takes advantage of cascading instead of fighting against it could perhaps become one of the new 'schools' of frontend development. I do not think it will become the most popular one, though.
Inline CSS doesn't support lots of CSS that is critically important, like media queries, pseudo selectors, etc. That's the big reason why inline CSS should generally be avoided. There's nothing intrinsically wrong with it otherwise.
You're almost certainly using a templating language to generate your markup anyway, so if you're worried about the need to repeat common styles then factor out the common style declarations into shared variables in your template language. Yes, it's a way to avoid writing CSS, but look what it gets you: you've reduced the number of languages and files you have to manage and context switch between when developing. Typically this is programming language + template markup language + HTML + CSS + JS.
Eliminating 20% of your cognitive overhead is nothing to sneeze at. You can add something like htmx and remove most of the JS too for a 40% reduction. Front-end development actually starts approaching something close to pleasant.