Hacker Newsnew | past | comments | ask | show | jobs | submit | emmacharp's commentslogin

Some real issues with Tailwind I don't see mentioned as often is they should revolve around the lack of some modern and efficient CSS features and techniques and the inefficient, outdated habits it tends to promote as a result.

Over-reliance on media queries and the absence of the clamp() function are just two obvious examples among many others.

Performance and browser devtool usability also suffer under Tailwind (compared to good CSS). Since I wouldn't want to waste time on a “dead” thread, I'll stop here. But if anyone is curious and wants me to elaborate, it'll be a pleasure to do so.


I am curious to hear more, particularly around performance. I also don’t quite grok the bit about “browser dev tool usability”.

There’s no reason you can’t add utility classes for clamp() in tailwind, and while I haven’t tried it, you should even be able to do things like `class=“w-[clamp(200px,40%,400px)]”` in Tailwind. A quick look at the docs and there’s an example of inlining calc() this way, so I don’t think clamp() would be any different.

Maybe Tailwind could add a clamp utility in the future, eg. `class=“clamp:w-2:w-full:w-16”` to evaluate to `clamp(8px, 100%, 64px)`, seems like that could be useful for avoiding the “over-reliance on media queries”. Side note: I’ll admit I haven’t looked much into the performance issues or inefficiencies of media queries, I’d love to see some writing on that.


Hello! Sorry for the delay, I thought the thread was indeed dead. Hehe.

Maybe you are right about clamp(), but I still think it's only available through “obscure” means if I may say so. So yeah, a clamp utility would help.

On performance and DevTools:

Take this site for instance: https://bentonow.com According to Chrome’s coverage panel, 75% of the CSS is unused on the homepage. I’ve seen this pattern on a lot of Tailwind sites. With native CSS, it’s easier to serve styles just-in-time, alongside related components. That way, users aren’t forced to download styles for pages they’ll never visit. HTTP/3 makes this kind of modular CSS much more viable.

As for DevTools, I find them much lighter, simpler, and faster to use with native CSS compared to functional CSS like Tailwind. The DOM is cleaner, and the cascade panel is far more intuitive to explore. Selectors actually mean something and reflect real relationships between content elements.

If you want to compare, you can have a look at https://ecss.info/en and then check out any Tailwind site in the DevTools.

Happy to chat more if you’re interested!


It comes from socially produced wealth and ressources, like all public services?


I invite (for fun and learning!) anyone here still thinking native CSS provides no efficient solution to the problems Tailwind may have solved 5 years ago to challenge me:

Bring up any said problem and I'll give you an efficient, robust, fast, simple and maintainable way to solve it in pure, native CSS (maybe even with further advantages!).

The time has come to embrace good ol' CSS again! Heheh.


I'm a new user of Tailwind, but there are 5 points where I believe Tailwind is wayyy more efficient than CSS:

- layout: flex and grid are great solutions, Tailwind just makes them extremely easy to use. Flex and grid are barely unusable without first defining at least 4 or 5 helper classes, more likely a dozen. Tailwind just provides them and makes them easy to use. I never could remember the flex properties, I learned the Tailwind classes I needed in like half a day of practice

- mobile first design: Tailwind strongly encourages mobile first design if you want to do responsive design. It scared me at first because media queries are hard to manipulate in CSS. I've never made a responsive design page faster than with Tailwind, it's insane how easy it is

- dark mode: as simple as `dark:xxx`. The default and dark properties being close together is awesome. As with responsive design, Tailwind prefixes make it so easy to do what's painful in plain CSS

- defaults: having sane, overridable defaults for everything (sizes, colors, whatever) makes the code infinitely more consistent. You barely even need to think about real values, just how it looks, and you have less risk of defining slightly different classes for no reason

- no cascading: CSS cascading makes it hard to reason about styles, and easy to break unrelated components by mistake. It's solved with CSS modules (or even with BEM), but Tailwind just eliminates the problem

CSS has the solutions, it just doesn't make them easy to use.


1. I don't think I understand why you think flex & grid are barely unusable... without a dozen classes..? Maybe an example would clarify this perception for me.

2. You barely need media queries anymore for responsive design. Auto-adapting flex or grid containers and the use of clamp() reduced 90% of my media query usage. I usually use like 1 to 4 media queries in project (for master layout and responsive nav, for example). And there's a nice side effect to this type of intrinsic design : layout and type is way easier to optimize for every viewport! Mobile first design is really easy to approach in this way.

3. You can apply a dark mode by only switching a theme file with design tokens inside. Way lighter, way easier to read, does not clutter your codebase.

