_All_ abstractions are leaky, but some are helpful. For me, as an unwilling and inept CSS dev working on small low-budget projects, Tailwind is helpful because it lets me put all styling information in one place.
Tailwind is not an abstraction, it's shorthand. It saves you several bytes in the source, saves seconds of typing, and in return rewards you with a massive headache.
I'm a tailwind fiend, ie I like tailwind, but I can say it won't save bytes in the source if you use it the way that the docs intend. Tailwind is not very DRY so you'll probably end up repeating classes in source resulting in extra bytes if you just used a custom class repeated on similar components.
But you can create custom classes using @apply. If you're doing that alongside using it in components then it's incredibly DRY and far more efficient than anything else I've ever used in the last 20 years.
Or Bootstrap, etc. This is why we didn't end up using TW for a large production app. It's really cool, but realized we were just ending up re-creating something like Bootstrap components using TW @apply.
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.
Absolutely, I love @apply for this reason. But just look at tailwind's website, they don't exactly present this front and center, so I read their site as though they don't exactly prescribe the usage of @apply like you or I would.
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.
Pretty sure tailwind has had the option to have the minimal CSS generated on-demand from the set of classes you actually use, for a year or so.
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
Someone can fact check me, I'm going off memory: I believe in v1 Tailwind (before they added the "JIT" compilation) when you were in dev mode it just had all the classes available at all times, and then added a step to the prod build where it would purge unused classes from the CSS.
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
Yes it does. You tell the compiler where your html lives and it keeps only the classes that you've actually used. This can be enhanced with a safelist of classes that might be generated by code. So you give it a regex to keep, for instance "all grid classes".
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.
Tailwind is amazing when paired with React. It completely avoids the need for a parallel, poorly coupled, poorly scoped CSS file (or whatever flavor of CSS you're using).
It's amazing to me how many people who try Tailwind don't realize you can just... Write CSS when you need to, while using Tailwind?
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.
There's a meaningful difference to my workflow of doing
<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.
Regarding the choice of class names: you are using using wrapper elements routinely, outside absolute edge cases like border gradients, you don’t have current CSS skills. As mentioned previously current CSS means you can have a 1:1 match of HTML element to UI element for the vast majority of layouts, no wrappers required.
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.
Ideally all components would be small bite-size pieces, where scrolling down wouldn't be a big deal. But in practice I could still see it being tedious, scrolling through a bunch of classes to find the one you care about.
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.
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.
Ah but then you would have to remember and repeat those exact pixel values every time you pad something and update them in every place if you decide to change it. Or build your own system of variables in CSS, or use some awful CSS in JS solution.
Also means you have to think of a name for every component you want to apply style to which is surprisingly tedious.
> Don't forget the time to look up whether it's vertical | horizontal or horizontal | vertical because I forget all the time
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.
The author mentioned, that Tailwind doesn't support the “perspective” style attribute - if it's correct (I have no expertise in this), then Tailwind is not just CSS.
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
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.
This. Tailwind and react work very well together. Components then become portable to other tailwind projects. The best part is tailwind then only ships the CSS you actually use to the browser. Much cleaner than older CSS approaches like bootstrap. Has made me much more productive overall.
After trying out Tailwind in one pet project I have to say that for larger teams it should not be used. Looking at the code now I get the feeling that maintenance would be a big problem.
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?
I think the key with Tailwind is that you need to aggressively componentize for it to be useful. If your pages are 90% made up of small components than it can be very reasonable and pleasant to work with. If you have a lot of pages that define their own HTML it can turn into a mess very quickly.
I dont see a problem with big teams at all. You opne up the components HTML and see the styling right there, no doubts in your mind where to edit the styling (no stylesheets to find etc.).
Of course, this requires the team to have disciplin and make proper components so their not copy pasting tailwind classes 100 times
I have only used Tailwind on personal projects, but I can't imagine it'd be harder than "regular" CSS, which is generally somewhat append-only anyway (you need a lot of team discipline to tame that somewhat).
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.
This succinctly nails what I love about Tailwind. It gets rid of the cognitive load of reviewing a css change and having to think through how the change will ripple throughout the site.
I've had some experience migrating large UI projects at work to use a design system based on Tailwind. Haven't faced any issue with Tailwind at all, even while integrating in existing projects.
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.
The symmetric bit is a good idea. I recently joined a team that maintains a multilingual LTR/RTL site so I'm learning some of these lessons the hard way.
Padding > Margin can also be a good idea, even if working vertically. Padding cannot collapse which is usually what you want. Obv, ymmv.
At work, we've built a component library styling readily available headless components from Radix UI, Headless UI, Reach UI etc. with a customized Tailwind CSS preset.
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.
”Never use margin if you can, use flex-box and grid with gaps instead. Placing a children node is parent's responsibility.”
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.
I hope what you describe will eventually be true for me, but so far the purported benefit of not jumping around isn't being realized. After 5 weeks of using Tailwind every day, I still have to switch between my file and the Tailwind docs fairly often to look up whatever shortcut classes I need.
So far I just miss CSS modules. Having my .tsx and .scss files both visible in split screen wasn't so bad.
You’ll start realizing the benefit more when you use it on multiple projects. Tailwind will be consistent across your projects where you can’t always say the same for custom CSS.
But you still have to have a config file, right? And the Tailwind docs. And then keeping track of when you changed something in your config that means the docs don’t apply 100%.
I’ve touched my tailwind config file a handful of times in two years. It’s not a problem. Tailwind just gets out of my way and let’s me iterate quickly without worrying about global side effects that always happened in other projects. I love it.
But CSS-in-JS was a mistake. Unless you want to start coupling your build tools even harder to get nonces on every compile, your CSP will require `unsafe-inline` which is bad for security. The simpler solution was to do CSS in CSS and keep your tools simple and minimal.
I had very few issues with just creating one scss file for every component module, ie ProfileCard.tsx and ProfileCard.scss, with sub components like ProfileCardimage being a nested selector in the scss tile. I don’t think you really need complicated design systems or atomic utilities anymore, just a bunch of variables for spacing, fonts, colors, etc. For any given component I knew exactly where to look for its styles, and it is all there. If the scss files becomes unwieldy as requirements change, it’s easy to just re-write it.
You could get the same benefits by using the `style` property though. The good part of tailwind is the part where it's all compiled down so it's not unnecessarily verbose in the HTML served to the client.
You either use a generic CSS or a generic HTML.
Everybody who picks Tailwind decides to use a generic CSS and a specific HTML.
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.
Styling HTML with a few classes and css selectors has drawbacks: you have tightly coupled code that is separated, and modifying one or the others implies that you recalls properly its counterpart. There are methodologies trying to address that, but it never seems to be enough helpful.
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.
> what's the benefit of Tailwind over style attributes on HTML tags
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));`
This. The secret super power in Tailwind is editing the tailwind.config.js and forcing standards. Then you have have something like only having four padding options per your style guide and them defined in one specific place. In style tags you could use a CSS var for this as well but you better hope you used it everywhere. Plus, that becomes super wordy: 'padding-left: var(--padding-1)' vs 'pl-1'
Oh I'm one of them..but incredibly clear shorthand is helpful for these kinds of cases. Writing code like `const a = 5; b = a/2; c = a/b` is pretty nonsensical. but in tailwind these things have meanings: p = padding, m = margin, then you have left, right top, bottom, (l r t b) and then X and Y. (so pl- mx- pb-) That is all of them. At the rate you use them it is pretty clear I guess.
It goes to say that it would be a computational nightmare to make computers understand styling, while HTML is very understandable for both humans and computers
Agree 100%. I wish there was a lightweight utility framework that is truly just shorthand, i.e. if I know the CSS property I can predict what the class name would be. With Tailwind it's all arbitrary
My plugin supports this, but only if I happen to know the shortcut. When I start typing...
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.
This is a good take here, even as someone who loves Tailwind, the intellisense should grep not just the class name but the contents. `leading` is an insane name for `line-height`
This feels like the wrong way to think about tailwind.
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.
I found that one can make Tailwind CSS utility classes appear vertically, if one's been using clsx library[1] (or something similar). clsx accepts an array, and at that point, prettier formatting kicks in.
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>
```
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.
The first example provided of building a custom animation is already outside of Tailwind's core use case of rapidly building common UI while only having to look at the markup. The occasional fine-tuning is usually when it's time to step over to your CSS file and write a custom component.
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.
> 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
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.
It's so leaky that I have to constantly think about the thing it's supposed to abstract away.
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.
Is making a CSS abstraction the primary goal of tailwind? With my limited understanding, isn’t the main point to use a compact DSL to stop separating style into a different file, therefor ending the scourge of ever-growing stylesheets?
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.
Many have suggested that Tailwind is some kind of replacement for knowing CSS, but I'm extremely skeptical of that idea.
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.
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?
That benchmark tests something slightly different with a focus on repeated CSS rules (and all from JS even): it's not surprising that's faster, though it's interesting it's faster even from JS (I acknowledged expecting that, and that CSS rules per HTML element are likely even faster, eg. a td rule with all the styles on it would probably win over that too).
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.
Write me a media query, or a hover state in inline styles.
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.
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
I don't think tailwind is built to solve the issue that the author is trying to fix. Tailwind provides a way to generate a set of consistent classes.
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 has enabled me (a hobby developer) to build a bunch of personal project and small business websites quickly and without having to know much about CSS.
I used Tailwind for a year at work, never having used it before. I found it overrated, frankly, and not much of an efficiency boost. We had a "don't use inline styling or your own CSS classes if a Tailwind class already exists" rule. While the reasoning was understandable, it meant that every time I wanted to style or position a component, I had to browse the Tailwind docs to make sure I wasn't duplicating something. And Tailwind docs... let's just say they leave a lot to be desired.
In your case, you'd be better of grepping tailwind's source. You could even spend some time writing a shell script that turns your proposed style rule into a regexp and runs it wherever tailwind's css file is.
We found that the tailwind VScode plugin was hyper helpful along with cutting down the unneeded in tailwind.config. Once you do that, most styling and positioning is variations on sizing, colors, and text sizes. Then the intellisense is pretty helpful to fill in the rest, some gotchas of course. `leading-` is an awful name for line spacing lol. A hardcore knowledge of CSS and all the governance you would use in vanilla is required to use tailwind however. It is important to see it from that angle.
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.
For inline CSS, I love Styled-System [1] for css as props. My components die and get redesigned too frequently to care for anything else. So I prefer them encapsulated in the Component itself.
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've been working on a fork of a project that uses styled recently, and I'm pretty sure it's responsible for slowing down VS Code to the point of being unusable, though I don't understand why.
> But that's kind of the point. Tailwind is a layer on top of CSS, but it doesn't actually hide any complexity in the layer below. You still need to know CSS.
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.
> 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.
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.
Really? Tailwind sucks but that's literally the only good part of 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.
> 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.
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 ?
Maybe I'm going out of a limb here, but wasn't the whole point of CSS to not have to touch the markup when changing the styling? Not trying to talk Tailwind down, I have just missed a lot of the evolution of web design in the past 15 or so years.
Yes but the way we do HTML has also changed–for example by using JSX and reusable components. The issue of having to change markup in a lot of places to change styling doesn't exist anymore if you abstract that away in reusable components.
And using tailwind without a reusable components system is cumbersome to say the least. What is suggested in the documentation is to use editor features (multi-cursor, research and replace, ...) https://tailwindcss.com/docs/reusing-styles#using-editor-and...
Everything on the web became a lot more dynamic. Stuff like web apps obviously but even relatively straight forward info pages end up with dynamic filtering and elements of app like functionality often.
Given that, the separation becomes more of a pointless burden than actually doing anything useful.
I've never understood the point of separating markup from the design. I've seen CSS Zen Garden and... neat? But I don't get the point in real-world use. I've never had to redesign a UI in a way where I'd only change CSS and not also be moving around markup in the process. And once you're changing both of those things, there's the overhead of cross-referencing and keeping names and hierarchy in sync. At that point I don't know why I'd want the 2 separate.
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 question of where to put the styles boils down to "do your content and style people work in completely separate teams?" For a newspaper, this is true. For someone who got their mate to set up their blog, this is true. Early days web was mostly like that. Not any more. If it's the same person doing it, put it in the same place. Separation will just be a hindrance.
it really only would make sense if we were serving pure xml and the webpage was the api for literally every webpage.
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.
That reminds me the pitch for the first time I discovered css zen garden: "you can redesign your website without changing the HTML!"
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.
Well you have that CSS zen garden thing nowadays. It's all the different media, screen sizes, user preferences. And instead of writing multiple classes that defines styles for different outputs you just have one and let CSS handle the differences. No need to duplicate classes, just semantic names.
That was back when XML was supposed to take over the world. We were going to style the same semantic content for print and web with CSS and/or XSLT. XHTML was the next big thing. Every web page was actually just a page.
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 in a way that same paragraph/criticism could have been written about Geocities in the early 2000's. Yet the simplified but imperfect onramp made a hundred thousand people more quickly understand their way into web development (including myself), than they would have without it.
I don't like it, too. Funny story: In my early years of development. My boss told me, that I should refactor the css of our application. We had a lot of styles directly in the HTML and I should create a css file with classes, to be able to reuse these classes. At that time I had no clue of css, this project should be a learning by doing. YES... Bad idea... So anyway: I did exactly that, what Tailwind does. I created a whole lot of utility-classes. I remember that our CTO yelled so bad at me, why I did such a braindead thing. I laughed so hard when I looked at tailwind, cause it's always reminding me at that time.
You have a shiny new tool called hammer and now everything seems to look like a nail to you. Tailwind is not made to completely ditch css. Its not a replacement, it just tries to make your life easier. At least in most cases in my experience.
388 comments
[ 115 ms ] story [ 664 ms ] threadTailwind'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
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.
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.
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.
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.
… 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.
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 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.
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));`
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.
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.
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
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.
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.
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.
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
vs Honestly the argument pro/con performance is irrelevant to me, writing this 100 times is immeasurably more ergonomic than writing this 100 times Or even worse and 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:
which would be the equivalent in tailwind of doing 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
I'm sure you could minify inline CSS similarly, but I suspect the tooling there isn't as polished.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.
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.
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).
[1] https://styled-system.com/
I'll take tailwind over Styled any day
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.
https://en.wikipedia.org/wiki/XSLT
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?