Hacker News new | past | comments | ask | show | jobs | submit login
Tailwind vs. Semantic CSS (nuejs.org)
124 points by tipiirai 10 months ago | hide | past | favorite | 205 comments



I just moved our website from semantic to tailwind after I didn’t understand the semantic bits anymore.

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.


Yep ! This is the huge problem with css and «meaningful» selectors, no matter how much you try to make things clear, nuances and details fades and you end up going back end forth between css code, inspector and HTML to figure out what's happening.

Utility based css selectors that do one and single thing are clear and explicit, removing most (but not all) that hassle.


Not all developers have experienced these problems. There are people who prefer the semantic approach. I'm definitely one.


I strongly doubt a reasonably designed "semantic" design can be half-forgotten after taking a break; the fine points of the implementation of sophisticated rules (e.g. why did we put the left border on the parent and the right border on the child) might suffer from obsolescence and lack of documentation, but the markup structure and the names should be obvious.

Forgetting what plethora of non-semantic classes should be used on what elements seems a far more likely problem.


Yes, there are conditions of people and project where you can avoid those.

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.


Not all team are homogeneous. go for the friction less solution.


Indeed. This is a matter of taste. I personally find semantic CSS much clearer. I move faster with it because I need less code to achieve the same thing, and the resulting site is leaner.


> but understanding it again after half a year of not editing the page took super long.

Longer than converting the entire website to Tailwind?


I wrote a new part of the website in tailwind to see what all the fuzz is about. Then I decided to rewrite the rest of it.

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.


Copy paste for styling isn't as bad as it sounds. Obviously with React you get components you can reuse. But manually chucking out mb-2, mx-2, my-3 to get it to look nice to the eye and having the same things repeat in different spots without a "meaning" or "semantics" can be quite nice. For small-medium size projects of course.


That quickly falls apart when I add an mb-2 to a Thingy, but fail to check that Thingy is also used inside SideThings (which we all forgot existed at all) wich already has whitespace (but somehow uses mt-2 because the developer preferred to declare intermediate whitespace at the top instead of the bottom). So when the communication dept calls me on friday, I add quickly add an #main div.nth-child(2).mb-2 { margin-bottom: 0 !important } to overrides.css and call it a day.

Point being: manually sprinkling mb-2s throughout your codebase is a recipe for disaster: an sure way to an unmaintainable frontend.


> #main div.nth-child(2).mb-2 { margin-bottom: 0 !important } to overrides.css and call it a day

And then 100 times doing the same later... We all know how it ends up looking because we all did it.


Until someone else edits your code and doesn't understand why the layout suddenly broke. nth-child(2) is very prone to this.


Or worse, it breaks at a completely different site you didn’t even touch, won’t notice and might also pass through manual testing (if that’s even a thing at your place), and end up in prod. Sure, there are AI-based testing tools that check for frontend changes, but let’s be honest, they are very rarely used, if at all.


Yes. My point was that to "fix" a scattering of .mb-2 going astray, we often pull out even worse solutions. Making the software as a whole worse.


Hacks like this exist in non-tailwind projects where the classes have lost all meaning and the CSS is in a messy state.

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.


This is one of the first Tailwind vs * articles that isn't bad. It makes a good case in the analysis.

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.


No this article is flawed. It fails to recognize the fact that css and html are always coupled in one direction or another. With tailwind the css is fixed and the html is designed around it. With semantic the html is first created and then you write your css around it.

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.


Author here. You are right: HTML and CSS are always coupled. The major difference is that Tailwind embraces tight coupling and semantic CSS embraces loose coupling. Please check the "Best practises" section:

https://nuejs.org/blog/tailwind-vs-semantic-css/#best-practi...


This section is what I am talking about. You compare two methods of writing components and then declare that the tailwind version is tightly coupled but the semantic version is loosely coupled. In programming lingo this means tailwind is bad and semantic is good.

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.


So `<div class="gallery">` is loose coupling, because the styling is not coupled directly into the element. You can completely switch the gallery design, by switching (or overriding, or modifying) the external stylesheet.


