Your comparison of the various ListBox implementations[0] feels disingenuous. I know Vue best, so I looked at that implementation in detail, and it's got a lot going on that you don't attempt to replicate in your version. A few key features that are missing:
* Search—in the Headless UI version there are several hundred lines dedicated to making typing work for jumping to specific list items.
* Multiselect—Headless UI supports multiple selections, yours does not appear to. Again, this accounts for a large number of extra lines in the Vue implementation.
* Focus management—Headless UI has a lot of code dedicated to smoothing out the focus management. In my testing, your implementation has pretty buggy support for using tab to navigate.
* The Headless UI version dedicates a lot of lines to types, where your Nue implementation is dynamically typed. This may be a feature for you, but in my mind those type declarations are doing important work.
* In general, the Headless UI implementation tries to be flexible for many use cases [1], while yours only needs to support the one demo list.
These kinds of comparisons are most persuasive if you can write all the implementations from the ground up, using idiomatic patterns for each framework and identical feature sets for each implementation. When you do that, it's easy to compare and contrast the frameworks. As it is, it's like comparing a house to a garden shed: yes, you've used fewer lines of code, but it's not obvious to me that that's a feature of Nue and not just a byproduct of a less ambitious component.
I wouldn't just call this disingenuous, I would call it lying. He's claiming that his barely usable, non aria-compliant listbox with a fraction of the functionality, is equivalent to the work of the headless-ui team.
If the reader wonders where these lines were saved, they will only find broad, general statements about (IMO questionable) values. Compare this to some other frameworks, where the changes from the status quo are clear on the landing page:
- Svelte -> compile step, templating, common features included
- Solid -> signals, constructor functions
- Lit -> web-components, fewer features
It is unfortunately common for new frontend solutions to not be as up-front about their trade-offs as I would like. But this takes the cake for most requirements swept under the rug.
I agree it's unfair at the moment. I'll change the example to be more apples-in-apples. I'm going to write a more detailed comparison articles for each framework. Point taken! Thanks.
Ultimately Nue will come out with a UI library with a high feature parity with Headless UI. I can imagine the current listbox (demo) doubling it's size with all the above features. Definitely not 10x!
In my opinion it’s far worse than this: it’s not just a considerably less ambitious component, it’s a component currently completely unfit for any purpose. Most significantly, it’s inaccessible due to semantic abuse and missing ARIA support (role=listbox, aria-controls, aria-expanded, aria-selected, &c. &c.). Even as a sighted user that doesn’t need to use accessibility tech (AT; screen readers are the best known example), I would hate to encounter a page using this, because it misses valuable functionality like type-ahead. Users that depend on AT will be left high and dry, presented with just a read-only text box and no idea what to do with it.
All up, it’s just like so many others, a bad reimplementation of half of what the browser already provides with <select>. About the only thing it adds to that is support for an image per option, and perhaps some more exotic styling aspects (which you hopefully don’t want anyway).
Mind you, I do find Headless UI generally bloated, mostly just suffering from being overly flexible, and it does end up with a moderate amount of code bloat attributable to the particular frameworks’ designs (though not as much as some I’ve seen), but the comparison is just unreasonable. If they think it’s fair to say “With React: 2.5k lines; with Nue: 200 lines”, then I respond: “With HTML: 0 lines”, and insist that <select> is better than the result of those 200 lines.
There are more problems with it, too, whereas I suppose Headless UI will be a pretty high-quality implementation. Examples of problems I found immediately, without having tried to actually run it, but which I presume Headless UI will be free of: it uses <menu> for the options and wraps each option’s label with <strong>, which are both semantic sins; some of the code sharing with combo boxes leads to obvious minor inefficiency (filtering, which can’t actually happen); Escape detection happens on keyup instead of keydown; clicking outside the listbox popup won’t close it if the target or an ancestor matches .open, which is very plausible, e.g. when within a modal dialog (and because it’s ad hoc, it’s certain to interact badly with things like clicking outside modals—any concept of potentially-nestable modality needs to be managed more than that, in one of a few ways but I’ve already rambled enough); after pressing Escape to close the popup, pressing Tab will reopen it as well as moving to the next focusable.
Hopefully the problems with this will be sorted out, but at present I think it’d be best to just remove the comparison, because it portrays a false picture, and suggests that the listbox is ready for use, whereas I hope that the author would agree it’s not.
Totally agree with "HTML: 0 lines" is the best pick in most, if not all situations. For example the Headless UI radio group component [1], can be implemented with just HTML and CSS — and they made it a huge JS component.
> * The Headless UI version dedicates a lot of lines to types, where your Nue implementation is dynamically typed. This may be a feature for you, but in my mind those type declarations are doing important work.
> No TypeScript: Nue embraces the dynamic typing in JavaScript
I for one have no desire to fight dynamic Javascript spaghetti any more. I personally think Typescript types aren't strong enough! (Too much `as Foo` around.)
Yep, nice landing page but the component comparisons are beyond disingenuous, as should be obvious to anyone who has implemented such a thing before. If you toss out the additional features, types, accessibility, customizability and composability afforded by the use of a UI library, of course you can implement it in < 200 lines -- in any framework.
Is there such suggestion? I merely counted the amount of HTML/CSS/JS tailwindcss.com has on their frontpage and put it in a comparison chart. They key take there is that inlining styles to markup and loadinng the utility CSS makes it hard to keep the initial TCP packet below 14kb, which is a limit for extremely fast loading performance.
The nuance you provide here is missing from the site. Your chart says only this (maybe I'm missing your explanation somewhere?):
> Amount of code needed for a rich, interactive web page.
And lists "Tailwind CSS" as 804k.
As a reminder, Tailwind CSS doesn't require or use JS in the browser at all and the majority of tailwind gets removed during build so the vast majority of the code you're attributing to Tailwind CSS is unrelated to the task of using Tailwind to build a rich interactive webpage.
To your point about inlining styles, using Tailwind out of the box I struggle to see how you're forced above 14kb, and if you really had kilobytes of classes in your markup you can switch to composing your own rules with @apply.
I recognize you are making a tool and not trying to do ecosystem wide benchmarks, but still suggesting you need 804kb to use Tailwind still seems disingenuous, or at least misinformed, to me.
Sure, the CSS in the second case will be larger, but that one file gets cached for the remainder of the visit, whereas every asynchronously loaded <div> in the first case is gonna be 3.8 KB. An inventory with 3,176 items will take 12.2 MB to fully load WITHOUT product descriptions e.g. user decides to search empty string and scroll all the way to the bottom. (These numbers are rather exact because I picked a random, well-known, segment-specific shop---not cherry-picked, just the first one I thought of.)
Maybe someone at this large, segment leading company carefully considered that they should have 12 different srcset pictures because "responsive" and "bandwidth", but I doubt it because the URLs violate DRY and have escaped characters in the query part and the URL indicates this brand doesn't host static content on their own server (as in, they've hired someone else and have non-technical employees log in to a CMS and upload/update everything).
More realistically, the company that creates these websites imports a framework that has already considered that 2.3% of users are using Edge, 3.8% are using Safari, 0.4% are using Opera on Mobile, 0.2% are using Kiwi on Mobile, 0.8% have disabled Javascript, etc. and then decide they absolutely MUST have everything display as-desired on all platforms rather than excluding the most unusual or ancient user agents that represent far less than one percent of sales. I mean, who's going to leave if your site loads a little slow (but like all the others)? Who leaves if it loads "not at all"?
I see everyone from blogs to clothing stores slapping in a dozen minimized JS frameworks, fonts, reams of generated CSS, ElasticSearch, jQuery, images scaled or loaded improperly, cookie cutter UI elements and giant rounded corners and then some having the nerve to link to a "How awesome is our site?" survey (aka another nugget of JS) where I feel obliged to reply "Your front page takes 8.5 seconds to load on current stable Firefox, has 30 warnings and an error... How do YOU think you did?"
While I'm no web specialist, it seems you could even have a lightweight front page that has a small or even inline stylesheet while the sitewide CSS is loaded asynchronously (cached) after the front page is presented, so the first thing visitor sees is a quick loading page, the 2 to 4 second load happens in the background, they click a link, BOOM next page loads in its full-featured, one-or-two-human-readable-class-per-element glory with the Sneaky Load Giant CSS as an already-cached dependency.
I've also seen much worse violations than my example: divs within divs 20 deep, 15+ clvrly-abrvd-cls per div, because someone decided it's easier as a dev to have "flex flex-col-reverse lg:flex-row items-center justify-center mt-20 mb-32 md:mb-40" outside of every paragraph (instead of a single, well-defined paragraph class that could even inherit common custom properties) or better yet, <footer class="ou ov ow ox oy oz pa pb pc ab q pd pe c">
(I'm sorry if you see that I called out a snippet of your blog here, but that last example belongs to a Next.JS/TailwindCSS advocate, and it's like he took the ideas of the coinventors of CSS and then went full Second City improv, "yes, AND!")
> whereas every asynchronously loaded <div> in the first case is gonna be 3.8 KB. An inventory with 3,176 items will take 12.2 MB to fully load WITHOUT product descriptions e.g. user decides to search empty string and scroll all the way to the bottom.
If you're asynchronously loading something, why are you loading divs, and not JSON which you then display on the page?
> fter the front page is presented, so the first thing visitor sees is a quick loading page, the 2 to 4 second load happens in the background, they click a link, BOOM next page loads in its full-featured, one-or-two-human-readable-class-per-element glory with the Sneaky Load Giant CSS as an already-cached dependency.
This has nothing to do with Tailwind, css classes, or even JS frameworks.
> outside of every paragraph (instead of a single, well-defined paragraph class that could even inherit common custom properties)
Again, this has nothing to do with Tailwind, and everything to do with:
- CSS sucks at components
- CSS sucks at scoping
- CSS sucks at nesting
- CSS sucks at namespacing
Literally nothing is stopping you from applying a global style even if you use Tailwind. Moreover, the authors of CUBE methodology [1] suggest Tailwind for utility classes [2]
* Search—in the Headless UI version there are several hundred lines dedicated to making typing work for jumping to specific list items.
* Multiselect—Headless UI supports multiple selections, yours does not appear to. Again, this accounts for a large number of extra lines in the Vue implementation.
* Focus management—Headless UI has a lot of code dedicated to smoothing out the focus management. In my testing, your implementation has pretty buggy support for using tab to navigate.
* The Headless UI version dedicates a lot of lines to types, where your Nue implementation is dynamically typed. This may be a feature for you, but in my mind those type declarations are doing important work.
* In general, the Headless UI implementation tries to be flexible for many use cases [1], while yours only needs to support the one demo list.
These kinds of comparisons are most persuasive if you can write all the implementations from the ground up, using idiomatic patterns for each framework and identical feature sets for each implementation. When you do that, it's easy to compare and contrast the frameworks. As it is, it's like comparing a house to a garden shed: yes, you've used fewer lines of code, but it's not obvious to me that that's a feature of Nue and not just a byproduct of a less ambitious component.
[0] https://nuejs.org/compare/component.html
[1] https://headlessui.com/vue/listbox#component-api