Tailwind CSS marketing and misinformation engine
nuejs.org
nuejs.org
Your, "don't make things cleaner"/"make clean things", images show part of the Tailwind classes used to make the black button on one side and the class "primary" on the other. Elsewhere in the article you define "primary" as only setting the text color to white.
I believe the specific example is a strawperson argument - you're asserting that semantic CSS (as you've defined in the article) is better than Tailwind's approach. However, you don't show an alternative.
---
On a personal note, I've never seen custom (S)CSS written in a maintainable way in my ~decade writing it professionally.
Encapsulating styles by colocating them with the markup (Button.jsx and button.css, styled-components, Tailwind, etc) is the most maintainable way.
I think Tailwind works really well because it eliminates collisions at the global level and makes it really easy to see what a piece of markup will look like without having to change contexts.
As a fan of Tailwind's approach, I like to hear the other side.
I have written a different article about Tailwind vs semantic CSS, which compares the amount of code needed to implement the same thing and how the resulting site is order of magnitude smaller with the semantic approach:
Either way, all these classes/css end up in a Button component for my site.
Have you ever used vanilla CSS? Please try to replicate that black button! You'd be surprised how much less it requires. No &>[data-slot=icon]]:-mx-0.5 or anything like that needed
https://nuejs.org/blog/tailwind-vs-semantic-css/
The bloat takes several forms
1. The amount of source HTML/CSS needed
2. The code complexity (class & DIV/SPAN "soup")
3. The weight of CSS & JS on the resulting website
Happy to write a comparison to Calypso, which is on a whole new level. Betting 10-20x bloat differences to semantic CSS.
I do think it would be great to make a follow-up post on Calypso, even if only on the button. Looking forward to it.
---
I read your other article and it still feels superficial and missing details. You compare a demo of a commercially available (effectively "closed source") template against a page you built, but don't provide the source for the page you built. Apologies if it's in the article, but I couldn't find it. I did find the simple blog template on the Nue site, but it had considerably less functionality than the Tailwind demo.
It's still not an apples to apples comparison, because you're comparing a site built with Nextjs to whatever you built. Of course static HTML without JavaScript is going to load faster than an SSG site.
I think the ending quote in the second article is completely unrelated to Tailwind and I'm not sure why it was your conclusion.
> The fact is that Tailwind's popularity will eventually fade. CSS-in-JS is trending now, but standards are forever.
Tailwind isn't CSS-in-JS. You don't need a build step to write it. You need a build step for important features like tree shaking and dynamic properties, but it's not CSS-in-JS.
Including that hurts your position because it sounds like you don't understand what you're arguing against.
Lastly, why so much hate against Tailwind? Positioning your framework as "Nue and semantic CSS vs Tailwind" doesn't make me want to try your framework. Show me why your framework is good and let me bring my own solutions to it.
It’s not without its flaws - you have to learn a new syntax (which is in most cases very intoitive, e.g. “align-items: center” becomes “items-center”, “margin: 0” becomes “m-0” etc.) and it has its gotchas (for example it generates the css at build time by scanning your code and extracting classes you use to prevent generating unused css, which makes it impossible to construct a classname dynamically e.g. `gap-${value}`) but I take these issues over having to write CSS in any other way.
One point where I 100% do agree with the author though - you should first learn CSS before learning Tailwind. Not only because Tailwind could go away some day, but because this will give you a deeper understanding how and why stuff works.
I never understood this argument about naming. You need to name the component anyway just to be able to reference it somewhere? You need to give the folder or the file where this component is defined a unique name anyway? Internal nodes technically don't need to be named when using Tailwind, but if for example I'm writing a Slider component, do I not want to name the "handle" thing that can be dragged anyway? How was finding the "handle" word a non-insignificant hurdle? And if I need to talk about this element with a colleague what would you call it anyway?
Also I'll never be able to understand to point of having extremely atomic utils like `bg-color-green-100`, `pb-40` and such. We have custom properties for that and for a large design system, maintaining variables in a css file is more ergonomic than editing a tailwind config.
It is the endless susceptibility by software developers to marketing campaigns.
There are real costs to the use of any tool and these are completely glossed over by someone looking to sell you goods or services.
Here are some very real costs: technical attrition, maintenance, and training.
Technical attrition: Perhaps some people would use the terms amortization or depreciation, but I consider software to be in a separate class than intangible or tangible assets, the point being, there is a rather short shelf life for almost any technical product. CSS isn't going anywhere but tools like Bootstrap will come and go.
Maintenance: The old version of a CSS wrapper will stop receiving support and require upgrading to the new version which will take significant time and effort.
Training: There are orders of magnitude more individuals that know CSS than know Tailwind and they will be less productive for a given period of time.
- Semantic class names for components like buttons, sidebars, etc.
- A wide range of utility classes to fill in the rest.
Utility classes (in my opinion) are absolutely the best way to handle different types of layouts, positioning, and spacing. Especially when it comes to overriding the component level ones from the semantic class names. This is not a novel approach by any means, I mean Bootstrap does it and it's still great.
Another problem is readability. When things aren’t getting named, the files are often unreadable at first glance. And that’s what they are to external services like Pendo, which rely on classes to tie their thing into the UI. Which are now susceptible to change on a very regular basis, because when you change Tailwind styles “that only changes that particular instance”, or so the story goes.
This can be mitigated by enforcing one-classname-per-line, but it's admittedly not the standard
Why aren't those buttons encapsulated in reusable components or templates?
What sorts of changes do you need to make where this is an issue?
When building a design system, there are a bunch of default styles, and then there are variations based on context. To put it in a real world example, imagine a newspaper heading style that varies depending on where the heading appears. Front page headings are bigger, sports pages might use a different style, and so on. Same with buttons, cards, tabs, what have you.
This isn't impossible to build with Tailwind by adding, say, react logic, but it becomes a mess quickly. If you have a well-built design system, class naming shouldn't be that hard to figure out and communicate to developers. But deciding on a coding logic to account for these context changes and sticking to it seems not as easy, especially when developers come and go.
Global defaults are a separate matter. As are prose styles / styles for UGC text because you don't know the HTML structure ahead of time - by all means use the cascade here, but for almost but for everything else you have two maintainable options:
1) Some CSS naming system or scoping system, e.g. BEM where you enforce proper naming and selector complexity limits (except in rare circumstances). This is extremely hard to do in the long term on a big project.
2) Leverage the component system / template system of whatever system you're using to make reusable components and use utility styles.
Using the cascade extensively on a long running project is a recipe for maintainability disaster - you will face a wild goose chase for the file you want every time you want to make a change, and then another wild goose chase for unexpected changes because your selectors weren't specific enough.
I'm also pretty strongly of the opinion nowadays that separation of concerns of CSS and HTML is a false separation - the single concern is how a thing looks on the page.
At one time a web designer could write CSS and HTML, pass it off to a web developer who would generate the HTML in some manner and use the same CSS written by the designer, who could then go in and make changes to the CSS without having to involve the developer or much if any of the developer's tooling.
The separation of concerns is not just about code organization. It is also about people organization.
Your average React component is starting to look like late 90s PHP: an intermingling of CSS, HTML, SQL calls, the kitchen sink, the entire cast of Glee, Kit from Knight Rider and your high school nurse.
.frontpage h1 or .frontpage .main-heading is going to have more specificity than .text-xl.
Option 2: Find a visual error, open e.g. React Devtools, find the component name, open the IDE, find the component source file, find the HTML, change the utility style class, job done - 5 minutes.
These are not contrived examples. I had a one liner take 40 minutes to find in a codebase a couple of weeks ago. Not an exaggeration.
I really used to believe in separation of concerns but I don’t any more, because of all the time I’ve had to spend fighting with “option 1” codebases over the years. Pretty much any big codebase with normal CSS is going to be an “option 1” eventually unless it’s the work of a single good CSS developer (rare skill), or a team where someone who’s a good leader also cares about CSS enough to enforce a convention like BEM in perpetuity (even rarer skill).
Anyone building anything complex these days is using some component abstraction - whether it’s client side or not - to manage complexity. May as well make use of it for styles if it’s already there.
The reason that there’s a backlash against separation of concerns, the cascade, and (low) specificity is that each of these things is a complete disaster for maintainability in the long run.
One note in making an example of that Catalyst button. If you wrote out all the styles required to do the amount of work that button is doing it would also be extremely difficult to understand, and what’s more, it’d be in a completely different file to the single thing that gives it any meaning - the HTML.
Tailwind is a way to justify writing inline css in our HTML. This was once considered the cardinal sin of a frontend developer. You avoided inline-css at all costs.
But now Tailwind just makes a class for text-color-blue which simply abstracts the `text-color: blue;` css property away into its own class. But don't fool yourself, you are just writing inline css by a different name.
There is no way to explain how it is more maintainable to write `class="rounded button hover:bg-color-black-700 bg-color-black-400 active:bg-color-black-500 focus:bg-color-black-500 rounded-sm text-sm text-color-gray-300"` instead of just `class="btn btn-primary"` using the "old-school" semantic approach. I can list numerous advantages of the semantic approach here. The only advantages of the tailwind approach is that you can style as you go and you can see the resulting style in the HTML page.
Tailwind fanboys will say "it's easier to style in the html". But that's not true. Tailwind to be done right needs a fairly complex set of tooling to enable the hot-reloading, minification, and parsing into your pipeline. None of this is necessary with simple CSS. Semantic CSS "just works" with no tooling whatsoever.
Then again, I'm old enough to have contributed to the famous CSS Zen Garden back in the day. So maybe I am just an old curmudgeon. But like all things, I expect that Tailwind's popularity will wane. Remember that before Tailwind, everything was Bootstrap. The point is that whatever tool lets developers be lazy and avoid learning fundamentals will be the tool that prevails.
The one thing I appreciate from Tailwind CSS and I'd like to see added to CSS itself is that I don't have to write media queries everywhere.
Nothing destroys the readability of my CSS more than adding multiple media queries to adjust styling for different screen sizes. I wrote a component that used CSS Grid and needed to vary the layout as it resized, and over half of the CSS is now media query definitions.
So having a built-in way to streamline defining media queries, and a built-in way to select in line styling from breakpoints a la `py-2 md:py-6` are my 2 asks for CSS after using Tailwind.
(I would prefer to do away with many breakpoints and have a properly fluid design, but IME it's a lot more work and designers can't get their heads around it, let alone clients. And where images are involved it's easier to have a smaller range of possible sizes for the srcset)
https://miragecraft.com/blog/responsive-css-properties
This is what I use for my own site.
I had a rough road learning css, and have been in a few situations where my css/design skills were insufficient to the tasks I was expected to solve. Inline styling let me work more intuitively, and in large part skip learning a whole new paradigm/architecture "just to style some components".
As I eventually grew and had time to work on styling/design in a less stressful environment, I gradually got better at it. Not by practice so much as by learning a new way to frame the problems of styling differently from the problems of "pure" programming.
Had tailwind been around back then I'm sure I'd be a big proponent. It's a product that proposes to solve the cognitive dissonance for a programmer with lots of experience coding, but little experience designing being asked to structure and reason about their work in an uncomfortably different way.
I think tailwind solves a real problem, just not the one we're being told it does.
Sure - it's very different to the old way, and takes some getting used to, but a lot of people enjoy using it.
But also people can still build sites the old way if they wish - that's ok too.
The key difference is when I want to change how my site's Buttons look or behave, I only have to look in the `button.ex` file, instead of tracking down the relevant button classes in a mess of CSS files.
Tailwind is not a good solution if you're writing something for CSS Zen Garden or a my_blog.html type website. Of course the repetition will render it an unreadable mess. But it is a wonderful solution when you have a framework generating the HTML for you.
Unless of course you have several similar buttons with only minor differences between them. Maybe copy-paste?
And then you have to slightly tweak your buttons... Or add conditional effects or dark mode and the complexity explodes, just on a simple button.
You separate it into a .button class, and then further specialize it by adding more classes (like ".button .button-primary").
With Tailwind everything is duplicated, but with CSS you can move out the common parts.
With CSS you also have to worry about the cascade way too much, so while you are doing much of the same work with Tailwind, you get the nice benefit of inlining the styles which will actually apply to your component. Yes, you still have to think about organization and how to make different styles apply to variants of the main class, but it's so much easier to not also have to worry about how cascading rules might apply and constantly check different rules for specificity and overrides
If so; what would be the motivation for using JavaScript to style a button? Call me old - my coworkers do -, but that feels counterintuitive.
Please note that I haven’t really formed an opinion on Tailwind yet. This is an honest question.
For example, partials in classic server-side rendering libraries can be thought of as components, although they tend not be used at such small scales. In CSS patterns like BEM, the B ("Block") is in many ways a component with different sub-elements and states. This is partly why Tailwind provides the @apply directive - it allows you to create "CSS only" components even if you don't have other ways of creating components. That said, other forms of components are often easier to use, and if you already are using them, Tailwind recommends using them for style reuse rather than @apply.
I also don't have experience with native webcomponents or HTMX but I believe it would apply to those as well.
I do have experience with HTML templating such as Jinja and ERB and it would absolutely apply to those as well (though I don't think those would count as component frameworks, and certainly wasn't what I had in mind)
Basically some system which allows you to write some "unit" of markup such as
<li class="search-item-result">
<div>
<img src={item.icon} />
<a href={item.url}>{elided(item.url)}</a>
</div>
<div>
{item.previewText}
</div>
</li>
Which represents some logical unit which should be displayed in a consistent manner (with allowed exceptions, you can style the first and last of the result list differently using CSS selectors or tailwind classes)With Tailwind, you just drop in the class names you want to spruce those up, and your component framework or templating library will give you a way to render a series of them with different properties. You have to repeat neither the markup nor the class names for multiple items
This is possible with CSS of course: Just add rules that apply to `li.search-item-result`, `li.search-item-result>div:first-child`, `li.search-item-result>div:last-child` but this creates a number of problems for maintainability. Say you want to wrap the first div in another element, or change the tag type, or add a spacer element before it. Now you have to repoint your CSS rules.
Additionally you have to think about the cascade, and how your CSS rules might apply to elements you didn't intend them too.
There are numerous systems for resolving these issues (BEM mentioned by the sibling commenter being one of them), but you end up writing a bunch of class names (and IDs for elements you only intend to render once on the page) which you have to name and reference. Then you have to probe devtools to determine which rules apply to which elements.
I can empathize with devs who have their own systems dialed in and don't think Tailwind brings them much value here, but if you don't have that system dialed in or just don't like thinking about names and specificity, it's a massive boon. Also, you end up writing a lot less code altogether since you don't have to write selectors or custom class names for everything you want to target precisely.
The only thing you have in CSS that is harder when working with Tailwind is overrides - e.g. "this component is normally blue, but it's green if this class/parameter/attribute is set". That said, even when I'm using CSS, I tend to find overriding, except in the simplest of cases, usually causes problems, because now I need to make assumptions about the order of declarations that are difficult to make in a codebase maintained by multiple people over several years. For that reason, in the button case, I would typically make it so that the different modifiers (.button-primary etc) are mandatory when using the .button class.
So my code is generated server side, using something Jam-stacky like Hugo or Cloudflare pages, (with a cloudflare worker that dynamically replaces or modifies a button ...in that very one specific edge case.)
Your existing example needs to be made more complex though because I'd just use standard CSS for that, for example button.submit { color:blue) button.submit.critical { color:red }
i.e. all my buttons are blue, except that one special case where I have a "critical button" and we "override" the color to "red". I simply flag it with the extra semantic attribute and style it accordingly.The button will clearly be red because the css for red has a higher specificity, but it also "reads" easily as such if the css is properly crafted. Of course it's possible to write Semantic CSS that no-one can follow; and the same goes for Tailwind.
The problem with CSS though, is unfortunately the most common approach is to add a new DIV, and slap on a CSS definition that ignores all inherited styling. So, unless you're going to re-write everything a big corporate ball of mud is made up of, Tailwind works better here, since it's PURPOSE is overriding and ignoring everything. He says ..ducking!
The other big option is just using a methodology like BEM and being consistent with that, which in fairness also isn't bound to Javascript and has the benefit that - like you say - you're writing CSS as opposed to a DSL that is mostly equivalent to CSS. For me, the problems there come from the lack of tooling for writing really maintainable CSS - stuff like scoping and detecting whether a class is still in use. But it's definitely still an alternative, especially with some of the new features like nested selected that are coming out now.
- marketing - influencers - novice developers who blindly follow infulencers - great documentation
Given these (and probably some more) aspects, Tailwind makes it easy to get some good results quickly which helps to convince users that their whole approach is the right way to go. Add very good marketing and early traction and you are where we are now.
So I agree with the article that Tailwind‘s conceptual approach is not the holy grail it‘s often said to be, but it maybe also is not even the reason for its success compared to many auxiliary things it did/does right.
That is what CSS is. In the melee of “CSS in everything but CSS,” people might have forgotten what CSS is for. CSS’s classes are what you use everywhere again and again.
CSS Utility classes have been there for a long time and were made easy and famous by the likes of Bourbon[1]. They even have pre-defined styles — Neat[2].
Even though Tailwind can and should have been used more as what it was destined to do — utility classes. In a larger team, it is easier to stick to the TailwindCSS as such and have specialty/custom classes based on its utility for your project.
I was checking it out during its early release and introduced Tailwind to a team in 2018 who were stuck in Bootstrap. I wanted to stop hearing, “This is not part of Bootstrap.” The good thing I realized then was that if I followed a custom class with utility based on Tailwind, the result could continue to be the same with someone doing a “button button-blue button-large text-xl text-white.”
Btw, this too shall pass. I suggest learning CSS from first principles — everything that comes later will be another syntactic sugar. Remember, we started our projects with our grid layout that we stole from A List Apart’s faux-column layout, then came blueprint[3], bootstrap[4], foundation[5], bourbon, and now Tailwind, and every other one in between.
There was even a paid service (or a few, I think) that just churns out custom CSS grid layout code based on your drag-n-drop sliders.
Personally I find diving through a bloat of identifiable tailwind classes allows me to review/change/adapt/create UI code MUCH QUICKER than having to re-learn some other abstraction or naming convention that some other developer came up with.
Complaining that lots of Tailwind classes "don't look pretty" makes me think the author has never worked on large projects/refactors using it.
To me Tailwind hits the perfect level of abstraction:
- not too low-level; allowing us to easily review/identify patterns in the code (p-0, leading-3, etc..)
- not too high-level; allowing us to not have to relearn a visual/naming pattern for every new project (.card, .button, etc..)
It boils down to being a search problem: how much of a language/DSL feature can I fit in my head? Reviewing a wall of CSS is harder for me to "search through". On the other hand a wall of Tailwind hits the right spot.
This works great for marketing or landing pages, or other use cases where it’s not you who maintains it after deployment; the next sucker will want to rewrite it all anyway.
I have a feeling people hate Tailwind for making CSS accessible to the masses.
If you're repeating monstrous groups of styles like the one in the blog post, but with a slight variation for each, then you're tremendously far out in the weeds and almost definitely shouldn't be using tailwind
Tailwind really only works for a component-based framework (like React, Svelte, Solid, etc.), where you define a component representing a button, and give it props to map to the 'minor variation' you want.
That way if you need to support e.g. a new device class, you just add the relevant class names there and don't have to revisit your entire codebase.
This isn't apparent at all when inspecting the source of a site using tailwind, because as far as the rendered markup is concerned, these are getting repeated (and the verbosity of the output is absolutely a fair criticism)
But if you're writing it, and organizing your components in a sensible way, then the problem you mention doesn't exist.
If I had to summarize this I'd say don't fall victim to hype cycle and marketing. Make technical choices based on the problems.
I've worked on teams where CSS was a mysterious language with many gotchas. So I can see how Tailwind can help with this.
However, I wrote this article for the love of CSS, web standards, and the web in general. I think Tailwind's dishonest marketing tactics aren't doing good for the development community. Especially for young developers who are suddenly told that things like separation of concerns and good naming practises are a bad thing.
But in that instance, all of those css rules will still exist, but they’re stored in a separate css file and given a class name.
Having the reusability at the css class level means that someone else might break your component by updating the css.
With Tailwind, the reusability is at the component level. All the styles are self contained. No one can break your component accidentally.
He’s not saying “separation of concerns and good naming practices” are bad things, he’s saying that separation of concerns between HTML and CSS is mostly a false separation (it’s the same concern - making the UI look right) and that good naming practices are hard - best just do that in one place, the component that address the concern with both style and structure.
I think a lot of the complexity in CSS is accepted because of complexity in other parts of the stack. I've dug pretty deep into real-world use cases of many approaches to front end and ultimately, tailwind solves the #1 issue people encounter daily.
Fear Of Breaking Something Else Entirely
by never leaving the html tag for a secondary resource, confidence today is gained at the cost of future compound complexity.
as someone that specializes in design systems, tailwind breaks my heart because it it fairly reductive in expression. Truly white-labeling a product is not as simple as changing variables, it is more like a zen garden of css.
1. Create a utility CSS system, use it, feels nice, publish an article around it why it might make sense and why it feels nice (which is probably a hindsight explanation)
2. People start using it, way more than ever expected.
3. Iterate on the product
That’s it. I’m pretty sure nothing about this was planned, the vendor-lockin wasn’t planned and just happened.
While your arguments about inconsistencies about semantic components in Catalyst vs semantic class names are right, it is highly likely your assumptions are missing a critical piece, because there are clearly many happy users.
You seem to deliberately not present tailwinds strongest arguments. For example, you claim their is no next step to abstraction, but literally in the paragraphs before the ones you quoted they say to build reusable html+css components instead of trying to make css only abstractions. Which very clearly responds to your argument and complaint.
There are other places in your writing like this.
It's fine to think and claim they are wrong. But it would be a lot more persuasive if you take them at their best and knock them down, instead of being selective.
Overall take their absolute best case scenarios and reasons and show why you are still right.
> Turns out there is no next step. Or it kind of exists, but it's called "premature abstraction". You can start extracting classes with @apply, but the documentation for reusing styles [link] describes it as a bad practice.
> But what should I use @apply for if not for cleaning up? The documentation does not say. It only tells me why it should not be used.
If you jump to the Tailwind section they link to here, the short section immediately before explains with an example you should be using HTML templates to reuse styles over @apply, which is really core to understanding Tailwind:
https://tailwindcss.com/docs/reusing-styles#compared-to-css-...
Unless a component is a single HTML element, the information needed to define it can’t be captured in CSS alone. For anything even remotely complex, the HTML structure is just as important as the CSS.
Even if you create classes for the different elements in a component like this, you still have to duplicate the HTML every time you want to use this component. Sure you can update the font-size for every instance in a single place, but what if you need to turn the title into a link?
Components and template partials solve this problem much better than CSS-only abstractions because a component can encapsulate the HTML and the styles. Changing the font-size for every instance is just as easy as it is with CSS, but now you can turn all of the titles into links in a single place too.
When you create components and template partials like this, there’s no reason to use anything other than utility classes because you already have a single source of truth for the styles.One of the often overlooked dividers between those who like Tailwind and those who don't is whether your Web framework of choice has a concept of components.
Template partials with their lack of properties and a defined signature (hi autocomplete!) don't cut it in my experience.
I honestly find the real debate to be inline styles vs stylesheets.
Tailwind is just fancy inline styles. Personally, every single time I've used stylesheets in a large production-grade project it's an absolute mess. Every project I start now I use inline styles. I'm pretty indifferent on Tailwind though, it's sometimes nice to get opinionated defaults.
Adam should have said that Tailwind is a good solution for him. Instead, he said that Tailwind is better than everything else and piggy-backed on famous people in tech to sell his idea. Which created a cult-like following.
Cults are bad for tech because it prevents out-of-the-box thinking.
I think tailwind is valuable and as always middle way is the best approach but more difficult to structure and definitely not good for commercial purposes.
My favourite way is to put tw classes for spacing components and layout directly in the template while using bem-like naming for the component ‘s own appearance.
Having utilities like mb-2 that can override the default set in a component I think solves many headaches and is one of the strengths of having a full set of utilities.
However I also find a real strength is having a standard accross devs AND projects and so when tailwind advises to customise your project. Change the meaning of mb-4 or text-lg then I find it completely undermines the value of having a standard utility set.
Traditional way of using CSS I feel is still advantageous for websites (instead of apps) with a limited set of highly reused styles.
Nowadays however there’s a general lack of restraint and self-control when it comes to web design, you must have a million variations of everything.
In that case utility-first CSS clearly wins because there’s almost no reuse except at the per-property level.
I prefer using plain CSS. I like semantic CSS and similar ideas of associating certain component class names with certain styles.
Tailwind is nice because it’s fast and you have a good idea what you’re going to get while writing it. Nobody believes that it’s the pinnacle of performance or maintainability. Keep using Tailwind if you like it and it’s appropriate for your project or business.
Atomic classes really shine in this aspect, since it’s really just what you have in html and nothing more.
Another fantastic quality is the fact that you can really tweak things slightly in the html and skip inventing a modifier class just for this particular situation.
I think bootstrap css hits a really sweet spot there, since they have lots of reusable components, which are very easy to use and extend and a huge bunch of utility classes to accommodate for exceptions. I know that’s not an argument for spas, since you have components there, but for server side templates that’s the best
For laying out items it might be useful but for styling it is not (why would you create a separate component for each HTML element, like a button!?).
And I even don't get the styling example in the article.
Why would you use a 'primary' class on a button while the button type makes it primary?
If you style your submit button with `button[type=submit]` you are sure all submit buttons in your app look the same. There is no need to use classes. (I also never understood why it is common to add a 'table' class to a table.)
I think the main reason why most CSS files become such a mess is that style and layout are not separated.
Tailwind didn't win because of marketing... it's marketing was no better or worse than the million other new products that show up in a developer's feeds every 22 hours.
Lots and lots of people like using it. That's the whole story.
I started using Tailwind pre 1.0 and if my experience with it had been shit, I would have stopped using it.
https://htmx.org/essays/locality-of-behaviour/
I don't use HTMX as I would like a smaller JS bundle. I would also like an efficient CSS system of utility classes. I think Hypermedia libraries & CSS utility libraries can be improved. I'm also working on app systems to reuse code across domains...using a consistent domain language. A challenge is interoping my domain ontology with other domain ontologies. The class name interop is an area where Tailwind has found success. You can start with their prototypes or another developer's prototype with copy/paste. This is great for engineers/developers who are less inclined to spend enough time learning the art of perfecting design.
This can happen in semantic markup as well, however the bounded context of the example site is less likely to interop with your app...because the semantic markup is less bounded than utility classes.
Tailwind also has `purge` to remove unused CSS classes. Yes, `purge` is a build step & it requires the js/html content to evaluate. However, if you want to optimize the JS bundle size, you probably use a build step.
I'm so glad I no longer have to wonder what primary means, let alone the other weird names people come up with for classes.
And if you don't want to see the classes, just write it out into a reusable component called PrimaryButton, if that floats your boat.
The point is exactly that you don't need to worry about styling in the HTML. You just structure the HTML. If you want to know what primary means that you open the css file and search .primary and see all the css properties right there. It was never rocket science.
The HTML was designed to be the structure of the site. So you can designate items as buttons, primary buttons, secondary buttons, success buttons, and so on. Then in the Style document (CSS) you would design what you want a button to look like, what a primary button would look like, how a secondary button should look like and so forth. Then together you have a website. If you want to change the primary color of the company from blue to purple than you change it in one place in the CSS and now the website is updated. If you want all the buttons to have a new hover state then you add it in one place in the css and the HTML doesn't need to know or care about it, but it updates everywhere. It was a simple system. Now we are designing inline in HTML.
Yes you could abstract it with frontend javascript components like you suggest. But thats like building a boat to cross a puddle. Not to mention it just brings you back to the original semantic styling context, but using multiple unneccessary layers of abstraction over what could be simple CSS. You suggest using tailwind to put into a react component, just to style a button with a semantic name. When you could use native css to do exactly the same thing with no dependencies or integration workflow required.
For me personally, I've rarely had a design to implement that didn't require a bunch of scaffolding html to conform to the design, css alone was never enough. But that means for all but the most trivial of changes you will need to touch the html and the css.
So the question becomes, is it worth maintaining that separation?
My experience has been that most sites get built, get signed off on, run for some number of years before the site get redesigned and rebuilt, we're rarely making changes to a site that are just simple like the colour of something, all that was decided during development, most of it was signed off on during the design phase even.
So I think that's the angle Tailwind fans take, if the point of separating css from html is to allow for easy changes to the design, but those changes are either never required or can't be achieved via css alone, what's the point? Better to go inline because the development process is quicker and easier.
I don't understand why you think this is like "building a boat to cross a puddle". Folks have struggled with this since the dawn of the web. It's hard.
Most of us aren't crossing puddles, we're trying to cross oceans with gigantic diverse teams.
The snails pace updates on TailwindUI are bad enough, in the time it takes them to release one tiny ~20 line component you'll see half a dozen new and better UI frameworks pop up. But to add insult to injury their obssession with over the top React and Vue usage on the most basic of components just highlights how disconnected from the reality of web development they've become.
It's a CSS framework. I shouldn't be seeing Next, Nuxt, React, Vue or whatever 'hot thing of the week' they're gawping at.
Where tailwind on its own shines is inside a Laravel project with blade components. They are their own individual, reusable and extendable items. Wheather thats better or worse than css classes is up for debate however.
Semantic CSS class names are a failure because the context in which they are named is often a moving target. In the real world, a good name for something at point A in the timeline is not likely to hold up at point B for the majority of developers out there. It also relies on a persons capability to extract meaning from an arbitrary name and hope that their understanding of what that means lines up with the original developer's intention of what it means. And then that can fall apart if you are working with teams with different cultural perspectives and language proficiencies.
Utility CSS also lets you tie into an underlying design system that enforces consistency. You know someone doesn't know what they are talking about when they make the claims that tailwind is just inline styling because it isn't. Inline CSS styles are absolute. Utility CSS classes are tied into a design system that can be mutated and changed.
Honestly, your whole Nue project is written about in such a way as to be mildly insulting. Like I would never even consider using it regardless of whatever supposed superiority it may or may not possess because you kind of come across as a pretty unlikeable know it all. I don't know if that's true or not, but that's the impression I get reading the stuff you've written. This article is sort of another example of it. You literally tell people who use tailwind CSS to "learn CSS" and that we are taking part of a "trend". It's got the real dinosaur vibe get off my damn lawn vibe going for it. That's saying a lot because I'm pretty sure I'm older than you and I've been doing this as long, if not longer.
First off, it feels weird for the developer of NueJS to be throwing shade at other tools for "marketing and misinformation", given the number of articles I've seen extolling the virtues of NueJS as the silver bullet to solve all your problems.
Secondly, I think it would be useful for the author to reflect a bit on what Tailwind is doing that people find so useful. Obviously not everyone loves it (personally, I prefer writing CSS to Tailwind, just because it's a much clearer and expressive language), but it's clearly very popular, and the reason for that popularity is typically unlikely to be a conspiracy involving easy onboarding and lock-in.
I think understanding Tailwind on its own terms, with its own merits, is really valuable for understanding the state of CSS development right now, what works well, and what needs to improve. The author here seems to see Tailwind purely through the lens of being able to write CSS decorations: in Tailwind I might write `m-4`, but in CSS, I'd write |margin: 32px`, these are equivalent, so why create a whole extra tool just to do what we could already do?
To me, though, this is deeply missing the point of Tailwind. Tailwind isn't just a different way of writing CSS declarations, it's a way of tying those declarations to components. This is nothing new - it's the same concept as BEM, or CSS modules, or CSS-in-JS, or scoped CSS, amongst others. Essentially, when writing large projects with extensive CSS, most people run into maintainability issues, and most people seem to develop similar solutions: they tie styles to an already existing, more maintainable unit of code, namely the component.
I can go into the details of where these maintainability issues come from, and why components are such a nice solution, but if you've worked with large CSS projects, you probably already know that stuff, and if you haven't, I think BEM is a good place to start for understanding how to organise CSS, and why using the concept of individual components with sub-elements and different states is very helpful.
Understanding Tailwind on its own terms, then, we start to get answers to a number of the questions that the author asks. For example, they compare a JSX-style declaration `<Button plain>` to an equivalent with classes `<button class="plain">` and ask whether the difference is just syntax. Well, let's analyse it from a component perspective: with the former, I know where the `Button` component is defined and can analyse it with my editor's code navigation tools, and I know that `plain` is scoped purely to that button. I also can't accidentally use `plain` from a button to annotate a checkbox - plain here explicitly refers to the button's "plain" style and it will not get applied elsewhere.
The same can also 100% be true in CSS - like I say, I'm not a huge Tailwind fan, and I personally prefer CSS solutions where they make sense. But it probably wouldn't look like the author has written it, it would probably look more like this: `<button class="button button__plain">`. That said, this still can have issues - the `button` class is completely global, and woe betide you if you forget it exists and try and define another `button` class somewhere else (or important one from somewhere else).
These questions of scope are hard ones to solve, which is why Tailwind exists alongside so many other, similar solutions. And I can fully understand why someone may not like the Tailwind solution specifically - its DSL for defining decorations is idiosyncratic, particularly if you're already familiar with CSS. But this article doesn't contrast Tailwind with those other tools, instead it compares having a solution for these issues with just not having a solution at all, and then tries to tell you that ignoring the problem is the better plan. I think that's a very disingenuous argument (possibly even an example of marketing and misinformation?) that makes the whole article very difficult to take seriously.