Separating the styles from the markup was always a terrible idea. There was a notion that you could mark up your documents “semantically” with elements and then re-use styles across documents and documents across styles. This has systematically failed. Nobody does this.
So instead you’re just left cross-referencing a giant CSS file with a giant set of HTML templates. Total mess. Zero benefit. Bad idea.
In fact, just using the style attribute would be a wonderful solution, except for the fact that it sucks. It’s ancient and it wasn’t designed for this.
The correct way to do composability and re-use is not to apply the same styles to different elements but to group elements and their styles into components and compose there. This is what everyone does now. We do it with Phoenix too.
Tailwind, as a library, solves three problems:
- Styles should be co-located with components
- Styles should be composable (no name conflicts)
- Design systems should work out of the box
Unfortunately, the utility class approach is fundamentally degenerate: it can never fully express CSS, and CSS is the thing which browser vendors actually support. So you are always playing catch-up.
The series of increasingly ridiculous hacks they have come up with to fit CSS into a class-shaped hole is something to behold. First they needed a compiler to strip the unused classes. Then they started dynamically generating styles at runtime with a made-up class syntax. So then they had to start shipping binaries just to compile CSS, and now they’ve even had to introduce a new Rust codebase to keep things performant.
There is a much simpler solution: web frameworks are already responsible for providing a component framework (we have HEEx). They should also be responsible for providing a scoped CSS implementation to go with it, and an extensible design system out of the box.
(See my comment in the other thread (linked in the OP) for an example of how this can be done.)






