I have rarely seen this work in practice in 20 years of CSS, but maybe in CSS Zen Garden. Yes, you can freely change the CSS. But usually changes are triggered by the HTML, like some information is added. This then requires you to change the CSS and likely break all sorts of other uses, which is not a problem with Tailwind.


hmm maybe IDE plugins can solve this? (eg. add a 'hover to show css' or 'expand css if I press ctrl+alt')

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)


The only way to know is to go to the page and inspect it using devtools. No Ide will be able to infer which rule is going to apply to any given element.

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).


hmm it might be possible with enough tooling:

- read from webpack's output

- read from css-in-js: emotion / vanilla-extract / etc


Then you avoid the browser, by emulating half a browser.. which is still useless, because you have plenty dynamic state you can’t test this way.


The coupling is in the right direction: designing possibly very advanced CSS to get a desired appearance from a given good markup, instead of compromising markup to simplify CSS.


The issue of this direction is if you need to update the markup, you will need to update the css, which is hard and risky because of css global scoping.


New and updated CSS rules should be usually needed for new "themes", exactly the type of change that semantic markup is robust against (e.g. placing image captions in a sidebar rather than below the respective images), and for backwards compatible extensions of the original design that add support for something new that will only be used in new markup in new pages (e.g. allowing small images inside paragraphs, meant to be displayed inline, in addition to large ones between paragraphs).

What kind of fragile markup and problematic CSS global rules are you worrying about?


> 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.


The author uses css targets like *body > header*. Search and replace won't be enough to tell you which components are affected by a given rule.


A lot of applications have a long lifetime. They are not shipped only once. If I want to add a new feature, say allow users to perform a new operation in an existing screen, I will have to update a few components for this.

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.


Ordinary changes fall within the boundaries of the existing design system: for example, "a new operation in an existing screen" probably means adding instances of a few existing components (button, paragraph, title, section, pop-up submenu, etc.), with no impact on styles, not "updating" components.


I can't, for the sake of me, understand how some frontend devs still complain about CSS' global scope when style encapsulation exists and is widely adopted in a form or the other, either native (Shadow DOM) or emulated.

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.


So what does it mean? You want the structure of your HTML to say the browser, that here is anything? Does your designer just give you a design with a huge blank rectangle in the middle? When does it ever make sense?

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?


It is still coupled because the css will need to know the html structure in order to work. If you update the html, you probably need to update the css.


It is _loosely_ coupled. I can switch the design completely without touching HTML.

If I modify the HTML structure, the component specification changes and CSS obviously needs to be updated.


This is simply not true of any real-world application.


You will be more effective if you deploy your words more carefully.

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.


Both of them need to conform to the same allowed patterns of markup. I think we lack a kind of markup schema or type system that everything can be validated against, where we can verify that our design takes every state and permutation into account, and that both styling and content implements their part of it correctly.


tailwind doesn't need to know anything about your components.


They need to agree, and in the case of Tailwind, it has defined the rules and the markup has to conform. It’s one way to solve the problem, but not the only one.


You can see this in practise here:

https://nuejs.org/@spotlight/

vs here:

https://nuejs.org/@base/

(the gallery component)


> "just don't make mistakes". Mistakes will be made.

Is kinda what I said.


> Rules are scoped globally.

Isn't "CSS scoped to components" nowadays a basic feature of frameworks?


The author is advocating for rules like "body > header" in separate css files.


Indeed! I'm a freak using CSS as intended :)


The problem is that CSS’s intended usage is broken, and never lived up to expectations.


I think CSS does a good job at separating structure from presentation. Or what do you think was the original intention? Do you think Tailwind fixed the core issue with CSS?


I really recommend you reading the blog post by tailwind’s creator himself: https://adamwathan.me/css-utility-classes-and-separation-of-...

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.


Fully aware of this post. It's well written and I can see why people follow his stance. But I don't share Adam's history and experiences with semantic CSS. I genuinely want to separate structure from styling and not end up to this situation:

https://nuejs.org/blog/tailwind-vs-semantic-css/img/markup-b...


>Similarly, I haven't seen a pattern where good CSS authors do not like Tailwind.

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.


> 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.

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.


