> See, I know that shipping end-user software - like the Tailwind compiler - if that software is written in an interpreted language - is fucking hard.
This I don't get. This is interpreted code, most web backends integrate node, why not just ship a node module? Why in gods name ship bun.
The whole web development scene has had some of the worst software engineering I've ever seen. With the exception of the Ruby scene, we have Tilt and Nokogiri for these types of things.
I was hesitant to give tailwind a try, like most aging web developers I couldn't stand that it breaks the "cascading" part of CSS.
But eventually I didn't have choice as I inherited a web app that has all of the newfangled build components that web apps come with. I love that we're coming full circle back to MVC with server components.
After getting used to it, I ended up liking Tailwind, mostly because it breaks the cascading part of CSS. There are so many unique parts to webpages these days that I think it makes sense to keep the styles close to their components, as opposed to global cascading components.
Yeah, I've been a fairly vocal curmudgeon about Tailwind within my team. Some folks really like it. They're doing the work and I trust them, so I rolled with it.
I still have qualms with Tailwind. My classic CSS sensibilities are offended, but whatever. The part that I still don't like is really what this post boils down to: a massively complex build system that creates footguns in the weirdest places.
That being said, Tailwind that's set up well in coordination with a complex design system really does feel like it's a win. Seeing that in action was an aha moment where I was able to see value that made some of the tradeoffs worth it.
The other thing that people seem to just either forget or ignore is that you can do _both_ things! Tailwind can be used for the areas that it makes sense for, and you can write a sitewide layout using insane (in a good way!) grid declarations using plain old CSS and a few extra classes in your markup.
If you're using Vue, you even get a <style> block that can be injected into any component so you're still working in the same context. It's all delightful and optional and you can still do whatever you want.
I've got a number of vanilla JS, HTML, CSS libraries that work only from within the browser, using the standard tags to include them.
For me, not having a build-step means that, yes, I miss out on typescript, but the upsides are easier to read, easier debugging and lower cognitive burden during both writing and reading the application.
It means I will never, like the author in the article, spend $X weeks, and then spend a further $Y dollars just to build something "modern".
My "modern looking" applications are developed, and then tested, on a machine from 2010 (1st-gen i7) that has 16GB of RAM and oodles of slow spinning-rust disks, and it isn't painful at all.[1]
[1] It is, in fact, quite nice when I see a client use their system from their own PC and it's just a little bit snappier than I am used to.
The comparison to inline styles is understandable but misses Tailwind's real value. Inline styles failed because they were arbitrary, inconsistent, and impossible to scale. Tailwind, by contrast, offers a constrained, standardized set of utilities that enforce consistency, simplify maintenance, and reduce CSS complexity. For example, instead of arbitrary inline styles like `style="padding:7px; margin-left:3px; font-size:14px;"`, Tailwind provides predictable classes like `p-2 ml-1 text-sm`, ensuring uniformity across your app.
> Tailwind provides predictable classes like `p-2 ml-1 text-sm`, ensuring uniformity across your app.
This is literally what bootstrap did/does. You could also trivially do this yourself with just a tiny bit of discipline -- it's part of why variables were valuable in SASS/SCSS.
Why must we re-invent CSS to get semantic class names? Like the parent, I have yet to hear an explanation for Tailwind that doesn't sound like "someone who didn't fully understand CSS re-wrote it, and a generation of bootcamp coders adopted it without knowing why, or what came before."
Front end is definitely pretty hard nowadays. It seems like the technology should be easy but keep in mind you’re getting everything for free and this isn’t the field you have a ton of practice in. It’s grown a lot while also being backwards compatible with browsers which are some of the more complicated pieces of software.
Trust me, I get very confused and frustrated whenever I have to figure out python deps or kubernetes, but I accept it’s going to be difficult since I’m not familiar with the field.
> It seems like the technology should be easy but keep in mind you’re getting everything for free and this isn’t the field you have a ton of practice in.
While keeping in mind that this isn't a field that I have a ton of practice in, I can confidently assert that a parser for HTML input that outputs CSS classnames does not need all of the following:
1. A recent Node (+ dependencies)
2. Pnpm
3. Rust (+ dependencies)
4. Bun build environment
5. A binary size of 100MB
I'm pretty certain I needed an HTML parser at some point in the past, and it was built as a single standalone file, that compiled to a single standalone ~50Kb binary, that took a single day to write.
Now, fair enough, that doesn't spit out class names (because I didn't need it at the time, although looking at the query language it seems that it might be able to do that) that, but it's very easy to add a single flag for "spit out class names". The build dependencies are:
1. A C compiler.
There's no makefile/build-script - simply doing `$CC program.c -o program` is sufficient.
So, sure, while I don't have a ton of practice in this area, I have enough to know that the binary/program in question is heavily over-engineered. Their dependencies are artificial requirements (we require this because this other technology requires it), not functional requirements (we require this because this feature requires it).
I was a web developer from 1996 until 2023. I jumped ship because of exactly this sort of nonsense. I still do private web development, but I do it all using native vanilla HTML, JS, and CSS. It just works.
reading that really makes me wish there were a compiled-to-static-binary language with a reasonably easy migration path from javascript (sort of like ruby->crystal, where the latter tries hard to look like ruby so that at least some fraction of your code translates mechanically). there really is no reason that a tool like tailwind should be this hard to ship in a robust and compact binary.
> See, I know that shipping end-user software - like the Tailwind compiler - if that software is written in an interpreted language - is fucking hard.
This I don't get. This is interpreted code, most web backends integrate node, why not just ship a node module? Why in gods name ship bun.
The whole web development scene has had some of the worst software engineering I've ever seen. With the exception of the Ruby scene, we have Tilt and Nokogiri for these types of things.
I was hesitant to give tailwind a try, like most aging web developers I couldn't stand that it breaks the "cascading" part of CSS.
But eventually I didn't have choice as I inherited a web app that has all of the newfangled build components that web apps come with. I love that we're coming full circle back to MVC with server components.
After getting used to it, I ended up liking Tailwind, mostly because it breaks the cascading part of CSS. There are so many unique parts to webpages these days that I think it makes sense to keep the styles close to their components, as opposed to global cascading components.
Yeah, I've been a fairly vocal curmudgeon about Tailwind within my team. Some folks really like it. They're doing the work and I trust them, so I rolled with it.
I still have qualms with Tailwind. My classic CSS sensibilities are offended, but whatever. The part that I still don't like is really what this post boils down to: a massively complex build system that creates footguns in the weirdest places.
That being said, Tailwind that's set up well in coordination with a complex design system really does feel like it's a win. Seeing that in action was an aha moment where I was able to see value that made some of the tradeoffs worth it.
The other thing that people seem to just either forget or ignore is that you can do _both_ things! Tailwind can be used for the areas that it makes sense for, and you can write a sitewide layout using insane (in a good way!) grid declarations using plain old CSS and a few extra classes in your markup.
If you're using Vue, you even get a <style> block that can be injected into any component so you're still working in the same context. It's all delightful and optional and you can still do whatever you want.
I've got a number of vanilla JS, HTML, CSS libraries that work only from within the browser, using the standard tags to include them.
For me, not having a build-step means that, yes, I miss out on typescript, but the upsides are easier to read, easier debugging and lower cognitive burden during both writing and reading the application.
It means I will never, like the author in the article, spend $X weeks, and then spend a further $Y dollars just to build something "modern".
My "modern looking" applications are developed, and then tested, on a machine from 2010 (1st-gen i7) that has 16GB of RAM and oodles of slow spinning-rust disks, and it isn't painful at all.[1]
[1] It is, in fact, quite nice when I see a client use their system from their own PC and it's just a little bit snappier than I am used to.
I have genuinely never understood Tailwind's value proposition, other than as padding for its developers' CVs, at which I assume it excels.
We stopped inlining style attributes for a reason - is this just how the next generation needs to learn?
The comparison to inline styles is understandable but misses Tailwind's real value. Inline styles failed because they were arbitrary, inconsistent, and impossible to scale. Tailwind, by contrast, offers a constrained, standardized set of utilities that enforce consistency, simplify maintenance, and reduce CSS complexity. For example, instead of arbitrary inline styles like `style="padding:7px; margin-left:3px; font-size:14px;"`, Tailwind provides predictable classes like `p-2 ml-1 text-sm`, ensuring uniformity across your app.
> Tailwind provides predictable classes like `p-2 ml-1 text-sm`, ensuring uniformity across your app.
This is literally what bootstrap did/does. You could also trivially do this yourself with just a tiny bit of discipline -- it's part of why variables were valuable in SASS/SCSS.
Why must we re-invent CSS to get semantic class names? Like the parent, I have yet to hear an explanation for Tailwind that doesn't sound like "someone who didn't fully understand CSS re-wrote it, and a generation of bootcamp coders adopted it without knowing why, or what came before."
Front end is definitely pretty hard nowadays. It seems like the technology should be easy but keep in mind you’re getting everything for free and this isn’t the field you have a ton of practice in. It’s grown a lot while also being backwards compatible with browsers which are some of the more complicated pieces of software.
Trust me, I get very confused and frustrated whenever I have to figure out python deps or kubernetes, but I accept it’s going to be difficult since I’m not familiar with the field.
> It seems like the technology should be easy but keep in mind you’re getting everything for free and this isn’t the field you have a ton of practice in.
While keeping in mind that this isn't a field that I have a ton of practice in, I can confidently assert that a parser for HTML input that outputs CSS classnames does not need all of the following:
1. A recent Node (+ dependencies)
2. Pnpm
3. Rust (+ dependencies)
4. Bun build environment
5. A binary size of 100MB
I'm pretty certain I needed an HTML parser at some point in the past, and it was built as a single standalone file, that compiled to a single standalone ~50Kb binary, that took a single day to write.
Actually, here it is: https://gist.github.com/lelanthran/896a2d1e228d345ecea66a5b2...
Now, fair enough, that doesn't spit out class names (because I didn't need it at the time, although looking at the query language it seems that it might be able to do that) that, but it's very easy to add a single flag for "spit out class names". The build dependencies are:
1. A C compiler.
There's no makefile/build-script - simply doing `$CC program.c -o program` is sufficient.
So, sure, while I don't have a ton of practice in this area, I have enough to know that the binary/program in question is heavily over-engineered. Their dependencies are artificial requirements (we require this because this other technology requires it), not functional requirements (we require this because this feature requires it).
I was a web developer from 1996 until 2023. I jumped ship because of exactly this sort of nonsense. I still do private web development, but I do it all using native vanilla HTML, JS, and CSS. It just works.
reading that really makes me wish there were a compiled-to-static-binary language with a reasonably easy migration path from javascript (sort of like ruby->crystal, where the latter tries hard to look like ruby so that at least some fraction of your code translates mechanically). there really is no reason that a tool like tailwind should be this hard to ship in a robust and compact binary.