Option to pass a --no-daisy flag when creating a new phoenix project

The Problem

Phoenix 1.8 comes aggressively coupled with Daisy UI, a decision which many developers in the community have had mixed feelings about.

While some agree that Daisy brings a lot of flexibility for quick and easy prototyping, there are many that are not happy with its look & how it cripples tailwind (sizes, color pallet etc)

Library maintainers are forced to provide 2 options for their UI elements- one with Daisy and one without it.

The mix phx.new command generates a web app with daisy- so there’s no option if anyone wants to opt out.

Migrating from a generated Daisy app to a pure tailwind app is a huge headache- as simply removing Daisy wont cut it. The generated core_components.ex & root.html.heex contain references to Daisy styles which will not work out of the box with tailwind, causing unexpected behavior in many cases. Its required to change all these styles manually.

Even the dark mode toggle won’t function as expected after deleting daisy.

From the time I’ve upgraded to Phoenix 1.8 release candidates, I’ve found myself using Daisy rarely, and migrating every time has became a nuisance.

Proposed Solution

I propose we allow a --no-daisy flag to be passed when creating a new project. This creates a phoenix app with just tailwind - allowing comfortable migration for developers who want to opt out.

This will be analogous to the existing --no-tailwind flag, which creates a project without any css framework.

Would love to work on this. Any suggestions or advice is welcome! :smiley:

15 Likes

I agree, I see the coupling with DaisyUI quite negatively.

I understand the need for a more comprehensive JS library, as many things are hard or impossible to do without a proper JS library (mostly animations).

But I always had a feeling of “understanding” of what was in my phoenix application, and with DaisyUI it seems way more opaque.

I am not against the integration, but I think the option should exist and perhaps even have an interactive prompt when generating a new app.

1 Like

A post was split to a new topic: Is DaisyUI like bootstrap on top of Tailwind? Isn’t this the opposite of what Tailwind advocates?

I thought I mistakenly created another topic so I deleted it. Sorry I didn’t see that you intentionally did split my reply into another topic.

1 Like

No worries, it seems like a good candidate for a dedicated thread as you’re probably not the only one wondering where it fits. I’ve undeleted the thread - however we can delete it if you prefer :023:

1 Like

I understand the need for a more comprehensive JS library, as many things are hard or impossible to do without a proper JS library (mostly animations).

DaisyUI 5 is pure CSS, there’s no JS involved.

1 Like

I thought it was a JS library to manage transition and such. My bad. But I still think it should be optional.

I whole heartedly agree that DaisyUI should be optional (and off by default).

Charitably, it was introduced because including it makes getting a super basic pretty looking CRUD app up and running marginally faster at the expense of long term maintainability and customization. Same logic that drove CoreComponents (which are, and I can’t say this enough, terrible both from a maintainability perspective and just a “how to make a good composable UI components suite” perspective).

The problem is that in no world are the 15 minute CRUD app weekend projects (which are the only projects that benefit from these decisions) the projects that should matter when it comes to decision making for a web framework. Making development of serious, complex apps that require maintenance beyond next week easier has to take precedence, and Phoenix just seems to be missing at every turn when it comes to this metric.

7 Likes

In that case, you should be able to justify taking an hour or so to remove daisyUI from the project and be done with it.

Personally, I don’t really like the default CoreComponents, but like so many other things in Phoenix, the framework doesn’t care what you use, and will chug along happily without daisyUI, CoreComponents, or whatever. They have to include something out of the box, and even if that something is not appropriate for everyone, the out-of-the-box experience is itself a teaching tool, which provides an example of what you can make with Phoenix. Use it, extend it, or get rid of it. The choice is yours.

I can definitely see the appeal of the pre-Tailwind CSS framework they used (Milligram IIRC?), but having something Javascript-based does highlight the capability of the built-in bundler integration.

8 Likes

The meaningless CRUD apps should not matter for making important decisions about framework design, but they do matter for making decisions about the “out of the box” experience (generators). Because that experience affects adoption. If it’s not easy to get started fewer people will stick around to build serious apps.

And as far as direction, well, what changes do you think Phoenix needs to better support “serious, complex apps”? Many here could easily predict what my answers to this question would be, but many would also disagree with my answers. There is a good chance you have a completely different idea of what makes an app serious or complex than I do.

I think what we really need, if anything, is more diversity.

1 Like

So, I think I disagree on two points. The first is that that “have to include something” out of the box. No, they don’t. They could absolutely ship with no CSS framework, no CoreComponent, and generators that just generate everything (markup and styles) inline. It would provide the same value in terms of generating a functional but ultimately unmaintainable app quickly without any of the downsides.

Alternatively, Phoenix could ship with stock tailwind and a legitimate component suite (think shad/cn, but in heex) and generators that use said component suite.

As it stands it is choosing to ship with a non-generalizable component suite and a CSS framework that sacrifices maintainability and customization for initial development speed.

Second, your point about taking an hour to remove all these defaults is well taken, though it does beg the question. If the rational option for building a serious application is to start by undoing the framework’s defaults doesn’t that indicate they’re bad defaults?

3 Likes

This seems to be a perennial misunderstanding among web frameworks. Not all adoption is made equal and you get what you prioritize.