4. As a corollary to the previous point, the defaults you are talking about can be CSS custom properties defined in a theme file as I think, Tailwind 4 will do. You could then use the exact same defaults in native CSS.

5. Simple rules about CSS usage, enforced by Stylelint for example, can prevent the vast majority (and maybe all!) of the cascading problems you mention. Here are some rules and a Stylelint config I posted in a reply below: https://ecss.info. I've got a good track record with teamwork and this config.

CSS solutions may be really easy to use when approached properly. I'm happy to follow up with examples and/or reply to subsequent questions and thoughts!


Ok, my problem is that I don't know just by reading the styles that other contributors wrote, whether they are still relevant, or if the HTML they applied to was changed or removed. They didn't write clean atomic commit, and I can't get them to adhere to some sort of convention.

The other problem is that I don't have a designer and tend to make things ugly given full freedom, but I do want things to have their own visual identity.


Great questions/problems!

1. I address the problem of relevance by inserting the CSS right into the component with a link tag. If the component is not used, the CSS isn't either. A positive side effect of this technique is that you always have an easy access to the CSS by following the link in your editor of choice. As for the relevance of the rules inside the component, the said component should be light/simple enough that this isn't harder that glancing a minute (max!) at both files.

2. You can use Stylelint to ensure adherence to specific rules. I have developed a config aiming to prevent these kinds of problems. You can find rules, guidelines and a link to the Stylelint config (still beta) at https://ecss.info.

3. A CSS theme file with custom property design tokens is sufficient. As I understand, Tailwind 4.0 made the switch to CSS tokens. You can thus use the same naming convention for your tokens in native CSS.

As complementary advantages you get future-proof code, no build step, and a lot less unused CSS (for instance, Tailwind's own homepage sports 85% unused CSS!).

I'm happy to elaborate further or respond to any subsequent questions you may have!


> I address the problem of relevance by inserting the CSS right into the component with a link tag. If the component is not used, the CSS isn't either.

Do the selectors in those style tags not apply globally? How do I prevent contributors (including myself) from accidentally writing a selector that affects HTML in other components? I see ECSS allows plain tag selectors for example?

Isn't the design token variable file quickly going to balloon? As you mention, it looks like Tailwind provides this now as well, but you'll still want to run the build step of generating the minimal style sheet that provides only the tokens you actually use, rather than having to compile that style sheet manually.

As a related question: if you're already running Stylelint, what's the problem with a build step?

And: is the CSS on Tailwind's homepage actually unused, or used in dynamically added HTML? Is the additional download+parse size actually significant, or does it e.g. compress well and not affect performance that much? Throttling network speed at least doesn't result in a terrible loading experience for me, it seems.


1. The stylelint config will flag any selector not scoped (be it a class or an attribute) to the filename. This way you'll be notified if any selector could be misapplied essentially preventing these global accidents.

2. You could use a build step for this, yes. Personally, I don't because the theme files more or less contain always the same tokens. I rarely add any (colors being the most frequent) and if I do, the cost of it being unused is minimal. My standard full theme file is around 5kb.

3. Stylelint complements my work but isn't necessary. My code does not depend on it to work, contrary to Tailwind, for instance. I'm free to stop using it any time.

4. It's a monolothic file containing all the code needed for the whole site and not only the page I'm looking at now. It may not make that much of a difference in performance, but still, the browser downloads CSS code it does not need now and then must read through all of it to apply needed rules. Not optimal.

By the way, it's great you came back with further questions! Hope to have addressed them at your satisfaction. Still at your disposal if you have more!


That generated some more question :)

