Tailwind vs. Semantic CSS
nuejs.org
nuejs.org
Main problem was: the semantic css was elegant, but understanding it again after half a year of not editing the page took super long.
Tailwind is clear. The code looks uglier but I instantly know what’s going on. No hidden things. And no fear in editing a piece of html that it will break sth else.
Huge upside.
Utility based css selectors that do one and single thing are clear and explicit, removing most (but not all) that hassle.
Forgetting what plethora of non-semantic classes should be used on what elements seems a far more likely problem.
But they a are plenty of situation when it becomes too hard, such as when you can't constraint a designer to your implementation details, or that same semantic concepts have different UI cases.
Longer than converting the entire website to Tailwind?
It's a small website, but it's complex with desktop and mobile layouts, audio players etc etc. There's a design system behind it, but many small exceptions to it to make it look really nice. Writing the new part of the website was so fast with Tailwind that I just thought I'd give the rewrite a go. It wasn't that painful, and while it definitely took longer than understanding the existing bits just to edit sth, it also allowed me to clean up the CSS mess that had accumulated over two years.
If I had just sat down and tried to clean up the CSS mess, it probably would've taken longer if I hadn't converted it to Tailwind at the same time. And I needed to do some cleanup as we were trying to tie different sub pages into a coherent experience.
Just to clarify: please don't take this as advice of "go and rewrite your code". Rewrites are almost always bad. But in my case here, it was a good decision. My personal learning is that a startup landing page just changes too much to be able to fit into semantic classes nicely.
Point being: manually sprinkling mb-2s throughout your codebase is a recipe for disaster: an sure way to an unmaintainable frontend.
And then 100 times doing the same later... We all know how it ends up looking because we all did it.
On the other hand, in tailwind you could organize things well using your template/component system, be that ReactJS, Vue, ERB, EJS, CSHTML etc.
I agree there is subtlety though. A well organized and "benevolent dictator-ed" crop of CSS files is going to be more powerful than it's Tailwind equivalent due to having selectors etc. available.
For a lot of applications (like a blog), what the author calls "semantic CSS" is the right thing to do. But there comes a point at which it is no longer feasible. We have a long history of SMACSS, object oriented CSS, BEM, CSS Modules, scoped CSS, and now Tailwind to make it work.
> Because mastering CSS requires practice. It takes several failed attempts before you get it.
Explaining the popularity of something by simply proclaiming "git gud" isn't a very strong point. For newer CSS devs, it does take away the woes of clashing naming and a badly structured cascade. The rest remains and the results remain shite. Similarly, I haven't seen a pattern where good CSS authors do not like Tailwind.
The fact is that updating css in a big project and a big team is very difficult. Rules are scoped globally. It only takes a junior making a few design mistakes and now you don't what you are going to break if you update anything.
With tailwind the css is fixed and will never change. So you just change your html and you know what to check/what to test again. For any medium to large project this is a big QA & time boon.
"just use code review, naming convention, xxx best practice". This argument is similar to "just don't make mistakes". Mistakes will be made. With semantic css you will suffer.
https://nuejs.org/blog/tailwind-vs-semantic-css/#best-practi...
But you don't explain why the tailwind version is tightly coupled and the semantic version is loosely coupled. And you don't do it because it is simply not the case. The coupling between html and css is not tighter or looser. It just goes in a different direction.
> The semantic version, allows you to change the design of the gallery freely. You name the component and style it externally. With Tailwind the style cannot be separated from the structure.
Same with tailwind. You update your component to update its style. With semantic you can update the css rules without touching the html, but you don't know if this css change won't break another part of your design. If you have a simple html structure like a blog with very few components, then semantic works (but so does anything really). If you have lots of components and you need to update them in order to add new features, then semantic will bring more issues.
as for tailwild, I just can't bare its wide-ness - too many className attribute string going over 150~300 char widths, with some ternary operators mixed on top...
(seems vanilla-extract / ete have better DX, with occasional inlining for immediate deadlines)
But the problem is that you need to make sure that a given css change is going to affect only a specific set of component. So you need to check all components. Since it will be too time consuming, you will probably skip this step and hope for the best (and do some QA to check that nothing is obviously broken).
- read from webpack's output
- read from css-in-js: emotion / vanilla-extract / etc
What kind of fragile markup and problematic CSS global rules are you worrying about?
I’m wondering that too. If I’m writing a SPA, styles are usually scopped to the component with a base styles for UI atomic elements. And some conventions around spacing. If it’s a website, it will be separated in layouts and small components Bootstrap-like. An update will be identified as a variant or a special case.
As for coding this, search all files is usually a godsend when refactoring. You can use ripgrep if the editor does not have a good implementation.
You want to preserve the theme an consistency of the UI, but adapt its functionality. For this type of changes the semantic version is problematic.
You don't like global styles? Don't use global styles, period.
Even without adopting Nue and staying on React, CRA and Next comes out with CSS Modules out of the box. Angular and Vue have their own equivalent.
Isn’t it much more meaningful to create a <gallery> component (it can be done through a template-engine, react, whatever, that’s another point).
I mean, sure, we could just program with void pointers everywhere, but is it really a good idea?
If I modify the HTML structure, the component specification changes and CSS obviously needs to be updated.
Loose coupling vs tight coupling has an established meaning ≈ “if a change to module X causes a failure in module Y, then we say X and Y are tightly coupled.” This has some “knock-on” effects that you are trying to gesture at, like, “If two modules are tightly coupled then because of that tight coupling we usually have to coordinate deployments between them both.” And then you're like “I can ship new CSS without having to coordinate a deployment of HTML, that must be loose coupling then!”
But this is a sloppy way to use the language, because in the one case you have one module, and in the other case you have two: there is no coupling in the Tailwind case because there is nothing to be coupled because it all ships as one module, then you construct a more modular approach, one with two modules in fact, and these modules are (depending on how you squint at it given that we are doing decorative programming and there are no explicit errors) in fact “tightly coupled”—the semantic HTML essentially provides a customized API which the CSS uses by hooking into; this is a common form of tight coupling in microservice deployments for example.[1]
So you are actually looking at, one module plus a static asset that defines a common language, vs. two modules that are tightly coupled, and declaring that the two modules are “more loosely coupled” and this is somewhat of a nonsense thing to say. Like I get what you were going for, I didn't write this as a top level complaint because I understand what you meant, but here in the thicket of comments I see that you are not communicating well with your interlocutor because of this sloppiness and language, you might want to pivot?
[1] This point about microservices is likely to outrage a random passerby, look I am sure that your microservices use RPCs against HTTP APIs in a way that doesn't cause you much grief in having to update multiple modules in one commit (if monorepo) or stage “topics” (Gerrit’s term) where you carefully have to merge 2 or 3 pull requests to different repositories all at the same time (if multi-repo). But even though your microservices are loosely coupled, I have worked on very similar architectures where we did have to stage topics and if you didn't then changes to service X would break service Y, “We’ll ship a new value for this enum, that requires changing the common schema repo, which requires updating every microservice that looks at that enum so that it doesn't have a deserialization failure ‘this is not an allowed value for that enum’ when the new values start rolling out, the ones that actually switch() or if() on that enum need to be updated to do the right thing, oh, I missed one because it relies on the contract that if enum == x then fieldA is set but if enum == y or z then fieldB is set but now when enum == q neither fieldA nor fieldB is set...” and I am happy that you haven't had these problems but yeah, some microservices are tightly coupled by this definition.
vs here:
(the gallery component)
Is kinda what I said.
Isn't "CSS scoped to components" nowadays a basic feature of frameworks?
Your “semantic” CSS depends on your HTML structure. Tailwind’s HTML depends on a fixed set of CSS styles. None is any more coupled than the other, it’s just that the direction of the dependence is different.
Depending on what is expected to change more, both can be a valid approach: e.g. for a blog post that only uses heading, emphasis, link, styling it from CSS alone is definitely the best choice (that’s what it was developed for). But I would argue, most real-world web applications see more HTML-changes, and thus the other direction may make more sense.
https://nuejs.org/blog/tailwind-vs-semantic-css/img/markup-b...
well I haven't used Tailwind but from the examples I've seen I would hate it, and from what I can see I would hate it for the same reason that I hate all CSS abstractions I've worked with - because they limit what I can do with CSS in the interest of making it easier for other people who are not that good with CSS to get their work done OR it will require me to do things in a particular way when I believe I know a far better way of doing them.
Now I'm not arguing I am a good CSS author, I do lack some things (like a good feel for design imperfections so I don't notice when something is off) but I am saying I am generally familiar with this behavior in devs regarding other technologies, if one is sufficiently good with a technology one dislikes restraints placed on usage by some external library.
This is why people always comment "This would be so easy if we weren't using {X} technology on top of our code" because they generally are comfortable using the lower layer that the higher layer is now controlling and preventing them from doing their work in what they see as the best way to do it.
So, all that said, I would expect that if Good CSS authors were forced to use Tailwind they would not like it. The assertion to the contrary is extremely surprising and I just don't believe it without some extremely surprising proof to back it up.
Tailwind explicitly doesn't do this. You can do pretty much everything you'd do with normal CSS. There are a couple of edge cases where you have to add configuration (e.g. if you want specific media queries), but Tailwind doesn't limit you in any way.
I also support the assertion that good CSS authors like Tailwind.
Both of these can be technically worked around. Config may be able to disable existing margin/padding classes and replace them with a custom set, and the groups feature helps especially for simple cases of referencing other elements.
In my experience though, as soon as config is meaningful modified or you elan on the more obscure features for groups, pseudo selectors, etc. the learning curve has just been moved from the dev who doesn't know CSS to the dev who doesn't know Tailwind.
- Did you compare both versions of the page for feature parity across multiple browsers, devices, and breakpoints?
- what exactly accounted for so much bloat with the tailwind version of the site? Was it the CSS itself, or the classes that accounted for most of the difference? If it was the CSS, did you optimize it as per Tailwind's docs[1] so that unused styles weren't included in the final page styles?
- How much longer (if at all) did you take to design your selectors for the semantic version in order to reduce repetition, than you might have taken for creating the template with tailwind?
- How did the final sizes compare after brotli compression?
2. I did not optimize the Tailwind version. The Tailwind developers did.
3. I have no idea. I only did the semantic version. It was quick, because I have done so much CSS in my life
4. You can check that out yourself. Both sites are brotli compressed
But tailwind/nextjs version has a lot of files incluced (ie. svg icons) which the other extracted to separate files.
Also, nextjs adds its own code that is completely unnecessary. Such as, 15 reponsive versions of the same image file, script tags at the end with the whole content in json.
This comparison does not feel objective (or honest) at all to me. If you want to prove to professionals that your CSS solution is better, you need to provide much stronger evidence. Preferably ones those professionals can't disprove within 30 seconds of comparing the examples themselves.
I have a few questions:
- What exactly do you mean by "Semantic CSS"? I've never heard this terminology before (might just be OOTL). I get the parallel between this and Semantic HTML, but I guess it's not as clear what it is supposed to mean for a styling language to me. Is it just native CSS (or "pure" CSS)? At least, as a result, I have no idea what this means, or by what measures you decided what constitutes "semantics" in Tailwind's CSS:
The most surprising thing is that Tailwind uses more global/semantic CSS than the semantic approach itself ¯\_(ツ)_/¯
- A small nitpick, maybe: could you somehow replace the top links to the two versions you compare with actual HTML links instead of JS events? This prevents middle-clicking and the location is replaced so it's tough to compare the two sites while reading your article. I have to click on your link, copy the url, and open that in a new tab. At least, opening the page in a new tab would be nice.<a className="group mb-8 flex h-10 w-10 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-80
The links are now fixed. Thanks!
I have often used their components as a basis, and clean up huge chunks because I feel that is in my interest for future maintainability - it is not a fault of Tailwind, but a decision they made.
You can make a "semantic" design , using Tailwind where it will save you tiem or add readability, and get the best of both worlds.
Can't wait to use Nue for my next project, currently wait for CSS implementation (even if it is not necessary for Nue). Project is currently in design phase, so i have luckily time.
- The whole "amount of CSS" part is unfair when the Semantic CSS implementation isn't responsive at all, so of course it will be lighter, it does less.
- The part about the big number of HTML elements is a bit frustrating too; tailwind doesn't require you to use more HTML tags at all. It's totally possible to redo the Semantic CSS example with tailwind by not adding any HTML tags.
Besides that, it's still interesting to try and compare what is the best between big HTML (atomic, tailwind) vs big CSS (semantic).
Tailwind is not perfect, and sure, sometimes, you can get more performant code by writing it the semantic way. Sometimes.
But tailwind sure is a great way to easily write maintainable CSS in a team with different skill sets, producing really performant code by default, on large web apps.
- How is it not responsive? I can easily fix.
- The Tailwind example is the official template made by the Tailwind developers themselves.
- sure, but since the tailwind page does more things, it's logical it has more code. Your base for comparison is production used code, sold to people. So of course it's polished, it must handle browser bugs and other things you might not expect in a quickly implemented alternative for a tech article. So I'm not surprised the code is bigger. That doesn't say at all that tailwind == more HTML tags :)
You did the exact same thing before when you compared the Headless UI combobox with your nue.js implementation. Offering fewer features will result in less code. Shocker.
Besides, I don't really care for these comparisons. If something is 2x longer code but more maintainable, it is 100% worth it. Just because something is short doesn't make it better.
Having said that, I promise 100% feature parity on my next article. Thanks for the heads up!
But you can have the best of both worlds with apply:
.card {
@apply p-2 rounded shadow text-gray-700;
}But with reusable components or unified design, you enter the @apply hell. At this point i could not see benefits of tailwind.
But there are other (better) options available in most circumstances. Tailwind actually have a pretty good write up here:
https://tailwindcss.com/docs/reusing-styles
That said, it’s easy to come up with a ‘gist’ like
> The semantic version is 8 × smaller, renders faster, and is easier to modify and extend.
but then underneath say you’re not minifying, and you’re not publishing the source code… it’s almost like the author had the conclusion before starting to experiment.
In fact, if there's enough interest, I'd probably build it too...
Possible on a later bigger update (if necessary), a complete app would be rebuild. But here is the same problem: why should you do it, if your project works?
I personally had not seen such an update in early stage of a startup. Did somebody have insights or examples?
Tailwind is somewhat like lisp in the aspect that people usually don't get what all the hoopla is about. All they see is parenthesis and want a "lisp" without the parenthesis once they figure out polish notation
* The irony being in this article is that the CSS file is also larger
- There are useless empty elements in the tailwind example - There are so many different breakpoints, paddings and margins in the depths of the tailwind DOM, that can not be achieved with the minified semantic markup as it is simply lacking the DOM structure necessary for it. Substituting just html > body > header > nav > a with html > body > div > div> header > div > div ... just isn't a correct comparison. It's like comparing airplanes to cars. - There are data sttributes such as data-new-gr-c-s-check-loaded or data-headlessui-state. Also, totally unrelated to the semantic markup comparison.
This is just bloated asf.
Do you even know what semantic markup is? Maybe some people are just stubborn and try to find anything against tailwind because they just don't like it.
I always thought semantic css was easier and made more sense, and when we were using Style Components we could still go ahead and have the styling right there in the javascript code if we wanted, best of both worlds, really.
But right now the whole industry loves tailwind so I use tailwind. So I use react, er, nextjs. So I use webpack. So I use yarn, or wait we're back on npm now right?
The template the author is comparing against (https://spotlight.tailwindui.com/) has better HTML structure, descriptive text and aria labels on buttons, icons and images. This is what the semantic web is about.
You could argue that the author's template loads faster (not for that much honestly) but the UX won't be better.
Tailwind or Bootstrap's (just to provide an additional example) documentation presents accessible and well styled patterns that end users will benefit from.
People complain about the bloat of these frameworks, but they don't make a fair comparison when checking examples for accessibility, responsiveness (mobile experience) and other features that make a great UX and help with SEO (because in the end accessibility is SEO).
I agree that using Tailwind--without extra effort--will end up with "meaningless" (not really) classes in the HTML.
But semantics should be prioritized in HTML rather than CSS or class names. What's the benefit of having semantic classes when your HTML is inaccessible and unsemantic? e.g., using `a` instead of `button`, `img` with unsemantic alternative text.
> But this article is not about the whole UI/UX stack.
And this
> It focuses on benefits on what semantic CSS offers. aka "naming things" vs "not naming things".
That's the problem, you are focusing on giving semantic names to classes while giving an inferior UX on your example. If the inferior UX saves me a few KBs on size and a few milliseconds in load time, I'd still prefer good UX.
A fair comparison should be good UX with semantic CSS.
- Semantic version is not responsive (sure it looks ok but isn't optimised)
- Tailwind version has 13 inlined SVG icons (on the homepage) which increase DOM size (Semantic version has none)
- Tailwind version uses optimised markup for SEO purposes which leads to increased DOM size (<ul> list for navigation; not wrapping entire article summary in an anchor; using <dl/dt/dd> structure in work section)
This is clearly not comparing apples with apples when it comes to semantic HTML+CSS vs Tailwind.
However, most egregiously, the author goes on to make a bunch of comparisons between the Next.js tailwindui marketing site and his Nuejs-generated template for the argument that "Tailwind is bad for performance". This is deliberately misleading.
There are plenty of valid criticisms of Tailwind but it's hard not to look at this article as anything but a puff piece for the author's framework rather than a serious comparison of two approaches to building UIs.
.button-primary { @apply rounded-full focus:ring focus:ring-orange-500 ring-offset-4 outline-none px-6 py-3 etc...
and it gives you the best of both worlds. You refactor common css code into components and still have the amazing flexibility of utility classes.
By using @apply excessively, now you have to deal with class names and the resulting messy conflicts, and yet you still can't/don't write plain CSS and harness its full power.
@apply is one of Tailwind's worst features, imo.
> even then only if you’re not using a framework like React where a component would be a better choice.
The big problem for me, that the author also argues that the Tailwind CSS version is an official template and, therefore, it has to follow best practices.
I think this logic is flawed, since in production (and especially if I sell templates online) I aim for readable code, which may result in more markup used. Anyone, who knows Tailwind CSS well, could copy the author’s version of the site with the same HTML markup and achieve the same output. Only then it would be a fair comparison, and I bet the results would not be the same at all.
And I personally find less code more readable than more code. Particularly with Tailwind where the difference can be 10x or more. I'm talking about this real-world scenario:
https://nuejs.org/blog/tailwind-vs-semantic-css/img/markup-b...
The "separate markup and styling" paradigm with shared css will lead to refactors having a large blast radius. When you've organised your team by mapping people -> features(components), this makes this paradigm completely unviable. Tailwind wins there, that's it.
For web apps, having separate markup and styling makes absolutely zero sense, while it does for textual, reading content like blogs.
So, web apps like to use tailwind/bootstrap.
Reaaaaalllly large web apps (e.g GitHub) swing the pendulum all the way around, and make their own little bootstrap which adheres to their design language.
There are plenty of modern frameworks nowadays that use single file components (pretty much every popular framework except react?) and often you even choose to scope the css to one component only, if you want.
Also, tailwind gives you a pragmatic, limited selection of sizes, colors, etc, which is a very good basis for designing. (You shouldn’t be using a new px value for everything, they should come from a small custom-chosen set)
But in real life, in the 80% case, a true software "team" doesn't exist. Instead, you have a bunch of individuals working on a product, who won't be ready to fix issues in another person's feature.
It is very common to map features/components to people when managing software teams, in the obvious case with react,etc, and even in Ruby on rails type stuff to a certain extent.
From a political point of view, aaas much as possible, you want changes to page A, to only involve pageToPerson.get(A).
So, the first thing companies want to avoid is people stepping over each other. Again, healthy collaboration does not exist in the 80% case.
It's the same pattern with other things that are questionable from a technical point of view.
- using docker, microservices, etc where not required, so no cross-team, common dependencies, or even common database in the extreme case. - making each part of your codebase its own npm package
N.B
My definition of "exist" is "present and dependable upon to such an extent you will risk your job on it".
Of course, people without the problems use the solution because $bigtech does it.
That is a separate problem :)
I've read the article and can't figure out if this is comparing the CSS framework Semantic UI to Tailwind?
If we're comparing CSS to a CSS framework, it seems fairly obvious to me that it's going to be bigger in size, and slower to load, and that the amount of code required to be generic (rather than totally specific) would be larger too.
Btw Bootstrapt is still there
EDIT: Semantic css is also bandwidth friendly for mobile connection.
I agree with the conclusion but that first example is so grotesque that it made me discard the whole article
One other advantage of tailwind is that it makes it easier to work collaboratively with people of different css level and it requires less review, less possible side effect. But it's clear to me that clean semantic well developed css is way better than tailwind.
It's the actual HTML code in the sites that are compared
or the person who developed that with tailwind just love having dozens of div for a simple header > nav > a
that person is someone from the Tailwind team. I would expect them to follow their own bst practises on the official template they sell online.
Calling tailwind tightly coupled Vs semantic loosely coupled is also a bit of strange. I would call it composition over inheritance.
First of all you are not comparing tailwind css vs semantic css. You are comparing tailwind ui implementation of a page vs your implementation.
You are comparing blog projects with very small amount of components. It's misleading, as good naming is only easy on small amount of components. This same goes for styling based on html element selectors vs classes.
The button example is just shameful. Half of the classes there don't make sense, and you are not showing your css at all.
This is how button would look https://play.tailwindcss.com/FHIFtYbrV8 .
The speed is also misleading. Most of the comparison there is next vs nuejs. In fact, you could probably do that without any js and have even better results.
The thing is that neujs itself sounds pretty good as I really believe progressive enhancement being the way, but comparisons on your site are also misleading.
Like this thing https://nuejs.org/compare/component.html that only says that it doesn't implement this same features further down ( it's not even mentioned on main page )
This is not great. Especially, that you could probably use examples that wouldn't be misleading as react is really bad when it comes to devex and amount of code.
Presumably the author must have already written the code in order to draw the conclusions in the article.
Not releasing this code, makes me question (1) the conclusions, and (2) whether the HN comments will prompt him to update the code, but not the article or conclusions.
The author mentions "not wanting the overhead of an open source project" to delay publication, which doesn't make a whole lot of sense to me. Code in support of a blog post isn't something that would require ongoing effort, or even documentation.
This falls down to the old "it depends" slogan. Tailwind is a great way to scale a your css with a larger codebase / team while semantic CSS will give you a smaller tighter stylesheet and cleaner html for that single handcoded landingpage or blog.
Also the download cost of tailwind falls as the size of the app grows and each utilityclass is used in more contexts.
This article does a good —if exaggerated— job of explaining the disconnect between markup and meaning, as well as the sheer volume of cruft. But tailwind does present well. Its plethora of sane defaults look… nice.
What I find myself doing more often than not is using tailwind classes in my handwritten SASS, using the @apply utility. I get tailwind's reset and typography but my markup isn't full of jank. PostCSS, PurgeCSS (or whatever's actually going on inside Vite) keeps everything small.
It's also good practice to try flying solo. A reset stylesheet and style everything, yourself. Few projects require that much CSS and it's a good excuse to try out some new ways of doing things. Because there are now several ways to accomplish major layout tasks, you can be quite expressive with CSS.
---
I've seen a few comments saying that maintenance is harder without utility CSS scattered throughout your markup. I think if you're struggling to maintain a semantic site, you're doing something wrong.
Even if you're using a complex SASS+Purge build chain, a sourcemap tells you exactly what's going on with a quick right-click-inspect. This method of development gives you a lot more space to split and comment on your source than you might want to in the middle of markup. Good component names help too.
Maybe it's just a mess that no sane person want to learn. Semantic html is really about creating blog posts, there's not much elements provided beyond that. If you want to create an e-commerce website for example, there is no "card", "carousel" or "popover" element. You have to make them yourself based on existing elements which then makes the css styling confusing.
But then the article suggests that every claimed benefit of Tailwind actually doesn't exist, and that Tailwind is in fact worse on all fronts. He seems genuinely to believe that there is no merit in Tailwind's huge success at all, that it must entirely be explained as arbitrary flocking behaviour. What idiots we must be, going to all this effort to write bulkier CSS, more slowly, with less flexibility! I have to guess that the author has not given it a fair trial (i.e. using it on a real project in earnest) before coming to his conclusions, nor had any good conversations with old-timer CSS experts who love CSS and love Tailwind. He's jumped to the conclusion it's a thing noobs like because they find CSS too hard.
I made a similar assumption at first, that Tailwind was just another horrible "CSS-in-JS" type thing, which I always felt were for front-end newbies who were so reluctant to just learn how CSS works that they'd be seduced by shiny packaging and use horrible JS contortions to 'shield' themselves from inheritence and cascading (which are actually useful, powerful aspects of CSS when understood).
But then I started to notice its hype cycle felt different, that even a few old school traditionalists seemed hooked on it, and I tried it out in earnest on a project. And I soon realised it's a very rare thing: a real innovation in front end, the kind that comes along every 5-10 years, up there with the component paradigm. It's not another CSS-in-JS thing at all. With Tailwind, you are writing CSS, in a very real sense. And thus your success with it is highly correlated by how well you can write CSS by hand – it is not an alternative to learning CSS; it is an extra, more advanced thing you should learn after learning how to do handwritten CSS well. What it brings is a beautifully designed build layer that justifies its own existence better than any other build layer I have seen 20 years of front end development. It gives you a massive net reduction in 'indirection', letting you style things right where they are. And it reimagines style 'reusability' in a way that (to be as succinct as I can) prevents your styles ossifying your DOM structure too early during the creative styling process. These are some of the reasons why Tailwind has blazed through the industry the way it has. Not because we don't understand CSS and want to avoid it, but because we want to write more of it, quicker and more flexibily.
I care very much about the structure of the CSS files I generate with Tailwind. I have been writing CSS for over 20 years. And I care very much about reusable modules – before good composable component systems came along, CSS classes were the best solution for this, but not any more imo. And I can certainly move faster with Tailwind in most situations.
Sorry for the negativity btw, I don't want to discourage people posting about their experiences with a tool. I just doubt you have really given Tailwind a fair trial. You're obviously a seasoned front ender who understands CSS deeply, but I don't believe you have understood Tailwind properly before coming to your conclusions about it.
---- You can move faster with Tailwind. But only when:
1. You are comparing Tailwind with your earlier, badly structured CSS or you are completely new to CSS development.
2. You don’t care about building reusable modules for later use. That is: you are not naming things that repeat. ----
> when you don't care about 'badly structured' CSS
Is a completely different claim than this:
> You are comparing Tailwind with your earlier, badly structured CSS
I'm sure we both care about CSS structure. My claim talks about developers' past experiences. If you enjoy semantic CSS, you probably won't switch to (unsemantic) Tailwind. And vice versa: if you never mastered semantic CSS (even hated it), you are more eager to switch and find the new way more enjoyable.
The more general issue is your article doesn't demonstrate that you have grasped the purported benefits of Tailwind. You just deny they exist. It would be fine if you showed that you understand the purported benefits before explaining why you still prefer handwritten CSS on balance.
In particular, I can tell you don't understand that no longer using CSS classnames for reusability (and instead extracting components for reusability) is exactly what Tailwind users love about it, and no, not because they "haven't mastered semantic CSS". I know from experience that is initially unintuitive to a long-time CSS author, because I had a kind of Stockholm Syndrome for naming everything semantically and keeping my DOM super clean and avoiding extra divs. With Tailwind, you generally only name things when you extract them to components, and it's hard to explain but I after a while I came to realise this is just the perfect moment to name something, leading to better conceptual separation of semantics and styling logic, and much less proliferation of 'things' and ossification of the DOM.
I am not arguing Tailwind should be used for everything. I still use hand-written CSS for many things. And it is fine for someone to not like Tailwind after understanding it. I am just saying that you haven't understood it yet. Perhaps you wouldn't like it anyway, but imo it's worth really giving it a try.
By the way, giving it a try means on a real project. If I have a one-off, never-to-be-edited-again page design such as the example in your article, then I would probably use handwritten CSS with semantic classnames, as the exercise would be to make the cleanest, most elegant and fast-loading bundle of HTML and CSS possible. But in more realistic component-based projects that are expected to change over time, Tailwind can be an excellent trade-off, sacrificing a tiny (really, tiny) amount of elegance and bandwidth for a lot of engineering flexibility.
Then they are so happy, that they understand it, they write blog article about it, other read it and think "it must be a good thing", then more and more use it.
And if more people use it, other think "it must be a good thing, cause many use it". Self runner.
The inequality of exposure of projects is massive and hard to overstate. Even if a bad project is exposed to 10 million developers over a few months, it will become a standard.
On the other hand, a project which only got 100K views over 10 years will never be adopted no matter how good it is. People can talk about how good it is, but that doesn't matter at all; the only thing that matters are the algorithms used by the big tech platforms and they have all been corrupted.
> There is no need to write a component for every situation because you can use external CSS
Unless you're writing the most basic document-type html, this is awful advice. Even the tiniest widgets can be subject to a structural change. I can only think of the line: "Implement Select, 2000 issues" [1] Admittedly, CSS can correct the structure in some cases. Grid gives us more control over the arrangement, :before and :after can add elements... but in the end, you're just praying that you won't run into a requirement for which you need to update 200 html instances. The realisation that styling is inherently coupled to the structure, and that you will rarely have two components with both identical structure and style, is why css is generally scoped to each component.
For anything that is scoped globally, use a component.
[1]: I forgot where I picked this up, could've been a video for Radix-UI.
What is the purpose of writing <nav class=".nav"> if you can just write <nav>? There seems to be only one navigation element on that page, and the class isn't adding anything that is not already expressed by the tag name.
1) You can absolutely not be sure you would not later want to use something as generic as nav in a different context. Being heavy handed with element selectors is a solid foot gun for scaling css.
2) Different selectors produce different specificity in css. To not write selectors that will later be overwritten, opting for class selectors is often a good idea.
Pure semantic CSS only works if you have a strict html document structure - where eg every h1 is an article heading. Any hint of "components" (like an image gallery, navigation menu) become way too fragile with pure CSS because you will be trapped when trying to get the correct specificity.
I agree that class names that match element names is a bit of a smell - but might be forgiven in this particular case, if html nav elements are used in different contexts.
But then you see a fork in the ecosystem, where some continue working on the semantical side, and some just want "Bootstrap, but with X and Y, so I can finish my project". I think the article argues against continuing down this latter branch.
With CSS preprocessors, variables, media queries, calc(), transitions, animations, layers, no IE, etc. we're in a very different position today. Everyone's now used to using JS bundlers, i.e. a build step. Webpack was released in 2014. Accepting that source and distribution are separate makes it much easier to motivate going for a preprocessor and being able to write nice CSS.
Complaining about a technology (tailwind) while not using its surrounding ecosystem is not really a good way to build things.
How to do it:
- encapsulte/extract react/jsx components - for readability, so you do not have a wall off endless meaningless divs. Examples: Article, ArticleHeader, ArticleContent, ArticleSummary, etc...
- use cva https://github.com/joe-bell/cva to style generic ui components (like buttons) to archive same grouping as in semantic css
- use https://github.com/dcastil/tailwind-merge to merge/overwrite tailwind classes (like in css)
- result: separated react/jsx components with only a few lines of tailwind classes - are very readable and easily maintainable
Like your past posts about Nuejs, you discuss on this post with the same arrogance as if everybody is wrong, you are right. But then you always fail to convince anybody that your idea is good enough, like NueJS reactivity that is a copy of the (now ditched) svelte approach.
https://nuejs.org/blog/tailwind-vs-semantic-css/img/markup-b...
There are many developers who agree with this. Luckily not my lonely fight :)
Accessibility (attributes outside "style" and "class") are outside the topic (even though both sites give accessibility score in the same ballpark).
But you are wrong and your claims are misleading :)
> Please don't use HN primarily for promotion. It's ok to post your own stuff part of the time, but the primary use of the site should be for curiosity.
The best I can think of is to use descendant selectors but that increases the specificity and is hard to maintain imo.
Excited to see what the @scope feature draft can bring to this
On fe html/Templates+webpack (scss for variables and typescript) has taken me very far with a clean and fast build step and more importantly dependencies don't break every few months!
One downside though is remembering why a particular "css" after a while. But i am not a particularly fe/styling person so my css is simple. May tailwind is for the Uber fe/design crowd!
I can't scroll horizontally and only half the text is visible.
For an example of "documents", see eg:
https://edwardtufte.github.io/tufte-css/
Or
This is often necessary to be able to properly style things like select controls or create more complex UI/markup.
Tailwind is useful in this case, or with things like react and vue where the layout can be specified once in the component templates. If hand-writing the classes, the semantic model makes sense as it allows you to remain consistent in what styles get applied.
“Semantic CSS” is not a standard, like semantic HTML follows certain rules that are widely accepted. At the end of the day, everyone is writing their own “button” CSS class, and it is different on every website you look at. It doesn’t help whether you name something “.nav” or “.gallery” if you are the first person deciding the CSS rules for it. Your code might be “semantic” in your personal understanding of how things should be named, but your naming convention is useless to others who have to maintain your code and still need to look up what “.gallery” actually means. So, from my understanding this is part of the reason TailwindCSS exists: transparent markup and improved maintainability in the long term.
As stated above this article makes a couple of false/misleading claims. For example:
- With TailwindCSS “you are forced to wrap divs inside divs inside divs”. Not true. I'm writing TailwindCSS code for a couple of years now and can confidently say that this is NOT what Tailwind forces you to do. You don’t need extra HTML markup to make Tailwind work. Your HTML can be perfectly “semantic” without wasted divs.
- “… utility-first approach lacks the power of CSS selectors”: Not true. TailwindCSS comes with grouping and child selector classes, for example.
- It also states that Tailwind styles elements exclusively inline, which is also not correct. They encourage you to name things as well but warn you of premature abstracting in their docs (https://tailwindcss.com/docs/reusing-styles#extracting-class...).
———-
I also can't help but feel a little patronized by the tone of this article. Examples:
- “Mastering CSS requires practice”
- “But when you truly master CSS, there is no turning back” (and you don't need Tailwind)
- You only require Tailwind when you have a “bad CSS structure” or you don't care about your code (https://nuejs.org/blog/tailwind-vs-semantic-css/#but-i-move-...).
If I was new to coding, these statements would discourage me trying out new things. I don't know how you learned to code, but sticking to the fundamentals didn't get me anywhere. Yes, you need to learn them, but you also want to get things done (and figure out later how they work). Writing CSS is not an ancient art, it’s not pretty or a noble thing to do.