There are a few important limitations, Tailwind can't fully support logical properties for example because `mb-8` could mean margin bottom or block. Any use case that requires styling one element based on another gets a bit hairy too.

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.


that is also a thing, when I look at tailwind, having spent my time learning CSS why would I want to learn another syntax to help me do what I can already do, and probably do better.


Thank you for the correction!


Author here. I implemented the commercial Tailwind "Spotlight" template with Semantic CSS and compared the differences in weight, amount of HTML and CSS, rendering speed, and best practices. I was surprised to find _that_ much overhead in Tailwind. Curious to hear your thoughts.


Thanks for the thought-provoking comparison! I have a few questions:

- 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?

[1]: https://tailwindcss.com/docs/optimizing-for-production


1. Only tested with a handful of browsers and devices

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


4. I just checked. Its 12kB vs 4kB.

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.


True. There is some unnecessary next.js clutter in there, which I'll address on my next post. Removing that would make the HTML leaner. Likewise, removing the inline CSS from the semantic version would make it leaner.


Hello, well done on your article!

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.


Thanks! Semantic CSS describes an elements meaning clearly: like <form> , <table>, or <div class="gallery">. Tailwind is non-semantic because you cannot say what the element does, like for example:

<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!


It tells exactly that it is a link.


Tailwind templates are extremely verbose, because they feel it is worth to spend their time optimizing features and adding designs to trying to make their code smaller.

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.


Did or can you publish a small snippet of how Nue-CSS would be implemented in or with Nue?

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.


Glad to hear this! I'm updating the create-nue project next. It will clarify a lot of things. CSS pre-processor is the next project to be published. I don't want to give ETA, because I'll likely disappoint.


The article is interesting but really feels unfair sometimes, it doesn't help:

- 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.


Author here:

- How is it not responsive? I can easily fix.

- The Tailwind example is the official template made by the Tailwind developers themselves.


- compare the tailwind example and yours, they don't have the same behavior. The tailwind one has a specific mobile menu for example. And that is just one example.

- 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 :)


Tailwind example indeed does more, but only slightly. Would increase the size of CSS by 1-3%. The semantic version has clearly enough to prove the point the article attempts to make: significantly less code is needed and the resulting site is leaner & faster.


If you really want to compare code snippet sizes, you must offer the same functionality. Otherwise, the comparison is meaningless.

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.


I'm sure most developers can see the bigger picture with a 95-99% a implementation.


I know what you are talking about. Projects tend to slow down at the end of it. But responsiveness is not one of those things, at least on this project. My honest guess is 3%, the menu being the biggest piece. It can also be lazily loaded so that it doesn't increase the amount of primary CSS. Just like the dark mode was implemented.

Having said that, I promise 100% feature parity on my next article. Thanks for the heads up!


You are underestimating the effort/size spent on the last 5% of a project.


Features that look easy to implement and are not a big deal can turn out to be massive problems and require a large amount of code to actually pull of. I would, in some cases, count responsiveness to be one of those things. Stuff like that may seem like 5% of the functionality but actually makes up a large chunk of the code. This is why you must always have feature parity if you really want to compare code size.


Is comparing the markup of an html page with all the style in html attributes, to an html page which imports style from a CSS file (which is not displayed) a fair comparison?


The comparison is exactly that because It's the topic of the article.


Tailwind excels when it’s used on reusable components. Anyone handcoding Tailwind for a full page will start to hate it quickly.

But you can have the best of both worlds with apply:

    .card {
      @apply p-2 rounded shadow text-gray-700;
    }


Exactly. This is a non-issue if you use @apply (as people should).


But if you make extensive use of @apply then you're just writing normal CSS with an unnecessary preprocessing step.


That is the point. As long as you use tailwind as it is, it works good and you can have fast results.

But with reusable components or unified design, you enter the @apply hell. At this point i could not see benefits of tailwind.


@apply is an antipattern.


Hm. @apply solves a problem. Basically you’re better off writing with the utility classes if you can extract your component out to… well, a component. If for whatever reason you can’t do that, @apply helps you avoid repeating yourself, which seems to be a reasonably good alternative to what’s in TFA.

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.