But on the ECSS homepage I see selectors like `html`, `.flex-module` or `.is-loading` (I'm assuming `flex-module` and `is-loading` aren't components - if they are, then that sounds like being pushed towards a layer of indirection that I'd like to avoid?) - aren't those likely to affect other components?

What's the disadvantage of not being able to stop using Tailwind at any time? How likely is that to play out?

Why does it matter if something's "not optimal" if it doesn't make much of a difference in performance? (See also: not removing unused styles from your theme files :)


Great!

1. some simple selectors can be used in specific contexts (like `html` for type inheritance, for instance). `flex-module` is a component name (albeit a very generic one only used for example purposes...) and `is-loading` is a "state class". I use simple prefixes to distinguish between selector types and `is` is one of them.

2. Breaking changes could be introduced through the Tailwind dependency tree. Some update could be necessary to patch an exploit, again breaking functionality. Updates become routinely necessary. Which is not the case for native code. Also, using new CSS features can be more taxing within the TW ecosystem than in with vanilla CSS. In a nutshell, it's about simplicity, maintenance and the perenniality of your code. On the other hand, you may be "lucky" and never experience something like the above. But I'd wager there's more chance that you will than not. At least, my own dev experience tells me this.

3. Well, I'd say it's a matter of scale... I see 60kb of CSS on Tailwind's homepage. If more than 80% is unused, that's something like 45kb of CSS. I think at this amount, you could see marginal improvements in rendering and loading. At least, more than with comething like 2-3kb of unused CSS. Hehe. But yeah, as I said, marginal. BUT! hehe, with a less monolithic approach to bundling CSS, as with links in components, for instance, there's a real gain to be had: the browser delays reading the files not being used in the current viewport. So, if you footer is not displayed in the first screem it's CSS won't be read, accelerating overall rendering. This, in my experience, makes a palpable difference.

Hope I'm being clear, english is not my first language. And thanks for the mental workout! Hehe.


Thanks! (Not a native English speaker here either, but was perfectly clear.)


> Bring up any said problem and I'll give you an efficient, robust, fast, simple and maintainable way to solve it in pure, native CSS (maybe even with further advantages!).

I'd like to not have to read or write CSS. Let's see you tackle that. ;-)


Well, Tailwind doesn't really solve that for you either - you pretty much have to know CSS to be able to use it effectively, in my experience. Technically, you may not really be writing CSS, but in practice, I think all the reasons you might have for not wanting to write CSS still apply to writing Tailwind.


Not quite, with Tailwind:

1. you need to know the specific effects of the individual directives but you don't need to deal with the cascading behaviour and other complexities.

2. you don't need to leave your local markup to make styling changes.

3. you don't need to synchronize selectors across multiple files (non-locality).

And because Tailwind involves a lot of repetition of the same CSS classes for the same markup, it naturally drives you towards defining reusable HTML components that encapsulate the markup and CSS classes.

There are a lot of subtle usability improvements like this that avoid CSS footguns and make dev UX better overall.


1. Which part of the cascade do you struggle with? Because it can make your code really simple and efficient!

2. If you link your CSS inside your component you have a direct access to it. A simple editor split suffice to edit it. I would say that's a pretty standard workflow.

3. You can force (with Stylelint, for instance) the use of scoped selectors in CSS files, essentially preventing any selector spillover (which is a real problem). Scoping component selectors inside the one and only corresponding CSS file is important and achievable without complex code dependencies.

4. The drive to define reusable components should already be natural enough on its own! Heheh.

By approaching CSS with the good tools and mindset, you can have a great dev experience while preventing complex dependency chains, third party rules/lock-in. You can also use the most recent CSS features not implemented in Tailwind (looking at you clamp()!) to provide a better end-user experience more easily.

It's a great time to rediscover CSS!


That's fair enough, but I do still expect that someone who really wants to avoid CSS will still be put off by the extent to which Tailwind still is, in essence, CSS. I think Tailwind has advantages (the ones you note), but "being able to not learn CSS" is not it.


The clamp() CSS function (and auto-adapting grids) can reduce media query usage to a really loooow minimum. Nowadays I use media queries only for mobile nav and maybe layout grid adaptation. Like 2 to 4-5 media queries per project maximum.

I think that's what the comment you're responding to meant.


I started linking component stylesheets directly in the component HTML, de facto eliminating unused CSS from the output. HTTP3 provides performance gains we can benefit from by serving multiple CSS files "just-in-time".

I've had great results since. No more unused CSS. No more minifying nor bundling. What you write is what you get. It's liberating! Hehe.


That's good. Also, that doesn't work for a library where you're using one of six button styles.


For the curious, here is a set of "simple" rules for CSS authoring that, coupled with linting (through Stylelint, for instance) can address a lot (most?) of the issues raised in the article and the comments:

https://ecss.info/en/

Interested in what you may think about these rules and the principles behind them.

PS: still a work in progress!


This is impressive and an easy read, well done. Regardless, the fact that so many rules are needed to write good, safe, efficient CSS indicates to me at least a disconnect with the technology and how people want to use it. Indeed, just a casual look around the world of CSS tooling reinforces my opinion. CSS has such an odd mix of declarative-feeling design but with occasional "hacks" to get some imperative-like behaviors that more than once in my career I've thrown in the towel and just reached for a bit of JS to get the result I needed.

Sorry a bit rant-y.


CSS was not meant to be used by programmers but by designers... so yeah there's a disconnect there. But that does not mean programmers cannot use it the way it was intended to instead of making it, at great cost, like what they would have wanted it to be from the beginning!