If Phoenix wants to be a framework for hobby projects, then so be it, but it can’t claim to be a framework for serious complex apps while simultaneously repeatedly sacrificing the developer experience of the people working on those type of apps for the developer experience of people working on hobby projects. Adoption of one comes at the expense of the other. Phoenix is choosing their user base, not just expanding it, with these decisions.

I should add that I mean complex in a pretty specific way. Specifically, I’m referring to apps that are complex in terms of Rich Hickey’s easy vs simple dichotomy (where an app that ranks low on simplicity is called complex). With this lens, I would push for defaults and tools that prioritize simplicity. DaisyUI is a good example of a library that is easy, but not simple (aka complex). Honestly, you could say that about a lot of Phoenix. It seems to consistently value ease of development over simplicity. I’d push for defaults that are simple. Tailwind is simple (not easy, but simple). Refactoring the live view component model to be simple (make components actually composable for example) would also be high on my list. Getting rid of layouts would also be up there. I could go on, but I want to keep this discussion at least a little focused and I see DaisyUI as one of the most egregious decisions when it comes to Phoenix valuing easiness over simplicity.

1 Like

fwiw, I’m not a fan of the out of the box defaults either. I also think it’s a bit odd that the flags are opt out instead of opt in. Having said that I can appreciate giving people a nice starting point.

Maybe the solve for this is to have one more flag, e.g. —minimal or —bare, or flip it and start with almost nothing and opt in to certain things and have a single —starter that opts you into to everything the default currently has. Another idea: bring this package into core or something like it so people can customize more easily: Project - Automate common Phoenix tasks so you can start building your idea faster - #6 by marcxrand

1 Like

Sorry for this shameless intrusion. GitHub - gurujada/sutra_ui: A shadcn inspired library for Phoenix LiveView without any js dependencies offering full accessibility - we did this. https://sutraui.gurujada.com/ is where we have deployed a demo of all the components. We are planning to ship generators which will generate CRUD interfaces with this theme and GitHub - gurujada/live_table: Powerful Phoenix LiveView library that provides dynamic, interactive tables with built-in support for sorting, filtering, pagination, and data export capabilities. - so that you have powerful admin interfaces with no cost.

4 Likes

Agreed. I don’t think it should be default. At minimum a flag as suggested should be included.

I thought Daisy was awesome, until I used it in a fairly complex app (more than a weekend project). And then really felt boxed in by it. Ended up just creating custom components styled with Tailwind (which I had Claude write, since I’m not a big front end guy) - and was much better for me.

Honestly, no matter which approach the Phoenix team takes, there’s going to be a vocal crowd of anti-that-approach devs. They’re currently optimizing for a good out-of-the-box experience to get people up and running quickly. And while it doesn’t suit my tastes either, it’s totally fine.

7 Likes

If it isn’t a lot of code I think a --no-daisy flag is reasonable. However, the reality is that I start a new serious project every 5 years so it’s not like I have to remove daisyui from a new project every 3 weeks.

1 Like

Okay, well, I guess I was too quick to discount the possibility that your opinions are the same as mine.

Take your argument to its logical conclusion. How do you make the components compose? What needs to be done?

This is entirely true, and it’s why feedback can only get you so far. There is no accounting for taste.

The React team practically had a pitchfork mob chasing after them when they introduced hooks, yet they were completely right. The difference between them and the mob in question was that they had spent a whole lot more time thinking about the problem.

1 Like

So, this comes at two levels. The first is fixing the framework’s component APIs.

Top level live views are have a bunch of features that nested components (be they live components or functional components) don’t have. This is a problem because it means if you want to do a bunch of things you have to do them in the root live view, this breaks composition. There should be 1 component API, that has a mount, update, render, and unmount callback (the lack of this makes a whole class of things impossible in live view because you can’t reliably clean up after yourself). All the other stuff live views are doing should be mix-ins (though we’d probably end up calling them hooks). Want to get/set URL info, that’s a hook. Want to run in a separate process from the parent component that’s rendering you, that’s a hook (really more like a flag), and it shouldn’t change your rendering semantics one bit. Want to get/set the page title, that’s a hook.

Okay, once that’s done, time to fix core components. First, build a suite a headless components. Then build a suite of concrete components on top of that suite. I could maybe be convinced that skipping the headless/concrete division is okay given most apps are not going to ever have a need for multiple concrete versions of the same headless component (though a flag would still be nice). Anyway, components shouldn’t be using margin, I would have hopped this would be obvious in 2026, alas… These components should be decomposable to the smallest unit of design that a UI will need to render. That is not a whole ass form field that includes a label and errors. Finally, these components should allow you to provide the rendering semantics you want. Vanilla live view forms are fundamentally unusable for a whole class of apps that need to drive forms from the server because ultimately they make the decision for you about what is the source of truth and when. E.g. if an input is focused it will ignore any updates from the server, this breaks ui = f(state) in a really profound way.

Also, just to get ahead of it, no I’m not going to do any of this myself. I have a job, and even if I had the time outside of work, I’d spend it building my own thing or improving a different framework that I thought was making better decisions. There always seems to be this defense when something is criticized that unless the person doing the criticizing is willing to fix it themselves, the criticism isn’t valid. That’s silly.

1 Like