Why I don't like Tailwind CSS
aleksandrhovhannisyan.com
aleksandrhovhannisyan.com
I switched to TailwindCSS a long time ago and I have zero regrets. Its so awesome I can switch to a project that is 2 years old, and make a quick layout UI change without having to look at old CSS files to find classes etc. I also can just find a tailwind class name and apply lots of cool stuff that I've normally found difficult to do via "pure" CSS files.
But I also feel that this is likely to be a thing that half prefer A, and half prefer B, and there is nothing wrong with either. In this thread is likely to be a 50/50 split between both camps, with different "ideologies" on how the front end should be done.
As the author of this article says - give it a go. If you like it, awesome. If you dont, switch back to another tool, there's not a definitive "right/wrong" answer to any of this.
In a million-line codebase, you can add and remove classes from a specific element with 100% confidence that you are not breaking anything else. With class-based styles, even when using theme variables, there is always the chance you end up breaking something at a distance by adding a property to an existing class, changing a shared value, renaming a selector or changing it's precedence.
If you find yourself in this environment where dozens of people are making changes concurrently, and these issues are a daily occurrence, naturally you'll wonder how you ever worked with CSS any other way.
styled-jsx and other approaches can get you similar benefits but extra care is necessary. I mostly avoid Tailwind due to the lock-in, complex setup and slow build, otherwise might have used it for quick prototypes more often.
However, the Tailwind team are very close to releasing something that is, apparently, going to dramatically speed up build times (source: Adam Wathan's recent tweets).
Isn't the flip side of this that it makes it almost impossible to keep styles (visually) consistent across the codebase?
.button {
@apply py-2 px-4;
}
This ensures CI elements are styled the same way everywhere, but still relies on the Tailwind config to figure out what „2 abstract units of vertical padding“ mean in your application. .button {
padding: var(--medium-padding) var(--medium-padding);
}
Or use LESS/SCSS/etc if the var(--) syntax is really that offputting.The point of Tailwind is that it’s a straightforward way to handle all the CSS complexity and put the moving parts into a single config file that works the same everywhere.
Tailwind: https://tailwindcss.com/docs/theme#spacing
Would it be strange to define p-2 to be smaller than p-1? Yes. Exactly as strange as defining your padding-medium variable to be smaller than padding-small.
I sometimes wonder if people even understand what technology they are using:
CSS = Cascading Style Sheets
Go read some history, as to why exactly they were created. What you are describing is not CSS at all, that's properties on a design grid.
Which goes back to the author's point - if you want or really need that, don't use CSS. Just use components with standalone properties.
Use CSS when you want to be 100% sure it will change everything else.
A large company with frontend specialists and CSS experts might not need Tailwind - and indeed they might find it gets in the way. Personally I find it lets me build and maintain decent UI layouts with minimum fuss. When I'm repeating myself with too-long class strings, I can either use @apply or wrap the markup in a component.
Some people did have a visceral hatred of Atomic CSS, but even those people were able to solve problems with it very quickly, without having to dig on tens of CSS files that happened to affect a single component.
There's only two ways to avoid it: having very small, very cohesive, teams, or using tools and methodologies that avoid this.
This is one of those things that gives me the feeling the true "10x" engineer mostly just writes code to some reasonable architecture standard and does their best to avoid tech debt. Speed in the longer term means avoiding these situations if you ask me
Hmm, that would be interesting data to use to analyze the CSS quality of a site. If you used the sourcemaps along with a rendered html page you could find which elements had styling from the most separate css files which might give you clues on where to improve your CSS.
However that also means its perfectly ok to have one’s own system (cf. twitch, github...). Why not use a standard though? Tailwind saves us so much time coming up with a solid consistét utility set.
And then combine it with Sass
If anyone familiar with how Tailwind works lands on a new project, regardless of its age, this person can immediately understand the front-end code in a matter of seconds, without having to look through thousands of lines of code in a separate CSS folder.
I don't understand why Tailwind is "harder to read", as the OP wrote. It makes it much easier to read as what the code does is immediately understandable.
Others view compartmentalisation as important to ease of reading, letting them focus on one level at a time.
I wonder how well the preferences for tailwind align with people's views on unit tests in the same source file?
I had to eg. fix a stacking context issue (z-index) a so called full stack dev could not solve. Basic css technique.
For someone creating layouts from scratch tw is very verbose and templates can become difficult to reason through while you develop them. Easy to disregard for devs who just jump in a ready made template and make smaller changes.
I want to echo the author's recommendation of styled-jsx. I've been using it with React for a few years now and love it. Like Tailwind, the styling is conveniently co-located with your elements, but it's just regular CSS properties with their full power instead of a set of aliases crammed into the class property. It also encapsulates the styles to the component's own elements by default, which is very good at encouraging making encapsulated components. The codebase I'm working on used to have a mess of CSS files, and CSS files for various screens often messed with the internal styling of common components used within the screen which would regularly cause us surprises whenever we updated those common components without realizing how they were abused across the application. Now that we're using styled-jsx, proper component-level encapsulation is enforced by default, and it really helps guide us toward making the components themselves be responsible for their own styling needs, which keeps things much more understandable and more local with less "spooky action at a distance" (external styles modifying the component's internals).
I've also found the tailwind.config.js very useful - you can load it up with all your custom colours and fonts. It's much easier to refer to text-primary (for your primary brand colour) than remembering a hex code and pasting it all over your markup :)
<span style="color: var(--color-primary); font-weight: var(--weight-extrabold);">my text in primary color and extrabold</span>
I believe Tailwind's syntax is still far terser and more manageable: <span class="text-primary font-extrabold">my text in primary color and extrabold</span>
When you have lots and lots of styling this quickly becomes importantIf I’m going to use custom properties it would be in a way that plays to CSS’s strengths.
.card { color: c(gray-700); }
Same for font sizes, font family definitions, screen sizes.
I like having to remember less stuff, within my scss definitions.
It is better than inline styles, though. Inline styles can't use media queries or pseudo-selectors, are difficult to override due to having a too-high specificity, and you have to copy values around (CSS variables also solve that though), among other issues.
<div class="lots of stuff">... or <div style="lots of stuff">...
one can do <style>
.something { lotsofstuff }
.something:evenwithpseudo { otherstuff }
@andevenwithmedia {}
</style>
<div class="something">
and just have lots of small <styles> everywhere.That's basically how elm-css[0] does it. One writes css in the attributes of elements, and then when it's converted to DOM-stuff the css is extracted and classes created (or reused if similar). Since it's all elm-code one can reuse variables, call functions, import styles from somewhere etc
[0]: https://package.elm-lang.org/packages/rtfeldman/elm-css/late...
At this point one has to ask themselves if they're "avoiding inline styles" (or anything that looks like it) for a true technical reason, or just because of cargo-culting.
I believe that Tailwind is a trap for people that don't like CSS, because if they one day come to a project with regular CSS there will be a lot of unlearning before they will start working at full-pace.
I might be biased because I love CSS and I find it the easiest thing in web-dev, especially now - without IE in the way and with flexbox + grid fellas, not to mention components. It gave me a lot of guidelines how to think about abstractions and how to build them. This translated nicely to other aspects of programming.
I can't speak on the validity of this for all devs, but for me, I really enjoy CSS and I love Tailwind too. CSS is there for an escape hatch when I need it, but then I can use the utilities for a lot of mundane styling and placement.
Tailwind can absolutely be used for boring layouts and designs but can also be used for really cool custom stuff like this Diablo 4 landing page from a few years ago[0]. People made cool stuff with Bootstrap and then a bunch of crap also got made.
Tools ebb and flow and it's up to us as devs to find what's working for us now and then get the work done. Worrying about the future before you even get started doesn't make sense imo. The reality is very few sites are being worked on continuously for more than a few years, so trying to optimize for that potential maintenance burden doesn't make sense. If your site continues to grow and it becomes painful, that likely is a good problem to have and you can address the issue when you have the team and budget to do so.
[0]: http://web.archive.org/web/20191222080351/https://diablo4.bl... & https://twitter.com/tailwindcss/status/1190423327590027264?s...
Tailwind is a tool that's absolutely for skilled CSS people (you still have to know what everything is doing). And it is much more maintainable for a large number of people working on styling.
Do you? I'm sorry but Tailwind takes quite a lot out of the CSS equation. You only have to learn what each property is doing. Then for example C for cascading, being a great feature of CSS, which with Tailwind you don't even need to know it exists.
Tailwind is just shorthand for CSS prop/value keys that also gives you an easily customizable and maintainable design system, which an increasing number of developers understand. That means that you can be productive immediately on a new codebase instead of having to spend hours getting your head around the multitude of CSS files that may or may not have been organized in a way that makes sense to you.
See - floats and negative margins are one of the code smells if are used to position things (yes, they have valid applications but in very limited use cases) that make me believe, that if you are still using them in 2021 then my worries about how Tailwind make dangerous shortcuts are valid.
You're still using CSS...just more efficiently.
Tailwind is much closer to raw css than some of the SASS, LESS, and Compass mix-ins I have had to wrangle.
Maybe the difference is that other people build apps with lots of components. When I write HTML I mostly build web pages - a lot of text, some images, and maybe a menu.
I need to be able to tweak properties among a lot of different elements at the same time. E.g. change the font face on all elements in the main area, change the color of all links that are not special links, rotate all titles and underline them, and so on. It's an iterative approach, as I evolve the design in HTML (I don't have a designer team that hands me a psd I just have to copy). To do this with Tailwind, you'd have to use components, and you'd have to add another layer of abstraction to affect the right components together.
And I know nobody really uses CSS Zen Garden style themes - but it is great to be able to take a HTML snippet and use it in another project with minimal changes and a different CSS design.
CSS has shortcomings - it's missing a way to "include", it could have more complex selectors, it can get unwieldy in larger projects - but it is also does a couple of things really well.
For example I have used Tailwind with Styled Components so I have isolated CSS for components and interactions, and consistent classes for layout.
It really makes maintenance a dream. When you update a component it is clear what CSS is tethered to it, so you don't accidentally leave unused classes around or tweak classes that are used elsewhere with unknown results. The bigger the app, and the more developers the more this becomes useful.
Of course for personal sites and blogs and projects many tools are overkill. But I have saved so much time, bugs and effort by moving to Styled Components. Tailwind feels like what I previously used bootstrap for.
I can hand write the CSS but I want to be able to quickly switch columns and fonts and alignment in a cross browser, mobile friendly way while writing the HTML on the first pass when working professionally.
Almost everything on the web is basically an application, or runs on an app. Anything based on wordpress is an app. On Wix, or Rails, or whatever. They're all apps.
Almost no-one has static websites.
So your use case, which CSS might actually kinda fit, is the niche that CSS should actually ignore, or at least treat as a 2nd class citizen.
Edit: just as whatever tooling you use to build content or dynamic runtime interactions in the browser are ultimately going to be HTML and JavaScript respectively... even if those aren’t the languages you wrote.
Wikipedia is documents, news sites are documents, wordpress blogs (that are not shops etc) are documents. Even in this forum, when threads are locked after some time, they become documents. Even if they are dynamic behind the scenes.
Naturally, if you are writing something like Twitter, Gmail, any SAAS app, ... you need a different approach than when building websites for documentation, essays, news, and so on.
Anything that uses dynamic pages to generate content, like wikipedia, is a web application and benefits from more structured, class based, CSS.
99% of the web is run on web applications. Whether it be Wordpress, Wikipedia, a news site CMS, whatever, they're all applications.
So by components you meant react components not css components?
The thing about writing the CSS to make a button pretty is that you only have to write it once (it's also usually no more than 10 lines), and then you can reuse it as much as you want. So forgoing a framework has some up-front cost, but in the long run it amortizes out and makes things easier to maintain imo.
Now, if I were churning out a new website every week or every month I would not be starting the CSS from scratch each time. I would probably carry around some starter CSS from project to project myself, though this would also be a good case for a framework.
Just write your CSS selector to target whatever you'd like, then use `@apply tailwind-classes md:other-tailwind-classes` inside the brackets.
This way, it's just a set of shorthand tags that replace the long-form CSS props, but it's still useful for quick design work.
I never understood this argument, at this point you are just writing CSS with extra steps and unnecessary abstractions.
With Tailwind: defer decisions about assigning classes until you've built enough to see what the proper abstractions are, then just refactor out the repeated utility classes. There's even plugins that will consistently sort your Tailwind classes so that refactoring can be automated by simple search and replace.
I don't want to argue whenever it is good or bad, but it solves my problems. When you are deep down some html component, being able to style it without having to find the css file is very pleasant. The code is also more maintainable as there is no "domino effect". You change what you want to change, and you are done.
As for vendor lock in, tailwind is MIT licensed, I don't see the issue here. Of course if you use X, you are going to depend on X. And if they release a breaking update, just pin the old version. I'm happy with improvement in tailwind 2, but to me the framework is complete, I don't mind it being frozen.
Concerning the long lines, I agree, but you can just put newlines between class names. Class name is a cdata list, newlines are ignored: https://www.w3.org/TR/html401/types.html#type-cdata What I do is that I group classes by function, like color on one line, margin/padding on another...
Is this a template based project with global CSS? That is not an issue at all if you're using an actual component system.
> tailwind is MIT licensed
I don't think the lock-in has anything to do with the license, but how hard it is to migrate away from it. You're locked to its build tooling and converting the styles back to anything else is a rewrite-everything-from-scratch proposition. Whereas migrating from say, styled-jsx to styled-components is a lot easier.
Yes, I use phoenix live views, which is just HTML templates with global CSS. I know there are tons of "real" CSS component systems, but live views has been really a life saver to me, and I don't need offline apps.
Concerning the lock in, I think it makes sense only if the vendor will stop supporting it. I mean, if it works, doesn't change it, even if it means forking the repo and having it sit somewhere. The security and "bugs" are handled by the browser, the CSS framework can just be stuck in times, it shouldn't be an issue. Of course if CSS specs changes drastically, it might be an issue, but it would be for any other CSS solution. Also, we are talking about class names, this is fairly standard, tailwind didn't invent "utility-first" CSS, maybe not a drop in replacement, but I don't see that as a real lock in.
I believe it's still not the point. By vendor lock-in, the author meant that if one day for any reason (maybe size of the bundle due to many unnecessary class variants?) you will have to go away from tailwind, you will find it either extremely time consuming or even impossible (for example due to time/budget limits).
I know people still using AngularJS 1.x because it is too difficult or painful to rewrite it into another framework that is more up to date.
I've recently been working on updating a project from Bootstrap 3 to Bootstrap 4, similar pains are involved.
That's simply not true. The knowledge of Tailwind's class names, media queries, and tooling is not transferrable, whereas styled-jsx/styled components/css modules/emotion are all still based on CSS syntax and applying classes, moving between them is vastly easier than to/from a framework like Tailwind.
Same is said for moving from Bootstrap to some other way of managing your styling.
Different tools have different naming, different "class names", different "media queries", and different "tooling".
I can't take my direct knowledge of Bootstrap 3 and use it in a styled-JSX world.
Moving between frameworks is always going to be difficult and require rethinking how things are built.
Does anyone else get frustrated when people post blogs like this without actually using the technology in any meaningful way? I was also skeptical of Tailwind, and disliked it for the first 2 weeks or so of using it, but now I'm not so sure.
This reminds me of the phenomenon in outdoor gear where 75% of the reviews are people who bought a tent and set it up in their backyard and for some reason deigned it necessary to write a review.
And yes, I've handrolled my own and used bootstrap and bulma previously.
Tailwind hits a sweetspot for me when I'm trying to knockout components fast.
Yes, it's only in development, but it's still something I'd rather do without.
> Where does this anti-frontend-engineering madness end
I love frontend engineering and I would work on a tailwind-based project if I had a strong need or desire to work on it for other reasons. If I was picking between two projects and all other things were equal, I'd choose the non-tailwind one, though.
I think we’re on the same page about generally picking what’s right for a project, even if some of our individual choices don’t line up— and that’s totally fine! We’re lucky to have so many free tools tailored to so many specific needs, and it’s okay when some of them are very similar, they can still both be valid!
"I am fortunate to have barely scratched the surface of the tool, missed important details of how it works, and not used it in the situations where it becomes a lot more valuable."
The first week or two of using Tailwind is indeed awkward and a bit frustrating, but after that it's like a switch clicks and 3x productivity is unlocked. Especially if other devs are working on the codebase, or will need to in the future.
It’s okay if you don’t like something, but if you’re going to publish a hate piece like this you’re going to get criticism like this.
Also the tailwind is bloated is disingenuous given you just remove the unused classes as Pty of your build.
Bad faith points all around, maybe except the obscure naming in tailwind. But alternatives like Hucssley solve this (name the properties the same as their css counterparts)
&hover, &active, etc.. fuck that.
Tailwind might be simple, but it is undoubtedly opaque. A CSS class defined in the same application is not.
Something like...
<div class="button button-primary">
...isn't going to confuse anyone. I believe tailwind is hard to read because it's basically just inlining everything tersely with very little abstraction at all.Now that's not to say we don't use established libraries for CSS. But we definitely add our classes and add our customization.
In places where I might find tailwind appropriate, I could use just the even more common standard of bare CSS properties.
It basically boils down to naming things for me. The author says it's a good thing. I don't think so. Most of the time in plain CSS we have to come up with arbitrary names, that introduce confusion and bugs later on.
Utility classes, while sometimes cumbersome to read, always state their intent.
Based on the name I'm going to assume that the class is used on container for a layout with cards on the left.
Utility classes don't state their intent they state their literal contents. If the intent is to be a button or be a wrapper-container then that's in a well conceived class name.
> Most of the time in plain CSS we have to come up with arbitrary names, that introduce confusion and bugs later on.
If the names are arbitrary then of course have problems! If you code and made up arbitrary names for functions you'd also have problems. The point of the name is to add clarity and abstract out details I don't need to know. I don't need to know that buttons have a certain padding, margin, etc when I'm placing them on a form. And I don't need to know everywhere they are placed when I style them.
The reason tailwindcss has so many proponents is arguably because of its compositional design, but it's paradoxically not one of the first things people seem to talk about when they're discussing tailwind. They fixate on its utility-first design. I think this does it a significant disservice.
No, I don't know why. I'm not a designer.
That's funny my intention was to name a wrapper around a container inside of card that is usually used on the left side. But of course we had different contexts in mind and a lot of these issues can be sorted out by coding conventions.
The thing is I was never able to wrap my head around the whole separation of concerns thing, when it comes to HTML, CSS and JS. I think that is because I come from a mobile dev background where we always had some kind of markup (or just plain code) which described the elements and how they looked. The structure of the UI and the styling were always co-located.
Coming to the web I was very confused of why you would want to separate both of them. And in my opinion we can see that change not only in TailwindCSS, Tachyon or ChakraUI but also in component based JS frameworks as well. Everything is about colocation these days, which makes it much easier to reason about what is going on in this small, little, pocket of code in my application.
I like that change. :)
CSS properties always state their intent as well and IMO are even more explicit in what they are doing.
If you're going to offer "criticism like this", at least have something more to go on. It's not that ambitious to find a more readable solution, if a popular choice seems flawed. That's just a single hurdle to overcome.
The advantage of Tailwind [1] is very subtle over the short term (such as not having to constantly context switch between HTML & CSS files), but dramatically impactful over the long term.
Both in terms of time-savings, as well as code quality and ability to work with others quickly.
It really is something that you must earnestly try to gain an appreciation for.
My initial reaction was the same as the authors when I first came across it. Actually using it on a real life project changed my mind.
[1] - https://planflow.dev/blog/the-main-advantage-of-tailwindcss.
When I'm creating an element having it styled as-I-think, without having to switch to another file is a nice productivity boost
Especially if you have some sort of real-time rendering on your local server
There does come a time when Tailwind starts to click, you've memorised some of the class names and you get a feel for it. But, I still found myself consulting the documentation to do things "the right way" ultimately spending hours reading documentation, when I could have written plain old CSS. The maintainability aspect in the long-run is the selling point of a solution like Tailwind. CSS without some semblance of order can wreak havoc on a codebase.
These days I am using plain old CSS on a component level inside of Aurelia 2. My components are Web Components utilising Shadow DOM features which scope my styles to the component, but also using Constructable Stylesheets to reuse styles throughout my app. In combination with CSS Variables and Shadow Parts, it's an incredibly powerful workflow that takes a lot of the pain of CSS out of development, it's closer to the metal and feels way nicer.
Do you have a good writeup or example on the way you're working? I'm googling Constructable Stylesheets, but would love any other pointers, because what you're doing sounds like exactly what I've been wanting to do.
Edit: I see Constructable Stylesheets are Chrome-only. Unfortunately that's a deal-breaker for me :/
constructor(html) {
super();
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "/theme.css";
const template = document.createElement("template");
template.innerHTML = html;
this.attachShadow({ mode: "open" }).append(link, template.content);
}
Global things like vars, default typography, and input style resets go in theme.css.Also filesize isn't as big of an issue that the author makes it out to be.
> 1. Tailwind Makes Your Code Difficult to Read
...
> First, like Bootstrap, it’s semantically obscure because all of its class names are awkward abbreviations
...
> I would rather look at some CSS that has padding: 0.25rem or margin: 0.5rem instead of trying to mentally map Tailwind’s p-1 or m-2 to their CSS equivalents.
Please the latter is so much easier for me to read, and can (hopefully) be remapped from rem to px in one place, or to use increments of .5 instead of .25, which avoids manually changing potentially hundreds of selectors. Furthermore, using conventions for these kinds of class names makes the knowledge transferrable to another project without having to relearn/rewrite all of these.
> Another reason why Tailwind is so hard to read is because it requires you to pan your eyes horizontally rather than vertically. ... When you string a bunch of class names together, you get markup that looks like this
<div
class="w-16 h-16 rounded text-white bg-black py-1 px-2 m-1 text-sm md:w-32 md:h-32 md:rounded-md md:text-base lg:w-48 lg:h-48 lg:rounded-lg lg:text-lg"
>
OK, I get that this could be an issue if you're writing markup by hand, but for a large project this seems like a terrible idea. If you're over in JSX-land (or any templating, really), you'd have an array/set for classNames somewhere, and can just join(' ') it. If for some reason you need to have 50 classes, sure, line-separate them. In many (most?) situations you'd want to construct that array/set programmatically anyway, and encapsulate tightly-coupled display logic in smaller building blocksRecommending jsx styles has at least the same amount of shortcomings as tailwind, the whole post feels like „i dont like and you shouldnt either, because what i use is obviously better.“ :shrug:
I am pretty sure that with plain old css and our poor architecture we wouldnt be able to deliver in time.
I thought CSS modules have done that.
Unless you are also arguing against a build step.
People are allured by the rapid prototyping aspects of Tailwind which is the main bait. It's pretty fast to see results immediately without openining the css file.
This an interesting psychological point to discuss IMO, we get lured to things that feel "fast" but actually aren't.
Hey, wasn't that the original intent of HTML+CSS? To isolate markup and design?
It's not so much “feels fast but actually aren’t” as “fast onramp that makes downstream maintenance slow”.
That people tend to be bad at balancing downstream costs against current benefits is well known, and that's even outside the context of business situations where the cost is attached to a role that decision-makers may expect to be out of before the cost is realized, and outside of social contexts like speed-of-this-iteration-focussed development methodologies that structurally encourage discounting future costs.
If you are working on more of document project where markup reuse through components or partials isn't available then I could totally see your design elements like buttons or cards becoming inconsistent but your spacing, font-sizes, z-indexes etc. will still follow the system.
Unlike the author though, I have grown to love it - not having to look at two files to understand what's going on (html/css) really felt like a big boon, especially for "trivial" UIs that just need a bit of flexbox, css-breakpoints and a margin here and there to get going. For more complex stuff, falling back on custom classes is still perfectly fine.
Also, the "html becomes a mess" argument was only an initial issue. Once you get used to the tailwind-classes, most of it becomes fairly readable - the examples of the author are, in my opinion, just as unreadable in css, especially if you're not a design-oriented css-geek (like myself).
If I am currently creating the html and the css I don't have to look at both to understand what's going on. I only have to look at both if I am coming into some html and css that was created a while ago.
If I have to understand the html and css that was created a while ago and how it all relates I don't do it by looking at different files - I do it by opening up the browser and inspecting.
I suppose you have a different way of doing it that I have difficulty envisioning.
- yeah, I like having the whole project's code in one big index.php file. No context switching, no need to go through dozens of files (controllers, models, etc.) to understand what the system is about. No semantic naming at all. My productivity is high!
The majority of the comments here that praise Tailwind seem to be from people who are actually not " 100% frontenders", but perhaps "fullstack devs" or even "backend devs" that have to touch HTML/CSS from time to time. It's understandable that the practicality of Tailwind attracts people who work occasionally on the frontend.
I’m sure there are downsides, but the upsides so far seem to dramatically outweigh the downsides.
Let's say a client wants to add a custom page, with some custom design.
Where do I add that? Into the global CSS, where now it needs to be maintained? Do I use a body#custom_page selector before everything? I've got elements that I now want to override only on this page. The easiest way is using inline CSS, except I can't inline responsive and pseudo-states. Updating the 5000 line CSS file with a bunch of classes under a body#custom_page selector is hard to maintain.
Tailwind solves this problem, by effectively allowing me to use inline styles even for responsive and pseudo-states, and simple variables.
CSS is effectively global state. You make a change on one bit of CSS, it can affect any part of the website, so you have to know about the whole website before making changes. And that's impossible when you maintain 100s of websites, especially when budgets are small.
I can see why Tailwind isn't helpful in a large company maintaining its own large website. But for small teams managing lots of different websites, it's fantastic. It allows you to localise and isolate the effects of CSS.
What's the problem with the #custom_page selector? If inline styles have oddities, why not keep it in separate css files, one per custom page? That would seem to precisely match the cascading style CSS is supposed to handle in the first place.
That's a lot of files if you've got 200 related-but-slightly-customized pages, but it tracks the organization of the project as a whole, no?
This is exactly what we've done for years, but with tight deadlines, small budgets, long-running projects take on all sorts of crufty technical debt. Maybe I change a value in "special_page.css" and suddenly the homepage breaks.
Tailwind lets us isolate that cruft to where it matters.
It's not a silver bullet, but it's helped immensely. Initially, I thought it might just be "new broom syndrome", but after a year of using it, I'm still in love in a way that I haven't been since the launch of jQuery.
in a rare case where CSS actually seems to have an intent rather than throwing spaghetti at a wall to see what sticks
I'm sympathetic to this problem, having suffered through it plenty of times, but it seems like a scoping problem, and an entire library of specific functional CSS classes doesn't seem particularly well-matched to a scoping problem.
I believe everybody who says "Tailwind saved me the of work matching up CSS that was file-separated from its markup AND thinking about selector scope." And I guess this is one way to do it, but the overhead of a comprehensive property-to-class mapping seems inefficient and it's really not clear to me advantages it has over the method of the author of the article described, which also keeps markup and css in the same source file and manages scoping issues.
For example, I also don't like Tailwind (or, for that matter, Tachyons or any of the similar tools). I find it absolutely infuriating to work on a site that uses it, it is a massive drag on my productivity, and offers quite literally nothing that I want.
But here's the thing – I was a front-end specialist for years. I know how to write CSS and Sass, how to build UIs from them, and I have an established set of patterns and helpers for doing so. I used to be a graphic designer too, so doing things like building an application-specific design system is my bread-and-butter. For me, Tailwind is a massive distracting abstraction on top of a thing that I'm already completely comfortable using.
Other people aren't necessarily in the same position. It takes a lot of time and work to start thinking in terms of the way that styling works on the web, and CSS is pretty different to many of the languages and tools that developers are used to. If you're a back-end developer who is primarily interesting in implementing a straightforward and functional UI for something, then investing lots of time in the quite different skill set of front-end styling probably doesn't offer a good return. So there are sets of tools out there which make it easy to write simple, organised, and maintainable sets of styles, without having to get right into the weeds.
Of course as always YYMV and you might be a CSS expert who loves the consistency of this kind of framework. But I'd definitely back up the idea I've seen some other people mention – there's no obvious right or wrong, or objectively correct answer.
Of course, you don't have to do that—you could write your CSS classes as ultra-low specificity classes that each only target one property...but I think you'll find that's what Tailwind is :)
I spent many years doing frontends with CSS, then Less, Sass, and I have used all of the popular methodologies at some point or another. I am very comfortable building things that way, and it would probably be my preference, all things being equal.
However, developing with Tailwind is just plain faster than the other methodologies. And it's easier to adjust components in production. When it comes down to it, those are two areas that Tailwind excels in, and high-abstraction CSS methodologies (despite their other merits) do not.
Don’t knock until you’ve given it a fair chance.
what kind of argument is that?
A pretty strong one. If I'm maintaining a project, I really do not want to spend my time learning some random DSL made up of obscure class names with abbreviated letters and numbers just to change the padding on a div. The component based approach is infinitely more maintainable. Things like Tailwind are fine for quickly prototyping something if you can't be bothered with design or styling at the time. But long term they are a maintenance nightmare.
Framework components are indeed industry standard, and I'd argue that Tailwind (and Atomic CSS in general) also works better with them.
But CSS components (the alternative to Tailwind) are a hot mess. There are lots of methodologies, some of them are very complex, but even the straightforward ones like BEM are very error-prone when used by developers who haven't been using it for long.
I have used the trio styled-components, typescript, react which results in highly reusable, self documented UI primitives. You write css not some made up language and you map your modifiers (in BEM lingo) to react props which is then type checked by typescript. You can also use all the existing css tooling like sass mixins etc, not that you're gonna need them.
If you're directly coupling CSS components to React components, then you won't really see the issues me and GP are talking about. Names can be inconsistent because markup is centralised, it will be rarer to see overrides, people will be more reluctant to change it to fix one part of the website and then breaking all others... etc.
You don't have to learn some else's idea of what CSS belongs to a component and how they structured it. You just go into the HTML, add or remove a couple of tokens and you can be sure yoyu didn't break anything else.
Once you've learned it - which doesn't take very long - it scales to other projects. You don't have to relearn - again - what someone else thought would make a good CSS abstraction for their UI.
In a project where you have to name everything semantically you don't know where the hell the padding is going to be.
It's amazing for long term maintainability and quick prototyping. There is a lot of value in classes that does one thing well.
Been wrinting CSS for 15 years, switched to Tailwind 2 years ago, just for reference.
Just RTFM.
It's part of an overall code quality attribute I refer to as "discoverability" which is tied very closely to clean organisation and maintainability. Can't maintain something quickly if it takes 10 minutes to reverse engineer the code and find it.
```
{{ recent_blog_posts(blogPosts) }}
```can be a function that expands to:
```
<aside class="border-pink border-1 rounded bg-orange">
{% for post in blogPosts %}
<li class="text-underline text-purple font-list">...</li>
{% endfor %}
</aside>
```I think tailwind in React does make a lot of sense, but I think there are even more simple solutions to use inside React that solve the same problems but with normal CSS and none of the extra tooling and config of Tailwind.
You can think of tailwind like a higher level language for CSS, since you are writing inline CSS again just with more concise attributes. With that being it's only real advantage in my opinion, I didn't think it was worth learning a whole new dialect just to avoid a few lines of CSS. CSS variables can be solved trivially with an imported JS object, so the config file as well adds a lot of minutiae knowledge without delivering that much more benefit.
At any rate, it is for some people and not others. I'm not going to try too hard to objectively prove why I don't like it, it just adds a lot of tooling overhead while not really solving enough problems for me.
It's not just debugging: If you're doing any kind of testing on the UI you already need class or data attributes to identify the components you're using, otherwise you test becomes coupled to the structure, or worse: to the styling.
The difference of Tailwind to other methodologies is that the Tailwind classes are used exclusively for layout.
Programming with only utility classes, and patterns like OOCSS and BEM are bad, and we've stopped using these for literally decades.
The more important question is how do these libraries work under the hood, which is both little understood and rarely included in analysis. Breaking out CSS files is critical, any CSS created and inserted by Javascript is unforgivable in production. Focusing on what the libraries do in production is the key. And if your tool needs some special DSL to use native CSS features like @keyframes, don't use that tool. Period.
The author is right in that CSS is 100% a solved problem, and the answer is component scoped CSS with some global utility classes (which can come from Bootstrap or wherever if you choose to use such a framework). There's nothing better, it solves every need, and doesn't come with any of the baggage of other systems.
The beauty of Tailwind is that you can still define abstractions when they are needed and pay the price of using abstractions but you can also handle the many one offs every site has in a much cleaner and more direct way.
ah!! I was wondering why HMR wasn't working like I remembered the first time I used React. I think the first time I picked up React I used plain CSS, and the CSS would change on the page without reloading. Now I'm using tailwind and material-ui and thought HMR was just busted.
I like tailwind but it doesn't play well with material-ui. Material components always take precedence which is super annoying. I might just end up canning tailwind.
I don't understand what is impossible about this. A standard tree walker and a GUI tool to define the individual components will suffice. Or to make things simpler, have a command line tool that takes in the CSS selector/XPath of the desired tag/component and generate the relevant CSS. There are already browser extensions that can do this. Also, nothing is stopping you from writing semantic HTML. Vanilla Tailwind is an (almost) one-to-one bijective mapping to CSS. It does not define your HTML unless you are using prebuilt components.
Agree, and as someone who's done these conversions both ways I wanted to just say that you don't have to use complex tools to do what you're describing really. Those things are definitely in my toolbox, but simply just going by-hand and duplicating applicable styles from one site to the other with Chrome/Firefox devtools is a breeze.
I say this as a developer who embraces/studies CSS
With that said: this resonates strongly with my experience using utility classes (Bootstrap, not Tailwind, but I think it's close enough for these purposes). On the giant front-end codebase that I used to manage - which didn't use any CSS libraries or utility classes - here are some things I learned:
- It's okay to have semantic classes and also bundle commonly repeated styles under reusable classes. Most of our classes were component-specific, but we had maybe four or five cases where a particular set of multiple(!) CSS properties were repeated in all sorts of different semantic contexts, so we encapsulated those as "utility" classes. However, when it was just one property getting used frequently...
- We learned that inline styles aren't the devil. It's perfectly fine to do `style="display:flex; margin-left:1em;"`. The only disadvantage this has compared to utility classes is brevity. And in exchange, it's much more readable and carries zero bloat or lock-in. The verbosity also encourages you to move styles to a more meaningful place once the amount reaches a certain threshold.
- For consistency in styling/spacing/branding: just use CSS variables. Preprocessor variables work fine, as do native custom-properties. `margin-left: var(--m-4);` works just as well as `class="ml-4"`, with the added benefit that it can be used equally well inline or in a stylesheet (and, again, no bloat or lock-in). Another interesting benefit is that that value can be reused and assigned to other properties; for example, maybe in one spot you want to `padding-left: var(--m-4);`.
- Unpopular opinion: your HTML and CSS aren't really separate concerns in most cases. Sometimes you can find repetition that can be factored out, and if so that's great, but the CSS that controls your layout is and always will be intimately tied to the DOM structure it's targeting. Given this, in my experience, you're much better off having hyper-specific class names (most of the time!) and, if you find it helpful, some sort of componentization/scoping system to make sure they stay hyper-specific and don't leak (I didn't find that we needed the latter, though only because we followed very strong conventions).
That's just my two cents. In my decade of writing CSS I've come out against utility classes as a formal methodology, though I'm also very comfortable hand-writing CSS and I've mostly worked at small companies, so those factors and others may bias my opinion.
Sure, scoped CSS also handles this but most developers are not building JavaScript SPAs. They're building monolith applications or WordPress/Magento/Craft/whatever themes where a handful of stylesheets are included via a <link> tag.
Vendor lock-in doesn't mean that you can't get out at all, just that it has significant cost. Maintaining both Tailwind and another CSS framework/philosophy/classes in the same project has a significant cost.
React example:
``` import styled from 'styled-components';
const Button = styled.button` padding: 4px; font-weight: bold; `;
const PrimaryButton = styled(button)` background: ${primaryColor}; color: white; `;
```
From what I can see, Tailwind doesn't offer a solution to the div-soup HTML at times becomes. When I look at five or more nested divs I kinda forget what I'm even looking at. Having semantic names is crucial in maintaining a layer of abstraction that helps to map the HTML inside your brain as entities with specific purposes. I myself use JSX and name my elements as Container, ButtonGroup or TooltipWrapper for example.
The other problem I see are the conditional class names - how does Tailwind deal with them? Do you write if-else class names? Doesn't seem very clean. I myself use styled components where I add properties, such as visible={isVisible}, and resolve them inside the stylesheet where the value matters.
The benefits of Tailwind, however, seem also evident to me as abstracting away CSS into much smaller number of utility CSS classes does appear to be a very quick way to make decent layouts. It's just that having done plenty of CSS/SCSS I don't find it too time-consuming to write the CSS myself. And also because of the previous points.
My issue with Tailwinds from a design standpoint is that it allows for too many combinations, which is the issue with plain CSS. Having less choices makes things more consistent and IMO easier to read.
For example, a font style in our project includes the family, weight, line-height, alignment, and size in one class. This is called something like `Font16RR`. Responsive sizing is handled in the class itself (so, it may be 20pt on large screens, even though it is called "16").
We also have utilities for a small set of spacers, margins, colors, and layouts.
These are defined in parallel with the UI designer, so we both call them the same thing and we agree it's a "big deal" to add a new one. This has reduced the number of styles in a large legacy app dramatically, making everything look much more consistent.
This has the added benefit of being very easy to provide different themes which was a requirement of the project. I admit that this system requires other front end developers to either 1) Internalize the design system or 2) Receive specific mockups and copy them verbatim.
Would love to hear the perspective of anyone who has worked on whitelabel software and had to manage multiple themes and/or worked with very strict design systems with Tailwinds.
OP wrote:
> Because every new class name that you introduce could have potentially hundreds of property-value combinations, and that translates to more compiled CSS—which, of course, means a larger network request, and potentially slower performance.
After PurgeCSS you can end up with a 15kb CSS bundled payload even before gzip for a decently sized site. That's so much smaller than most other CSS libraries because Tailwind is very purge friendly.
In real life, tailwind produces the smallest dist files of anything I've used just because the built in treeshaking thing is just effortless and works so well.
(Clearly this is my least favorite "I don't like tailwind" gripe lol)
My site https://www.listenaddict.com/ is 5.6kg for all the core/layout, and then another 1.6kg for the color theme (I have 12x themes and they're all separate, so the user only has to load one of them).
If anything, Tailwind is the opposite of bloat.
Original HTML could do it in table cells. What's taking CSS so long.
Anyway it's easy and has been for ages:
display: table-cell; vertical-align: middle;CSS accumulates techniques, each one intended to compensate for defects with the other -- and they interact in obscure ways. Googling will always turn up recommendations for each. You end up with Perl-like "There Is More Than One Way To Do It" -- and you have to know all of them if you want to actually do anything.
Not a fan of tailwind, but whoever thinks that is simple misunderstood the meaning of the word, it's not about the number of characters you have to type.
<div class="container">This is the text</div>
.container {
align-items: center
display: flex;
justify-content:
height: 200px;
}Two of those lines exist to handle each axis separately, the first is to define the layout mode you are using. It doesn't get much simpler while still maintaining a fine level of control over each aspect of the layout. I don't buy that it has to be "better" than this.
We are owed a better API, let’s get the right people on this.
div { display: grid; place-items: center; }
I see tailwind as a damn cheat (a good thing), a hyper productivity tool throwing away all the what we learned as 'correct', and just get things done with escape hatches to fallback.
- Rapid prototyping with tailwiwnd
- Making component frameworks like bootstap? Just use tailwind sass with helpers. You can use a sass approach with including the helpers so you dont polute your DOM.
- it is easy to remember and guess which increases velocity. p-1/2 etc.. m-1 p-2 etc etc. Conventions make sense!
- Bloat can be solved with purge
- Refactoring easy easy if you abstract it away in sass so you can make global style changes.
- Protyping is easy, and you can then move it towards a component framework once you've done wiggling around and changing your stuff.
- I find css in components hard and painful personally also makes doing broad style refactors a bit more difficult.
- I also use css for email and other simple landing pages that dont require html components or react/webcomponents or whatever.
I find this a great addition to my life. Its a darn sharp knife, up to you what you do with it.
As for 'it makes your code harder to read', I simply write the classes in HTML, then when done, move them to my CSS file via @apply, and my HTML looks the same as it has always done.
Even after optimizing Tailwind. You still end up with way more css than you should have. Add all the classes in the HTML and you're sending users a massive amount of data.
Instead of writing:
main > * + * {
margin-top:1rem
}
you write `mt-100` a thousand times.Repeating yourself isn't the end of the world, and on occasion DRY is worse (look up incidental duplication).
I do think css-in-js solutions are good, but also they're a lot more work, and probably better suited for large teams with a shared design system, etc, etc.
Personally, I think Tailwind is great. But I understand some people don't like it and cannot appreciate it because it might not look good seeing many classes and "but this is just inline styles". Ok, not for you. It is still great for some other people.
[1] https://github.com/vercel/styled-jsx/issues/688
[2] https://github.com/vercel/virtual-event-starter-kit/tree/mai...
Using Tailwind purely at the HTML level is not the only way to use it, and in fact, this is only supposed to be used at the prototyping stage. it's a pretty standard part of the Tailwind workflow to extract class combinations into helpers that can be reused. If utilization doesn't even get to this depth of use of the tool, can one really consider the usage the tool to be anything more than an abuse?
Frankly, Tailwind was a gamechanger for the way I do vanilla CSS. It makes it really fast for me to prototype, straightforward for me to refactor, and I end up with CSS and HTML that is straightforward and easy to maintain. It is what I always wanted Bootstrap to be, which is the Rails of CSS. Now, there are certainly valid critiques to be made of Tailwind, just as there are valid critiques to be made of Rails. But in my opinion, many of those critiques are red herrings that boil down to abuses of a very useful tool. I always have to ask myself in these situations if the problem is the tool itself (if used properly) or an abuse of the tool.
True, before I learned how to use Tailwind and grokked /why/ people used it, I was suspicious and leaned towards the former. But that's exactly why I wanted to force myself to use it and figure out what everyone else was excited about, even if I didn't feel the same.
Of course, once I learned it a little bit, I felt the same way I did about Rails, which is that it's an extremely useful tool for a lot of jobs. It did not get popular by accident, or because "everyone is an idiot" -- it got popular because it solved a very real problem significantly better than its competition.
I would hope the author eventually opens their mind a little bit to try and figure out why that is the case. They just might experience the same a-ha moment that I did.
I use Tailwind to style templates that are used for static pages. I don't have JSX components, I don't have javascript at all other than for Tailwind's building...
Tailwind is great. I have quickly been able to create a template that reflows based upon the size of the browser, in a manner I haven't been able to do so before, and all in the same place where I am already defining the HTML. If I were to do it with media queries and have to redefine `.thing` in two different places if I made a change to one I would now have to hunt it across the file to make the change in the other four locations for all of the breakpoints I want, that becomes tiresome quickly.
To each their own: for instance, I used Sass but disliked it. It always seemed to add needless complexity to my projects.
But I think this could be solved with an editor extension that shortens or hides the 'class' unless you mouseover it
Then import tailwind after yours, that way any simple flattened class can be overridden by tailwind utils.
So now you can have component say .ns-MessageBox feature a default text color, or font size or anything, (optinally using @apply in the scss stylesheet) and you can override any good defaults, as well as handle layout & spacing in the template eg.
<div class="ns-MessageBox text-red mb-4">…</div>
Likewise then you can also set a good default stylesheet for plain html headings, lists, etc. Because selector "h2" can be overridden by utility. So your headings lists etc can also have sensible default bottom margins padding and whatnot. You have to look at the design templates to determine good defaults, then you dont need to be so verbose with tw in templates.
Then you can also keep your entire color system in Sass if you prefer, take advantage of Sass functions.
It’s very flexible.
A good ruleof thumb is to use tailwind in templates for all the space around components, main layout and spacing of all individual components. Those are the things that tend to bring really confusing and complicated css selectors with pseudo selectors to target first/last elements, which is hard to maintain, hard to read for other devs, much better handled in templates.
With Tailwind, all of your abstractions are explicit; the styles are right there on your component, with a more or less 1:1 correspondence to CSS properties. Everything does what it says it does.
In traditional CSS methodologies, the abstractions are implicit but hidden. The relationships between the stylesheet and markup are still there, but they cannot be understood without knowing both structures intimately.
Which of these is cleaner, and for what purpose? At the end of the day, code is not there to be looked at, it's meant to be deployed and maintained.
The trade-off between having messy html for faster development or css-in-js is not worth it for me. I still don't like mixing html (jsx) with javascript. I'm an old-school seperation of code vs concerns.
This is why the key is not having a seperate CSS codebase. The separation of CSS and JS is just a legacy of web development from the time before component based application development. At most you might need a common source of basic variables like theme colors. But essentially everything else should be scoped within the context of an individual component, and live directly within that component. Huge stylesheets with specific selectors and complicated BEM style class names just aren't necessary anymore.
Your problem here is thinking of JSX as HTML instead of what it really is – a thin syntactical layer on top of Javascript.
What is weird, is the author, without irony, uses his vendor lockin to react to argue for better ways to do css? ok.... 1, his examples are super simplisitc and 2 that would actually look really simple in tailwind rather than string injected variables. Which if you have to keep using everywhere is going to make your css super bloated.
1. Tailwind Makes Your Code Difficult to Read
Not sure why the styled-jsx examples at the end are easier to read. Tailwind is semantically obscure like any language you don't know is semantically obscure. Also with the intellisese VSCode gives you, learning is super easy.
2. Tailwind Is Vendor Lock-in
Vendor lock-in is a really strong word for what is going on here. Would you say React is vendor lock in because switching to another lib is hard? I worked at a company where we switched all of our CSS modules to Tailwind, and it actually cleaned up the code quite a lot.
3. Tailwind Is Bloated
It's actually the opposite. Large companies use atomic css approaches because it keeps your CSS bundle small. Especially with purgecss, you can get bundle sizes super small. I even saw a company switch from CSS modules to Tailwind to reduce they CSS size for AMP.
4. Tailwind Is an Unnecessary Abstraction
@apply is the equivalent of composing functions together in any other programming language. Also @apply lets you keep using the scales and color palette you set in tailwind.
5. Semantics Is Important. Tailwind Forgoes It.
Naming things is hard. It's a problem in regular programming, and CSS is no different. Needing to name less things makes life easier, especially when you component system name is already descriptive.
6. Tailwind and Dev Tools Don’t Play Nicely
Usually I just click .cls in dev tools and check or uncheck styles as needed. Or if I start typing a style dev tools intellisense helps me.
7. Tailwind Is Still Missing Some Key Features
pseudo elements are being discussed. As for it not being possible because of bloat, see above (PurgeCSS)
Granted the value of composition is most appreciated when you do the frontend of component based apps.
You can use sass, big picture architecture of the styles across app, and also benefit from a shared language with the rest of the team and write less css for mundane things that composition solves very easily (eg. a heading with less margin than default, a messagebox with different font and whatnot, dont create hundreds of classes or modifiers when a few utilities can do this).
Sadly most devs dont understand css precedence enough to figure out how to easily combine custom css and tailwind approach. (use bem style convention, keep specificity as low as possible, ideally 1 )
With Angular, it's generally quite simple to figure out where the CSS for a component lives (it's literally right there in its own file alongside the component).
Sharing common values (e.g. colors, borders, paddings, etc) across classes was the biggest issue we faced, so I can see Tailwind being useful there.
In our project, we've somewhat solved it through a mix of sensible base styles and classes ('btn'), custom classes (e.g. `clr-primary--500`), CSS variables (e.g. `--clr-primary-500`) and SASS mixins / functions. It also makes it really to switch out themes dynamically, switching b/w dark and light modes for example, just requires switching up the CSS variables.
I love Svelte, it's my main framework these days, but after many years in React/Vue/etc of trying in-component styles, I've gone back to good old SCSS files.
It's a bit tedious to manage Component.svelte and Component.scss but there's such an elegance to writing pure SCSS. Not only your component files become much simpler, just markup and logic, but you can use SCSS as it was intended. With vars, mixins, third party utilities like Bourbon, etc. The separation of concerns feels awesome. Like going back to the ZenGarden of yore.
For me (and many others) it's been a game-changer, but I didn't truly appreciate it until I actually tried it.
There's a lot of help available, and I highly recommend Adam Wathan's Youtube channel (https://www.youtube.com/channel/UCy1H38XrN7hi7wHSClfXPqQ/vid...) where he has several livestreams of actually building pages using Tailwind. Watching a couple of these went a long way to convincing me that Tailwind was worth trying.
(Adam is the creator of Tailwind).
Here is what I have to share:
1. Tailwind Makes Your Code Difficult to Read
Tailwind CSS, in my opinion, is the easiest to read and understand what is actually happening. Yes I agree there is a learning curve to the shorthand notations but the naming is not bad at all as the author says. A couple days of light use and the system was in my head. Saying this as a full-stack engineer who deals with React + Python and devops. So I do not ONLY look at CSS shorthand notations all day.
The example with the really long string of Tailwind CSS classes do not occur with me since such a complex string of classes is probably going into a component that needs customization from React component parameters. So I build the Tailwind CSS class names line by line with if-blocks or similar.
2. Tailwind Is Vendor Lock-in
"Once you build an app with Tailwind, moving it to any other CSS framework or library in the future is going to be grueling." - the whole point of Tailwind CSS is not to move to a framework. Your JS components mixed with the CSS are exactly your own branded framework. I am not sure I understand the move argument.
Even though all the products I work with are small, we have new frontend engineers helping out and it took them less that an hour to find the right Tailwind CSS class and making pixel-perfect components. This is close to CSS yet comfortable to see and existing project and contribute.
3. Tailwind Is Bloated
Purged Tailwind CSS based stylesheets are ridiculously small. Try it out for yourself. (1)
4. Tailwind Is an Unnecessary Abstraction
Again, like I mentioned, I was even nervous that new people will not like the abstraction that Tailwind CSS has. But once you see an existing, well setup project, its so easy to check documentation and contribute. The abstraction is super thin on top of CSS.
"If it’s not already obvious, @apply completely violates Tailwind’s founding and guiding principles." - no it does not. Tailwind CSS exists to create your own style framework. It is that simple. Once you see patterns, you use @apply to codify them and reuse. That is so convenient. It is a feature in my opinion and a really important one as your project grows.
I want to stop here, but I feel that these 4 points are not strong counter points to someone using Tailwind CSS for a year over a few projects now.
So I do something like <div class="button text-center">.
I am not sure if I could actually include Tailwind and just use their utility classes for my needs, in combination with the normal CSS usage, or that would make things confusing.
> In my personal experience, poor naming conventions (or just poor variable names in general) are the source of a lot of confusion when other people read your code. I would rather look at some CSS that has padding: 0.25rem or margin: 0.5rem instead of trying to mentally map Tailwind’s p-1 or m-2 to their CSS equivalents.
Short names for commonly used concepts helps readability (quick to read, takes up less space) and makes it less tiring to type + change. It super tiresome to write long winded CSS (media queries are especially bad for this) and then find out you went in the wrong direction and have to rewrite it.
> Look, I don’t like Tailwind for lots of reasons. But none of them are nearly as concerning to me as vendor lock-in. It’s why I don’t want to invest time in migrating existing projects to Tailwind, and why I’d be reluctant to use it for any new projects.
Is this a real concern? I've migrated sites from custom CSS -> Bootstrap, Bootstrap -> Foundation, Bootstrap -> custom CSS etc. and it's always painful not matter what. At least with utility classes, you don't get problems where you're scared to delete CSS because you're never really sure where it's being shared. You generally just want to rip out all the previous stuff too as combining two frameworks isn't practical.
> For starters, if you’re not sure what to call that <div> in your markup, consider whether it’s actually needed. One of the great things about the Semantic CSS paradigm is that it forces you to structure your markup logically and meaningfully.
I think what makes utility classes nice for custom designs is there really aren't good semantic names for a lot of things that are only there for presentation reasons. Like I need to put a <div> around the heading + intro text so I can add some padding between those and the screenshot under it...so call it `.intro-wrapper`? Now there's a CSS quirk that requires me to wrap that all in the <div> so call it `.intro-container`? I want to make the three lines of the header animate in one by one so call them `.header-line1`, `.header-line2`, `.header-line3`?
It's tiresome coming up with name for stuff like this you only use once and even more so when you're experimenting and might not even need them. It's just noise that requires you to keep jumping between your CSS and HTML definitions to get anything done. Use semantic HTML tags like <header> <h1> etc. and custom class names for things you're sure you're going to reuse though.
Either way, utility classes is yet another "it depends" choice where there's no single answer. Use it where it makes sense.
How on earth can someone end up so cognitively dissociated?
However: The author writes a good critique here, and then destroys all of it by favoring the eternal sin of CSS-in-JS instead. If tailwind has its flaws, CSS in JS is the worst approach thinkable, something no web developer would ever do. I'm aware that it's used in the JS framework niche, but that's not web development, that's app UI development in a browser and seems to come mostly from web-foreign developers. If it's that or HTML with tailwind, then tailwind is always better.
What I see as main "advantage" is that it stops developers from trying to be smart. With Tailwind first idea when creating a new component is using some combination of existing utility classes and it will be probably more or less ok and consistent with rest. Without Tailwind, developer will probably start create new classes, possibly duplicating or reinventing lot of existing stuff.
Old-school devs seem to like TW for the benefits it gives over other CSS paradigms (Smacss, BEM etc.) It speeds up development over those. Testing and editing in the inspector is simple and easy to put back into the source.
New-style devs (SPA ALL TEH THINGS!!) do NOT like TW. They are already dealing with 3 concerns in the same file. Adding another layer of abstraction is unwelcome. Changes seem to multiply and search/replace makes seemingly simple adjustments a royal pain.
Then there are the Bootstrap acolytes who won't give up their btn-default no matter what comes along.
All I can say is to each their own. But Bootstrap guys(and girls)...it's time to move on. :P
That's how Tailwind won me over.
Sooner or later most design-devs (devs doing design as well) end up using some kind of utility classes. Using Tailwind from the start saves some serious amount of effort and provides a standard across projects and teams.
I often use bootstrap-vue (and used vuetify on a recent project). The accessibility, documentation, testing, and consistency these projects bring to my vue projects is big. BV uses bootstrap. I can make modifications as/when needed, but I have a whole library of these components that are documented and are styled using bootstrap.
Having to rebuild these sorts of things just so I can use tailwind is... hard to justify. I was able to semi-combine both in a recent project, but it seems a little fragile.
Is everyone else just building all their components by hand all the time?
Here are my responses to OP's arguments:
1. Tailwind Makes Your Code Difficult to Read
true, there is a learning curve. But this is over relatively quickly once you learn that "w" stands for "width" and "h" is for "height", and the screen size prefixes map as you would expect from default to medium to large.
as for scanning left to right... the enter key is right there. my VSCode autoformats the classes to break lines just fine. its probably the tailwind plugin doing that.
2. Tailwind Is Vendor Lock-in
first of all, "vendor lock-in is bad" is a poor kneejerk reaction. we all buy in to vendors at some point. eg with frontend frameworks. and as far as vendor lockin goes, Tailwind is extremely light - if you want to stop using it, freeze the generated output, and then continue writing css in whatever new way you choose. this is far LESS vendor lockin than alternatives like styled components and css modules, which force you to make specific js and build tool choices. and all of these are open source so this again is an order of magnitude less lockin than anything proprietary, which is where this argument really starts being at all useful.
3. Tailwind Is Bloated
how is declaring things on the docs indicative that "its creators have tried their very best to hide this fact from you."? they've just decided on sensible defaults. the bloat only impacts development and builds. This contrasts to the problem that utility css solves, which is bloat inside of hard-to-maintain append-only CSS with lots of duplicates, which actually affects users.
4. Tailwind Is an Unnecessary Abstraction
components. use components to repeat stuff. this rant on @apply takes things way overreacts, calm down.
5. Semantics Is Important. Tailwind Forgoes It.
counterpoint: naming things is hard. Tailwind names css utilities for you so that you don't have to come up with custom names for every component.
6. Tailwind and Dev Tools Don’t Play Nicely
au contraire, tailwind plays VERY well with devtools. https://twitter.com/swyx/status/1265745431448838144?lang=bg (since this video I even learned that you can type in the classname box in chrome devtools and it autocompletes nicely for you
7. Tailwind Is Still Missing Some Key Features
then write regular css :)
My only pain point with Tailwind was the order of classes. It helps with readability if the classes are always in the same order. I just discovered that eslint-plugin-tailwind [0] is solving this exactly. Configuring VSCode to run code formatter and perform ESLint fixes [1] is just an incredible DX.
[0] https://github.com/Idered/eslint-plugin-tailwind
[1] settings.json: "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true }
I am just trying to imagine working in a team 1 dev + 1 designer...
I was always a bit wary of CSS frameworks, and I preferred writing "actual" CSS and used mostly CSS/SCSS before I discovered Tailwind CSS. Interestingly, even before I discovered Tailwind CSS, I was already getting into the habit of using utility classes like "flex", "flex-wrap", because it made a lot more sense *to me* to see those in the HTML.
I personally find the examples given in the blog post contrived. Take the "hard to read part", for example, this is how I normally format my HTML using Tailwind CSS:
<div class="
w-16 h-16
md:w-32 md:h-32
lg:w-48 lg:h-48
"></div>
Even if you are using styled-jsx or any framework that gives you the ability to write "regular" CSS, it still wouldn't beat the example above in terms of readability. As such, the following paragraph just doesn't make sense:> I’m comfortable reading other people’s code, and making sense of their logic and relating their markup to their CSS.
Assuming that we are not talking about component-scoped styling here (otherwise the point made would be moot), with Tailwind CSS you can instantly relate CSS to markup without switching between files and hunting down all relevant class names in the example given above; this is arguably also easier when you are not working with the source and just debugging on the front end.
> But if I have to look at ~1000 LOC changes in a PR, and most of that is coming from long strings of class names, I’m not going to be happy. It’s also going to be more difficult for me to make suggestions that could eliminate redundant CSS or simplify your markup.
I have never dealt with ~1000 line of changes in a single PR, but that sounds pretty broken and there is an abstraction problem that is much worse happening elsewhere and irrelevant to the argument.
The last line also sounds like a throwaway without any solid example to prove it: even if switching between markup and CSS files is not taken into account, I can't imagine hunting down redundant CSS would take longer with Tailwind CSS. If the redundant classes are on the same element you can see it instantly; if they are on different elements, it would still take similar amount of work to debug and find them? Assuming that there are no redundant classes, then there isn't much to be simplified because the whole point of Tailwind is to have (managed) complexity in the markup instead of having it in the CSS. I personally would rather have it in the markup, but that's just me and I can't say if it's better or worse.
> With Tailwind, you’re forced to interpret semantics on the fly.
I find it easy to just ignore class names and focus on content when I need to with the way I structure my code. Maybe a lot of people don't realise that it's okay to, or feel comfortable with, splitting class names and attributes onto multiple lines.
Just one more point on being bloated, note that on Tailwind CSS's website they say:
> Tailwind automatically removes all unused CSS when building for production, which means your final CSS bundle is the smallest it could possibly be. In fact, most Tailwind projects ship less than 10KB of CSS to the client.
For reference, the blog's CSS is 12.12 KB gzipped.
Most of the rest of the post follows a similar pattern that's just out to trash Tailwind CSS. As others have already pointed out, this may end up being a 50/50 thread of personal opinions after all, but it seems that we keep getting more and more of these sensationalised hate posts that are just based on personal preferences...
---
> 1. Tailwind Makes Your Code Difficult to Read
This one is interesting to me. I think devs should take responsibility for writing easy to read code. A framework is a framework. I agree shotgunning classes all over markup is messy, but Tailwind doesn't do that - you do.
I would encourage the author to understand the concepts of extracting[0] and defining components[1] within Tailwind. That can make things very easy to read by the author's logic.
> 2. Tailwind Is Vendor Lock-in
Not necessarily. Especially if components are defined, if anything it makes it easier to change. Because you have a css class abstracted at the same level of your markup. In the world of various competing component frameworks, I would argue the opposite is true. You can just as easily apply tailwind component classes to Vue as to React as to Svelte etc.
> 3. Tailwind Is Bloated
As others have mentioned purge css[2]
> 4. Tailwind Is an Unnecessary Abstraction
"Because instead of repeating styles in your CSS, you’re now repeating them in your HTML", "you’re still writing the exact same amount of CSS, disguised as class names" This just isn't true if you use it correctly again see extracting and defining components.
> 5. Semantics Is Important. Tailwind Forgoes It.
Author recommends BEM and then in the end says "You don’t really need BEM, either". I agree with component scoped css, BEM is less important. Tailwind
> 6. Tailwind and Dev Tools Don’t Play Nicely
What? Anyone who experiences this should revisit CSS order of precedence. It behaves exactly as expected. I've never had a problem with chrome/firefox dev tools.
> 7. Tailwind Is Still Missing Some Key Features
So use css? Using tailwind isn't a binary option. You can use both tailwind and css. It's a tool, use it where it makes sense.
---
[0]: https://tailwindcss.com/docs/extracting-components
[1]: https://tailwindcss.com/docs/plugins#adding-components
[2]: https://tailwindcss.com/docs/optimizing-for-production
[3]: https://jigsaw.w3.org/css-validator/validator?uri=https%3A%2...
> So use css? Using tailwind isn't a binary option
then what, you now have 2 set of styles in 2 different places, written in 2 completely different ways, does that sound fun to maintain? I guess still you have to structure your css in some way too?
<div class=" w-16 h-16 md:w-32 md:h-32 lg:w-48 lg:h-48 "></div>
do you just casually throw these magic numbers everywhere or there is a way to use css variables with tailwind?
When functional css ideas were introduced it presented a novel solution for scoping issues, naming issues and bloat issues. I remember discovering basscss and tachyons around 2015. The paradigm immediately felt like a huge improvement in my workflow. The iteration was so much faster than what I was used to with Bootstrap, BEM, or SCSS- I felt like I could design in code for the first time. My favorite workflow for web design became loading basscss in a CodePen, with almost no idea of the outcome I wanted and iterating towards something. What’s kind of amazing is that it allowed for these sorts of quick sketches and improved maintainability in large projects.
It also led me to my first inkling of a design system- through the use of composable and constrained styling primitives. A lot of the ideas introduced with those libraries now saturate the frontend ecosystem. And keep in mind, functional CSS, or at least the principles behind it have evolved a lot since 2015, particularly when it comes to components and CSS-in-JS. The original creator of basscss has gone on to create a number of equally impressive projects that advance the main ideas: styled-system, rebass, basically everything from @jxnblk.
I haven’t used Tailwind or any other functional CSS philosophy over the past few years since I find primitive and composable styling components, CSS-in-JS, themes, and CSS variables offer even better workflows for building the applications I work on. When Tailwind initially made a splash I was already pretty into what I consider this next phase of styling libraries- so I was a bit confused why people were so excited about it when basscss and tachyons had been around for years. Looking through the docs it seemed like they had taken basscss and tachyons and layered a bit of Bootstrap on top (in the form of nicer visual defaults and some component patterns).
Outside of the core functional principles, I don’t like the idea of Tailwind as much as I do basscss/tachyons. I think Tailwind tries to put too much of your styling in atomic classes. Part of what makes basscss great is the small API and the constraints it introduces on reusable patterns. I don’t want all of CSS to be represented as atomic classes- just the stuff I reuse 90% of the time. It also seems like a bit too much tooling when one of the main advantages I saw in the older libraries were that they were just plain CSS- you could use them on pretty much any project, regardless of build tools or architecture. I used to start with basscss on a project and then add my own layers of patterns on top- extending the system as needed.
That said it still offers a lot of the good stuff, combined with great documentation and more thought put into component patterns. It’s a great starting point, especially if you don’t want to or can’t take on runtime costs for CSS-in-JS (this is also evolving but still a concern). Its success isn’t a fluke or a result of misdirection as the author of this critique alleges. Reading through Adam Wathan’s original post on functional css (https://adamwathan.me/css-utility-classes-and-separation-of-... he describes the exact same trajectory in CSS thinking I went through as a frontend developer through the 2010s. It’s a great read and I think speaks to the level of consideration put into the library. If I were to try a new project today using functional CSS I’d definitely give Tailwind a shot.
> Tailwind Makes Your Code Difficult to Read
I think this is a preference, not a fact. I tend to write 2-dimensional CSS (I know the standard convention is 1D) so seeing a bunch of classes one after the other on the same line doesn't bother me.
Regarding the abbreviations being difficult to parse, I don't find it so. And, do you want short class definitions or do you want long and descriptive class names?
> Tailwind Is Vendor Lock-in
I'm not convinced. Every framework is lock-in. Otherwise there'd be no point.
> Tailwind Is Bloated
Possibly, but it seems to trim itself down when compiled to production quite nicely. Paired with minification/gzip, doesn't seem like such a big deal.
> Tailwind Is an Unnecessary Abstraction
I like the promises it gives. "Here are some predetermined boxes your design can fit into, if you need to change them, do it via the config." In other words, you're constraining yourself. I think this is important. Even on teams with designers, the design can evolve over time and you'll have one section of an app looking different from another. Tailwind enforces a set of good practices. Maybe I like this because I'm not a great designer at all, but it seems to me this kind of constraint could have helped many projects I've worked on in the past (ones that had professional designers).
Secondly, regarding "What exactly is the difference between using @apply and just using the CSS rules that correspond to your utility classes?" Well, because your utility classes are still subject to the constrains I mention above. The documentation specifically only recommends doing this for places where something is too small to be its own component, but too common to repeat the same class names over and over. Sure, you can misuse it, but I found the documentation obvious how the best practices here.
"Because instead of repeating styles in your CSS, you’re now repeating them in your HTML, through class names." I think Tailwind specifically makes sense in the context of components. If you're repeating the same classes over and over again, you're doing something wrong.
> Semantics Is Important. Tailwind Forgoes It.
I can see both sides to this. The one comment I'll make is that in the cases where I've vehemently stuck to semantics, it has been wonderful to build but horrible to change. Semantics makes a tone of sense, until things start shifting around, and then you've got half of your classes referencing some object that's no longer there, or a thing that used to live here, but now lives there, and only some of the styles moved and some were copied etc etc.
When I first saw Tailwind, I thought "wow, this is just inline styles++, how stupid." Now I kind of see the magic behind it.
> Tailwind and Dev Tools Don’t Play Nicely
This might be true, I haven't really done much devtools CSS debugging with TW yet.
> Tailwind Is Still Missing Some Key Features
It also defines "dark" but I haven't seen anything about custom themes in general. What if I have more than just a normal mode and dark mode? I'm curious how one would build themes on top of TW.
> With frameworks like React and, more recently, Svelte, it’s never been easier to write CSS without leaving your markup, and without resorting to inline styles. You don’t really need BEM, either.
This is true, and I'm excited to try and build something more complex in Svelte and see how it goes.
For small projects, you’re wasting time with a massive abstraction layer when you could be writing a small set of easy styles in plain CSS. You don’t need the weight and complexity of a sizeable framework.
And for big projects, it’s technical lock-in and additional maintenance overhead that absolutely doesn’t work if you’re trying to coordinate a microfrontend environment for example.
Or define components? https://tailwindcss.com/docs/plugins#adding-components
For small projects you don't have to create any boilerplate styles, resets, and the like...you just start writing code. A lot of frontend frameworks that are popular these days have CLI options to set up Tailwind out of the box at the same time you bootstrap the rest of the files.
For large projects, maintenance is easier because you don't have to relearn the CSS architecture each time you (or more importantly, someone else) comes back to work on something after a few months. If you're doing microfrontends, just import the same config file?
Where Tailwind is different is the development process. One problem with CSS is that before you can do anything, you have to come up with a class, ie some abstraction. You then start using that class all over your code until over time you realize it is the wrong abstraction.
What Tailwind lets you do is get going without having to create any abstractions. Then as time goes on you see the repeated patterns in your code and can use @apply to refactor those into the correct abstractions.
So you end up in the same place as traditional CSS, but the trip is much smoother.
The author's complaints about pseduoclasses and break point are spot on though, and he only lightly touches on pseudo elements which are another huge black eye for Tailwind.
You probably weren't alive yet, but the CSS methodologies that originated from the CSS Zen Garden made sense in an era when XML was supposed to take over the world. But it didn't, and there's no sense hanging on to those promises.
ps. Media queries were not a thing twenty years ago.