Exactly, I thought this was in fact the whole premise of tailwind. Scattering it directly through the html is just for testing or special cases.


Exactly. Using tailwind in your views in a backend framework such as Rails/Laravel means you can build pages extremely quickly.


This is not a good comparison. The Neu implementation lacks a lot of styling and features from the Tailwind CSS implementation. It's not responsive, missing styles for a bunch of components, using much simpler styles for other components.


I find Tailwind really good for prototyping designs and iterating quickly, and as the design becomes more crystallised then I moved to semantic css and start to clean up the complexity. Once I've figure out that patterns and components required...


I’ve found myself wanting a Tailwind compiler that makes it easier to work like this. Iterate fast with maximum verbosity, but later extract common patterns to classes when things are more stable. Anyone aware of any tooling like this?


That would be nice, like a Webstorm / editor feature that detects when you re-use code and recommends abstracting it. I would love that to be able to detect that like "Hey these buttons all share these features, let's refactor it for you"


I love that idea, and would definitely use it.

In fact, if there's enough interest, I'd probably build it too...


Go for it! At the very least there are some interesting algorithmic problems in there. I was leaning towards calling it "Leaf Blower", because it's a form of wind that tidies things up. Take it or leave it. :P


But who really work like this? The most people and company would not do this extra work and use tailwind for finished products too.


I figure most startups would build an MVP in two weeks and then rebuilt it later or move onto the next version and clean it up a bit in each version that follows.


Never change a running system. If you create a working project, you will not change it, especially a startup.

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?


Agreed, even if you do a rewrite - you'd still want to use tailwind so you can re-use some of the elements you already built


To me you are adding complexity. Back to finding which styles are cascading over another.

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


Makes perfect sense. Prototypes are all about doing something very fast and all hacks allowed. Cleanup later.


Yeah, cause I end up with like 5 different buttons designs using Tailwind, and they all have different classes but when I clean up the code later I reduce it down to one button design that fits the final theme.


Thanks for confirming my suspicions regarding Tailwind. Their marketing loves talking about how small the CSS file size is vs semantic, but that code still has to go somewhere - and it's a bait and switch of "smaller css" with the cost of an inflated html file

* The irony being in this article is that the CSS file is also larger


tbf the examples brought up in the blog post omitted semantic attributes such as lang and the whole dom structure (all the divs!) and the other data- attributes just to make it look even worse. OP is severely opinionated.


Sorry, which data- attributes? And what do you mean about the semantic DOM structure?


The Tailwind markup vs Semantic markup example just straight sucks.

- 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.


Bear in mind that the Tailwind example is the official template made by the Tailwind developers themselves. It is the prime example of Tailwind and it's benefits and best practises. (Should be at least)


Why do you think this example is supposed to be the pinnacle of what the most performance and optimized tailwind can be?


I just took the first one from the official Tailwind templates. What would you suggest?


For Tailwind all I ever do is copy big blobs of tailwind "styling" text (classnames) into my components from someone else's "tailwind components." Tailwind themselves even offer a component library.

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?


choo choo! all aboaard!


The article focus on the semantics of CSS classes present in the HTML rendered in the browser, but the example the author presents doesn't have a good semantic HTML.

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).


The article focuses on Tailwind CSS vs Semantic CSS. HTML attributes, beside "class" and "style" are outside the scope. Both approaches are equally good: there is nothing extra that Tailwind provides for making websites more accessible.


It's an unfair comparison because Tailwind as a library is composed of tooling, documentation, design patterns, good practices and obviously CSS. The same is for other libraries, e.g., Bootstrap.

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.


Accessibility is important. No question about it. But this article is not about the whole UI/UX stack. It focuses on benefits on what semantic CSS offers. aka "naming things" vs "not naming things".


From this

> 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.


Not mentioned in the article:

- 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.


In my experience, it isn't black or white. I've used tailwind along with components, e.g.

.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.


Arguably, this is the worst of both worlds. In a React world, you'd have a <Button variant="primary"> component for this.

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.


Certainly not black and white. The article merely states that you need less HTML/CSS code to do the same thing and the resulting site is leaner and faster.


This is not the best of both worlds. This is an antipattern and discouraged by the Tailwind core team and the Tailwind community at large.