Also, wouldn't you say that there are also many rules and guiding principles necessary to write good javascript? Genuinely curious here.


You can use structural HTML and CSS inside components as their content is already structured and should not change on whim. Same for the first "master" layout layer.

Applying styles with "form" classes like card still is semantic too!


Some thoughts on reasons above:

With recent display roles (like grid, flex, contents, etc.) the HTML needed exclusively for layout problems is really minimized if not completely nonexistent.

With `:has()` you can query "up" and "previous".

Some structure in your HTML is absolutely necessary, yes. But I'm not sure it is a problem. Data, content and states have structure.

Classes and attributes should be used for styling purposes, yes. Namespacing and linting can do most of the heavy work of keeping things tidy.


> With recent display roles (like grid, flex, contents, etc.) the HTML needed exclusively for layout problems is really minimized if not completely nonexistent.

I'm not talking strictly about _problems_ but about requirements/relationships. Flex/grid describe parent/child relationships between elements. They inherently couple styling with structure, so that you are sometimes forced to introduce tags for stylistic grouping.

> With `:has()` you can query "up" and "previous".

:has() is a lookahead.

Please correct me if I'm mistaken: But there are no selector rules that you can put on an element which looks up or at previous siblings that I know of.

The only CSS features that query up/previous are @media and @container rules.


You may have to group elements, yes. But generally speaking, they already are grouped in some logical sense. And you can do a lot now with grid, without having to wrap child elements in containers. It's not absolutely separated but to great degree it is (in my experience at the very least).

And yes, you can select previous elements with :has(). Here is an article about it: https://tobiasahlin.com/blog/previous-sibling-css-has/


Thank you that's a very useful article!


My pleasure!


Aren't there inherent advantages to correct and semantic code, advantages like performance, simplicity, understandability, expressivity, that could/should benefit the business?


Those advantages don't appear on metrics and therefore don't exist. If the benefit to the business is in two years and isn't measurable, the benefit it would bring will be misattributed to whoever happens to be around afterwards using it


That depends on context though. Sure, in a company driving for profits those metrics almost certainly won't exist. If the project is focused on end user experience, though, they would.

Accessibility is one area I've seen an overlap. It is still rare, but I have been on a couple teams where accessibility was a metric and it largely lead to better DOM structure and semantic HTML.


Quite true, unfortunately. But I think there are benefits that can be "feeled" almost instantly. I think problem is one of focus.

Lots of team leads don't focus on these solutions and their benefits but rather on the most recent technologies and their own benefits while ignoring (or accepting as inevitable) their costs. The longer you look in that direction, the harder it gets to see what you are missing or could get by looking elsewhere.


There are advantages for the society, for the rest of humanity, but usually not advantages for the short term bottom line of the business.


I'm curious, why would you say "applications don't map too well onto HTML"?


Most of the heavy lifting of modern web applications is still on "userland" JavaScript frameworks instead of the browser, because lots of dynamic interactions can't really be modelled with HTML alone. For example, consider creating a dropdown menu to select a contact, displayed with an avatar, name, and email as a subtitle. Doing that right—so it is accessible to screenreaders, has dynamic completions fetched from a server, adjusts to different output devices—is not only quite complex on its own, but also requires reinvention of several things, like an input field with a custom select box. Support for modal dialogs just became widely available two years ago. Two way data bindings between dynamic state and HTML elements still requires complex Javascript code. Drag and drop is a pain. Input elements haven't really caught up to the last twenty years. And while HTML does provide capabilities to register custom elements, they don't really catch on for a reason.

I will say HTML is flexible enough to be used for just about anything, but it certainly is better suited for text documents than web applications.


The OpenUI folks have been doing the angel's work of upstreaming various core components. And doing a great job. Popover & dialog are both coming along nicely. https://open-ui.org/

For a while it was all left to appdevs to build a UI toolkit out of basic HTML but OpenUI really has been driving the standard forwards.


Why would you need a custom selectbox in the example you provided? Because you could use the menu element with the popup API and AJAX if I understand your example correctly.

On a sidenote, do you know about HTMX? If so, what do you think about it?


The popover API is pretty much brand new. Sure, that would probably work. That misses my point, however; lots of basic building blocks of modern UIs users expect are missing from browsers today (or on the popover case, yesterday) and need to be added manually.

HTMX is the same story—it's what HTML could be, but isn't, unless monkey-patched in userland.


Yes it's still and always evolving and we need less and less of the heavy JS frameworks to produce interfaces. HTMX is way lighter than React, for instance. And from what I know, it's enough for many, many app use cases.


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

Search: