This is what Daisy UI does. But at that point you might as well just use CSS with a nice set of CSS variables.
No, I am not using Tailwind. I really hate it.
Why do you hate it? Please give us more info so we can determine whether it’s something that might be worth taking into account ourselves..
I’m using tailwind and now Daisy because phoenix recommended it. I have been around a long time and I remember how bad it was in the old days. We would have just huge gobs of CSS no one could remove but no one knew what it did so each new component just added to the pile because we couldn’t risk breaking the old stuff.
I’ve been a backend dev since then, so I’m not on top of it at all, but coming from that what tailwind does is a breath of fresh air. For a small app a little CSS is fine but it never goes away, and that requires something like the code analysis that tailwind does.
Not to mention I set up this app, wrote some pretty complicated pages and with Daisy everything looks decent. It even helps lower the amount of classes you send over the wire. And ai really kills it with a little tailwind v4 context.
I get that the up front is a bit annoying but going back to the basics is not for me. I’ve been there.
Could we use both TailwindCSS and custom css? I tried with some suggestions from some AI to avoid TailwindCSS to reset custom CSS but the custom still work not smoothly.
Wow quite a thread so I’ll make it short. I’ve neglected Tailwind for years, preferring manual CSS/SASS. Tailwind 4 changed my mind and I’m never going back, unless forced by existing project’s stack. It’s compact, well-documented, with mature ecosystem around it.
I’ve never been styling so fast and so beautiful & consistent before. Not even close.
For small projects, to be honest, it doesn’t matter, go with whatever if you are working alone.
If you don’t know CSS well, I would advise to just go vanilla and learn some CSS, it is through pain that we learn and grow. I have in my career maintained large projects from big companies where CSS was a major big pain, specially in legacy systems.
I like Tailwind CSS but I know CSS very well, if I had to choose, I would choose co-located Scoped CSS, like VueJS does over Tailwind, but since that isn’t a choice in every framework, for example LiveView, I do prefer TailwindCSS over un-scoped CSS class soup hell.
Every big project starts small and with very good intentions, but maintaining CSS is HARD, this is what TailwindCSS does better, at least, at the lack of scoped CSS / CSS Modules.
Also, utility CSS makes a lot of sense, specially for layouts, there is always a lot of repeating classes, no one needs different named classes for single utilities, but it has been done before, for example, Bootstrap, it had utilities built-in.
This approach to CSS is probably my favourite: https://cube.fyi/
Interesting, before switching to Tailwind I’ve worked on React frontend with styled-components which were, according to our FEs, as good as it gets wrt modern, scoped, well organized CSS. And while I saw the good parts, the neat way to inject props etc) there were clear cons too - need to import everything including spacings, risk of infinite-permutation props etc.
It way probably that point which made me feel „enough is enough” after 10+ years of writing CSS.
So it happened that Tailwind 4 release just came in, with super-nice stuff like **:[sleector]:util to further reduce a need for ad hoc custom CSS, making TW a really complete & uniform option for me.
You can try this for scoping - GitHub - gnat/css-scope-inline: 🌘 Scope your inline style tags in pure vanilla CSS! Only 16 lines. No build. No dependencies.
You (almost) do not even need JS for that - @scope - CSS | MDN
I wonder if someone out there has a fork of the generators that is set up for plain CSS. I know you can use –no-tailwind when generating your project, but I’m thinking more of one that has a CSS file set up, added to the static path, core components generated but without tailwind styles, same with the other view/live view generators, and so on.
We now have colocated JS so I see no issue with colocated CSS. Guess it’s upcoming with the new Macro components.
I think it depends on what you’re actually trying to solve and for me it comes down to the following:
- a way to manage design tokens, with sensible defaults and naming conventions
- utility classes for layouts/positioning and one-off visual changes
- a maintainable & readable way to manage CSS for reusable components
So then you naturally arrive at something like this: stylesheets just for components (using some naming convention such as BEM, CSS Modules, etc.) in combination with PostCSS/Tailwind.
Via theme or @apply you can reference design tokens from Tailwind.
.heading { font-size: theme('fontSize.xl') }
.card { padding: theme('spacing.4') }
The beautify of this is that you can now combine these components with utility classes:
<h1 class="heading flex gap-2">Heading <svg ... /></h1>
<div class="card mt-4"></h1>
Since the composition of components differs page it makes a lot of sense to use utility classes instead of writing additional CSS for each situation.
On top of that you can even make one-off visual changes to components, until it makes sense to introduce a new component property:
<h1 class="heading border-b">Heading with border bottom</h1>
There are a lot of things that Tailwind does that you would otherwise end up doing yourself, just with more code to maintain and probably less well thought out.
It’s a bit unfortunate that Tailwind has become a bit of a religion and there isn’t always a ton of discussion about what styling problems we’re actually trying to solve
(not talking about this thread, just in general).
So I’m not married to a specific framework, it’s just that I found that this is sensible way to go about things.
For those suggesting using @apply, isn’t it against the Tailwind way?
Source: https://x.com/adamwathan/status/1226511611592085504
Edit: From google’s AI mode:
Why Wathan regrets @apply
Encourages bad practices: Wathan says that
@applytempts developers away from Tailwind’s core utility-first philosophy. Instead of reusing utility classes directly in the HTML, developers are led to create their own custom classes that contain a bundle of utilities, which is exactly the practice Tailwind was designed to avoid.A “bait and switch” : In 2020, Wathan confessed on X (formerly Twitter) that
@applywas originally added to “trick people who are put off by long lists of classes into trying the framework”. However, he emphasized that developers “should almost never use it”.Outrageously complicated behavior: According to Wathan, the underlying behavior of
@applyis very complex, which makes it difficult for developers to build a proper mental model of how it works. This complexity makes it easy to misuse and leads to confusing results.Poor CSS architecture: By hiding utility classes behind custom class names,
@applycan lead to less readable and maintainable code over time. It can also cause unexpected side effects that go against the framework’s predictable nature.The Tailwind philosophy
The core principle of Tailwind CSS is to use single-purpose utility classes directly in your HTML. For example, instead of defining a custom
.btn-primaryclass in your CSS file, you would apply thebg-blue-500,text-white, andfont-boldutility classes directly to your button element.This approach, known as “utility-first,” offers several benefits:
- You don’t need to leave your HTML file to style your components.
- You avoid accumulating custom CSS that is rarely reused.
- You can easily make design changes by adding or removing classes in the HTML.
The verdict on @apply
Wathan has stated that if he were to create Tailwind CSS from scratch,
@applywould not be included. His stance is a clear signal that for most use cases, developers should stick to Tailwind’s core utility-first principle rather than relying on@applyto mimic traditional CSS preprocessors.
Oh yes good point!
Perhaps I should have left that out, I tend to use theme() (in v4 that’s replaced with just CSS vars I believe). I agree that there isn’t really a use case for @apply, except laziness/convenience
.
The
applyfeature in Tailwind basically only exists to trick people who are put off by long lists of classes into trying the framework.
The irony of this statement is that @apply puts your long list of utility classes in your CSS file
.
I get the point about that you don’t technically “need” BEM, since you can reuse utility classes via partials or components as well.
But I find that it gets pretty unreadable pretty quickly. It is a bit unfortunate that this leads people to disregard Tailwind entirely.
Especially for components with many different states, it is (imho) just more readable to use a stylesheet and some naming convention such as BEM (or whatever you prefer).
It just helps a ton if your button has a .button--primary class on it instead of a long list of utility classes.
Tailwind is crafted for use with components, so component name would replace CSS class name.
.button-primary → <.button color=“primary”>
.heading → <.heading>
As a result your templates are not riddled with classes, as most “look how bad” examples show.
That being said: if you do “cleanup” the code by using components, you end up shimming all HTML elements. Hence native HTML elements such as h1 etc don’t have styling in Tailwind (without plugin Typography)! You are supposed to (inside) ‘component’ them ![]()
So no <div classes=“a thousand classes for this container containing the main content”> but <.main_content>
Wait, did we just shift from “ow no a central CSS file and having to come up with class names” to “a zillion components at no default places and having to come up with components names”?
Update:
In this thread my opinions (multiple!) while reading the book.
I see I agree with myself from the past:
For React 3, components are created (in the book) : Header, SubHeader and SubSubHeader. The next example uses a plain JS function named
titleto return a list of ultility classes. Let’s apply those to H6: SubSubSubSubSubHeader and subSubSubSubSubTitle()! Guess we are back at “naming things is hard”.
and
What I’ve learned so far:
- The solution to prevent duplication requires you to come up with names.
- Creating components/partials can cause code with weird syntax AND you still need to come up with those names.
- When all gets ugly, you can use
@apply…just know you will have to…come up with some names.I feel like we are going in circles, just so Tailwind can get away with “duplication is not an issue” and “with Tailwind you don’t have to come up with names for your shared CSS properties.
Glad those names are not CSS names cause those were really hard to come up with according to Tailwind
and
But can anyone explain me why it is
font-boldanditalicinstead offont-italic? I understand from a CSS mapping point of view but I rather would stay within the ‘namespace’ so the guessing would be easier. [..] So padding is done withp{side}-{size}and border withborder-{side}-{size}. Mind the dash. Why Tailwind?
Not using Tailwind daily, I still fail to pick the proper prefix text- or font- or none at all, and have to learn all the new ‘naming conventions’ (read: is there consistency at all?)
That being said: I use it in all my lib demo apps.
1. But CSS files tend to blow up with unused classes!
Well, build something to find used ones diffing it with the class names in the CSS file to drop unu…wait..there must have been a point in time the Tailwind compiler did just that!
2. Who can remembers all existing class names?
The same people who remember all Tailwind oddities?
3. But you can’t see how the component looks from source code
But you can see all other things; which get’s harder if you add 20 class names on each element.
4. But you can hide those in editor
True, but now 3… and this is a solution for a problem we did not have.
5. But you can’t do :hover in style attribute
Using style attribute is not mandatory.
To me it seems all issues Tailwind ‘solves’ could and should have been solved by a formatter to remove unused classnames (on request) and ‘LSP for CSS’: “Jump to class definition”, “Show class references” in all editors. Proof me wrong ![]()
I disagree, because I view components as encapsulation of functions, not encapsulation of styles. The same component can be instantiated several times, each with a very different style. In your example you only hide the dirty laundry at a lower level. My pet peeve with Tailwind is exactly that: it kinds of force you to deeply couple function concerns with style concerns.
So we agree
I demonstrated the ‘blessed’ Tailwind way and linked to all my objections against it.
I think the argument regarding the utility class soup is also about:
- what the markup looks like in the browser / inspect element
- that when you open your component file, it is now littered with utility classes
It was like this in an earlier version of petal components but they switched to proper class names (I think also to make theming easier): petal_components/lib/petal_components/button.ex at v0.19.7 · petalframework/petal_components · GitHub






