Actually, the docs says it's perfectly fine to use @apply for highly reusable components like buttons and form controls: https://tailwindcss.com/docs/reusing-styles#avoiding-prematu...


Did you continue reading?

> even then only if you’re not using a framework like React where a component would be a better choice.


Yes I did. You seem to assume everyone is using a "framework like React". I am not.


I think we all can agree that this article is not based on a proper comparison between Tailwind CSS and what the author calls “semantic CSS”. I understand, from authors’ comments here, that it does not aim to be a perfect copy. But arguing that it is 1-3% off, is just BS.

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.


I'm arguing that Tailwind requires _significantly_ more markup to produce the same look and feel. There is no way anyone could implement the semantic version with less code using TW.

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 intention is clear, but you are exaggerating your argument by comparing a plane with a car. The Tailwind example is a much more feature complete product, then your „lightweight“ copy. Also, as I said it might be written with another intention then being super small. You use body > header > nav > a. You could write an example in Tailwind that uses the same markup structure, which then would be a valid starting point for an article like this, despite I assume you could not come up with the same conclusions.


It's quite simple in my view.

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.


Using semantic css doesn't always mean your markup and styling are separate.

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.


Tailwind is not worse in this case either, and one might argue the “cascading” part of CSS is useless on such a local scope.

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)


Again. This is purely a matter of taste. I personally prefer the semantic approach.


Of course. From each person's POV, it is a matter of taste.

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".


Addition:

Of course, people without the problems use the solution because $bigtech does it.

That is a separate problem :)


Is "Semantic CSS" just... CSS? There's no such thing as "Semantic CSS". There is Semantic HTML. I suppose there is CSS for Semantic HTML.

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.


it's just CSS yes, with the focus on naming your classes depending on the content you style instead of their looks. "article-excerpt" instead of "bg-white padding-2 shadow rounded".


I never understood why use tailwind when I can have semantic css without any extra unusable content.

Btw Bootstrapt is still there

EDIT: Semantic css is also bandwidth friendly for mobile connection.


What "extra unusable content" are you talking about? With the recommended setup you only ship the CSS that your website uses.


That example with a million div for the nav with tailwind is so ridiculous.

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.


> that first example is so grotesque

It's the actual HTML code in the sites that are compared


I'm pretty sure it's not realistic, there's nothing that justify so many div because of tailwind

or the person who developed that with tailwind just love having dozens of div for a simple header > nav > a


> the person who developed that

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.


It's an official Tailwind example…


I don't understand why tailwind would force you to write more divs then CSS. How do selectors solve the problem of how many elements you need to use to achieve the same design?


Extra divs are wrappers that help you style the parent element to add flex layouts for example. Or why do you think the official Tailwind template has so many nested divs with utility classes?


Sure but how would normal css make it so you don't have to use those?


Maybe check out the semantic CSS example using developer console:

https://nuejs.org/@spotlight/


This article is a bit bs. the first example of how much code it requires is misleading. you can accomplish this same thing with exactly this same amount of html nodes. You just gonna have more classes.

Calling tailwind tightly coupled Vs semantic loosely coupled is also a bit of strange. I would call it composition over inheritance.


I sugest you look for the bigger picture on the article and the baggage you get when using Tailwind.


Dude, I am sorry, but almost every example has something wrong.

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.


> Where is the source code? > I’m working on it!

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.


Fair enough. Gimme few days, please!


Writing and maintaining clear concise CSS for a large application requires serious effort. It usually fails fast as the design grows and changes and a larger team tries to work on the codebase.

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.


I hate tailwind for top level layout.

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.


Author is using tailwind wrong

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


I don't agree with this post at all. It seems like it's been written by someone who never actually used Tailwind CSS: I had the same doubts before using it.


Author here. Not that it matters, but I have certainly used Tailwind. I even reverse engineered the Spotlight code to semantic CSS. Fully aware of the ins and outs of the TW ecosystem.


The post say not that tailwind is bad, it shows only that using tailwind could end with huge code base and higher load times.


