Brian Lovin
/
Hacker News
Daily Digest email

Get the top HN stories in your inbox every day.

earthnail

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.

tuyiown

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.

tipiirai

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

HelloNurse

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.

tuyiown

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.

aloisdg

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

tipiirai

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.

amjnsx

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

Longer than converting the entire website to Tailwind?

earthnail

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.

quickthrower2

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.

berkes

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.

egeozcan

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

earthnail

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

gherkinnn

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.

sbergot

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.

tipiirai

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

sbergot

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.

undefined

[deleted]

gherkinnn

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

Is kinda what I said.

pas

> Rules are scoped globally.

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

sbergot

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

bryanrasmussen

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

Timon3

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

flagrant_taco

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.

tipiirai

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.

pcthrowaway

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

tipiirai

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

twohaibei

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.

taink

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.

tipiirai

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!

kaba0

It tells exactly that it is a link.

sam_goody

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.

progx

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.

tipiirai

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.

Leimi

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.

tipiirai

Author here:

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

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

Leimi

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

tipiirai

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.

jonwinstanley

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?

tipiirai

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

d1sxeyes

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;
    }

romanovcode

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

foldr

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

progx

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.

willdr

@apply is an antipattern.

d1sxeyes

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.

jacknews

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.

jonwinstanley

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

cornedor

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.

k4runa

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

thom

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?

k4runa

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"

martypitt

I love that idea, and would definitely use it.

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

thom

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

progx

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

k4runa

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.

progx

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?

jonwinstanley

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

twism

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

tipiirai

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

k4runa

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.

amjnsx

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

AmazingTurtle

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.

tipiirai

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

AmazingTurtle

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.

komali2

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?

xoac

choo choo! all aboaard!

thinkxl

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

tipiirai

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.

thinkxl

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.

tipiirai

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

colourgarden

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.

ekzy

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.

NikxDa

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.

tipiirai

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.

willdr

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.

arzke

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

willdr

Did you continue reading?

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

Daily Digest email

Get the top HN stories in your inbox every day.