I think tailwind is like a drug for css developers which gives them power but takes something away. They've successfully convinced people to think the tailwind way and not beyond to the extent that we use absurd classes to keep overselves in html. It's cleaver but not simple. I still miss the days when I was able to quickly turn a design into html but now I struggle and search for my tailwind drug.


Oh and by "mastering css" we just mean to learn the basic box model (width, margin, padding), units (em, rem, px, %, vh, dvh, lvh, etc.), the normal flow of each HTML element, the display basics (inline, block, contents, none, initial, revert, inherit, unset, etc.), positioning (relative, static, absolute, etc.). Flexbox and Grid deserve their own chapters. Ah there's also those capabilities for elements to float and overflow... Also, those are just the basics for positioning we didn't cover things like colors and selectors. Feel free to explore, deep dive and stay up-to date (it's moving fast) so that you can become a real css master!

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.


Yes! I strongly recommend to learn the fundamentals of web development and the basics of HTML, CSS, and JavaScript. At least if you are pursuing a career on web development. Learning CSS makes you a better Tailwind developer too.


HTML is ok, you don't have that many elements anyway. JS is fine too, learning the basics of OOP and any imperative language compounds. But css? oh boy. It feels like a Frankenstein monster where each improvement comes with its new set of ad-hoc features. `grid-template-rows: masonry;` wtf is even that?!


I agree. It's a super weird "language". There are flaws of course, like the grid syntax is a bit wild. But love the very basics: the cascade, the selectors, and the ability to separate styling from the structure.


This article makes some bold, false claims. What is true is that Tailwind will generally result in a bigger stylesheet over the wire than if you hand-write CSS. That's a well understood trade-off, made in exchange for several benefits.

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.


Author here. Please specify a falsy claim on the article and we can talk about it.


You claim that you can only move faster with Tailwind when one of these is true: when you don't care about 'badly structured' CSS, or when you are completely new to CSS, or when you don't care about reusable modules.

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.


Copy/pasting directly from the article, because you have reworded my claims. Here's what I mean exactly:

---- 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. ----


Sorry, I can't see any meaningful difference in my rewording. Could you clarify?


Saying this:

> 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.


OK, I'm not sure how that affects my point. The point is, I'm living proof you are wrong on this particular claim. Tailwind makes me faster, and it's not for any of the reasons you said could be the only reasons.

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.


Most of this Article focuses on the argument that you can use CSS classes for code deduplication.

> 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.


This perfectly summarizes what I've been saying. Popular tools are not the most effective ones. The people who are leading our current tech monoculture are not qualified to be making such decisions and imposing them on such large numbers of people. The artificiality of our current tech culture is obvious. It doesn't feel organic, not free market.


Presumably tools only get popular if people find them useful though? As in, if no-one saw value in them then no-one would use them?


People normally use not the best things, they use things they understand.

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.


I think that doesn't adequately explain what's going on. A lot of tools that are popular are horrible to use. They just got a lot of exposure and that was enough to gain adoption.

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.


hmm maybe it's more related to marketing and who got the vc money first?


ditto this most tailwind projects have very wiiidddeee classnames in one line... which means I can't have split tabs on my vscode


> The semantic version is smaller because it utilizes high-level, semantic components like .nav

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.


Scalability and specificity.

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.


There are exactly four <nav> elements on the front page. The ".nav" class is given for the master navigation elements in the page header and footer. class="master nav" and class="sub nav" respectively. Maybe not the best, but something I am personally accustomed to. Semantic CSS allows you to create a custom naming system.


> writing <nav class="nav"> if you can just write <nav>

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.


The tooling around CSS has evolved quite a bit since we started the "one-rule-one-class" approach to styling (Bootstrap released in 2011.) When the best web frameworks can deal with attributes, but not with CSS rules, it makes sense that's where we go.

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.


This is a terribly done comparison and as you are the creator of the alternative framework it smells like marketing and intentionally targeting the HN audience. Your example isn't web semantic, isn't accessible, doesn't work in mobile which is from where the biggest audience connect from but you still have the courage to say that "the semantic version is 8 × smaller, renders faster, and is easier to modify and extend."

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.


I have zero regrets/concerns claiming that the semantic version is 8 × smaller, renders faster, and is easier to modify and extend. Tailwind markup is clearly more bloated than semantic one. See:

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).


> I have zero regrets/concerns claiming that the semantic version is 8 × smaller, renders faster, and is easier to modify and extend.

But you are wrong and your claims are misleading :)


I'm actually quite relieved. I was expecting much more hate & resistance from the Tailwind community. Showing this to people who prefer the semantic approach obviously gives a whole different reaction.


Do you see what I'm saying? You expected hate. You wrote it with the expectation of causing a reaction so you framed it in a way that could cause reactions to promote your framework as all your previous (also polemic) posts. This is against HN guidelines.

> 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.


I mean that Tailwind cricitcisim gets heated very easily


I’m still looking for a lightweight solution (no react etc) that lets me write normal CSS but scoping it to an arbitrary part of my HTML tree. Most of the time I’ll be working in a partial where I know what generic names such as .container refers to And I don’t want to bother with coming up with unique names.

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


I am currently undoing nextjs out of a side project (where I spent 2 months learning react/next as it seemed like everyone knew something I didn't). I think I got pretty good at it and frankly the only useful thing I get about is it were - I think you referred to as - "isolated css". Frankly I found the whole shebang way too complicated and bloated and just was not worth being stuck to Node as a backend (I am fairly comfortable (dare I say formidable) with golang and don't see a need - for me - to switch.

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!


Me too. Unfortunately the browser support for @scope is very low atm. Cascade layers are one way to decrease specifity and are well supported [1].

[1]: https://caniuse.com/css-cascade-layers


This probably isn't related to semantic CSS, but the "Semantic version" isn't working properly. If I open "https://nuejs.org/@spotlight/" and then click on an article, then try to return to the previous page, it will change the URL but not the page itself. This happens on both Firefox and Chrome.


I'm a big fan of Semantic UI, but I thought it was dead? There's barely been any activity on the repo in over 5 years (https://github.com/Semantic-Org/Semantic-UI/graphs/contribut...).


What if you could use selectors, media queries, and even shorthand in inline styles?

I created a library that provides this developer experience, but it's lonely because no one uses it.

https://artxe2.github.io/css-lube/


Tailwind is not pretty, perfect or the best. It's a pragmatic solution that works well for a lot of people.


Is this page ( https://nuejs.org/docs/nuejs/server-components.html and others) broken for anyone else?

I can't scroll horizontally and only half the text is visible.


So given the popularity of tailwind I can conclude with confidence, that semantic movement has failed. HTML does not need semantics (because every big site is absolutely filled with divs) and CSS does not need semantics either. Spending time thinking about semantics is wasting time.


CSS and HTML is great for documents - not so great for applications. Most sites end up implementing their own navigation UI/UX at a minimum (an application) - many end up as more applications.

For an example of "documents", see eg:

https://edwardtufte.github.io/tufte-css/

Or

https://alistapart.com/article/building-books-with-css3/


It will depend on what templating framework the websites are using. Server-side rendering frameworks can allow users to write semantic components -- e.g. `<menu-button>` -- that get expanded to HTML markup, which will often be realized as many divs.

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.


The article is not about "thinking semantics". It's about making leaner and faster websites, and writing less code to achieve the same thing.


From the examples the tailwindcss one works on android Firefox whereas the semantic one doesn't..


Author here: I'm sure there are a lot of corner cases since I did not test with all the browsers. My main focus was on writing, and explaining the differences between the two approaches. When/if Nue provides official templates they will obviously be fully tested. I expect the size of CSS grow by 1-5%. Certainly not 7x.


What about a mix of them? Which is actually something Tailwind expects you to do at some point, AFAICT: https://tailwindcss.com/docs/reusing-styles


Yep, once you have a few re-usable components, building with tailwind is incredibly fast


Seems legit. I personally use a mix of semantic class names and utility class names. But without Tailwind.


This article makes a couple of false/misleading claims. But let me write about what it presents as a better (old) practice compared to TailwindCSS first:

“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.


We have not had a single regression of styling since moving to Tailwind, not one.


The Nue server components link leads to a 404.


Fixed. Thanks!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: