Rethinking phx-no-feedback

Rethinking phx-no-feedback

So, I won’t go into how phx-no-feedback works in detail, but the tl;dr is that it lets you conditionally add a class to an element when it is not showing feedback. In most cases, feedback means validation feedback, aka errors. What this means, is that you effective have to style the feedback version of your component, then you use phx-no-feedback to hide the feedback portions of your component. This works, but I think it can be improved.

I think we should be able to come at the problem from both sides. I propose a new phx-with-feedback class. This would conditionally add a class when feedback is being shown. The idea being that, where it makes sense, we could style the no-feedback state first, then conditionally add classes to that to get to the feedback state’s style. I should mention this is possible today! This tailwind css variant plugin adds support for what I’m describing:

plugin(({ addVariant, e }) =>
  addVariant("phx-with-feedback", [
    "[phx-feedback-for]:not(.phx-no-feedback)&",
    "[phx-feedback-for]:not(.phx-no-feedback) &",
  ])
)

allows

phx-with-feedback:border-rose-400

I’m simply asking for a slightly better DX around this by creating this variant out of the box. A corollary of this might be that we should deprecate phx-no-feedback in favor of a renamed version phx-without-feedback to keep things intuitive, or alternatively, the new variant could be called phx-feedback to stay inline with the existing phx-no-feedback.

4 Likes

I haven’t thought through it too much so maybe there is a good reason things are the way they are, but I sympathize. phx-no-feedback has always read a bit strange to me and I have to keep reminding myself what it means (since I’m not constantly using it).

2 Likes

Update to the above, while this is possible, my example [&:not(:phx-no-feedback)]:border-rose-400 doesn’t actually work as expected. So, please keep that in mind.

I’ve managed make this plugin do the job

    plugin(({ addVariant, e }) =>
      addVariant("phx-with-feedback", [
        "[phx-feedback-for]:not(.phx-no-feedback)&",
        "[phx-feedback-for]:not(.phx-no-feedback) &",
      ])
    )

and I’ve updated the main post to reflect this.

This touches on one the biggest gripes I have with Phoenix.

I am not happy with the core components approach in Phoenix.

To be clear I really do appreciate all the hard work done to make Phoenix what is today, so please take this what follows as constructive feedback and not as some ungrateful complaint.

I do feel there are some rough edges that need to be addressed based on my own frustrations with the framework:

  1. I agree handling of feedback variants seems backassward, why not just “phx-feedback” when there is feedback…

  2. Lack of class attribute handling to pass down styles to core components.

  3. Inability to support light and dark mode without heavy changes. This is where the feedback stuff was a bit strange IMO.

  4. The selection of colors in core components means heavy edits which could have been otherwise solved within tailwind configuration using named color palette similar to say daisy ui color concepts.

  5. Pulling off a simple alignment of an input with a submit button for a mini form (think email+subscribe button) is not possible without some heavy customisations to support passing down and merging of classes so the form wrappers, input and button are inline.

  6. Any changes you make to core_components will get caught up on the next update of Phoenix.

  7. The generators expect the contract of core components so you’re stuck with that contract and maintaining compatibility and are likely to run afoul and require refactoring on future changes.

In practice you really do have to AVOID both core_components and liveview generators.

So we are left with the conclusion that core_components and the generators are really just a Phoenix “sales” tool, and not actually intended for real apps that you have to maintain.

The argument may well be that core_components are only there to support the generators. If that is the case then they really should be renamed phx_gen_components because they are not really fit for purpose as core components for a real application as it stands today.

For me the framework is currently a hard fail at the basics of getting a functioning app shell.

Some further observations:

  1. When we look around we can’t find a solid functional tailwind UI component suite for Pheonix. And the answer can’t ever be something like petal due to commercial license, kitchen sink, distribution issues and inherent lag keeping up with Phoenix releases.

  2. Why is it a kubernetes level dark art to build a simple app shell with a navigation bar, sidebar or breadcrumbs?

  3. Do you use a liveview?

  4. A nested liveview?

  5. Should the navigation liveviews be sticky?

  6. Can a sticky view use live_patch?

  7. Hmm perhaps a live component in every liveview then?

  8. Hmm I wonder if we could instead add a live component to a layout?

  9. Sigh… where does it get the current/active uri from?

  10. Perhaps a plug, no … dig dig dig perhaps a live session on mount hook ?

  11. Close but no… more digging, perhaps I also need a handle params hook?

  12. Having “fun” with Phoenix…

It gets very deep very quickly without any obvious well worn path for a basic app shell.

Yes it’s all doable given enough determination but there is a ton of frustration/developer-unfriendliness to actually get the basic shell of a typical SAAS application working and it really shouldn’t be this hard.

My point is that .Core Components needs a HEAVY RETHINK to actually be useful for real applications.

2 Likes

While I would generally agree with the results you describe I’m not sure I can agree with the expectation.

Yes core_components are not perfect, but they’re also way more customizable than what we had before 1.7. Nowadays you can at least think about trying to use generators while customizing how the pieces look that the generators generate. That either required very heavy handed css or customizing whole generator templates before. Yes there’s an interface to maintain there, but I’m not sure how else you’d expect generators to call into some customizable functionality. I’d love for that interface to be better documented (statically and across releases) though.

What I find over the top though is the expectation for the generators to be able to build you a complete application. The generators are there to get initial scaffolding done, but not to lift you till v1 of your application. I’d be happy if we eventually get there and someone puts in the work to make that happen, but I find it unreasonable to expect that as the default experience.

I acknowledge that liveview poses a lot of new challenges, but LV is still pre 1.0 and completely optional. If you don’t want to be involved with solving those challenges you’re free to not use LV. If you find the generators to be lacking details there’s also real life example applications with livebeats and todo_trek, which you can look at.

5 Likes

I don’t really care about the generators, per-se, as they are of limited value.

I care about a solid set of real core components you can actually base an app on. It’s too limited for that currently being a stop gap for generators.

Currently we get neither a good core component solution or generator solution in practice.

Ideally a solid core UI component set should enable a path to more powerful UI generators to follow however, but we desperately need the UI components first.

I don’t think that’s that’s something the phoenix team should provide – or even could for the matter.

5 Likes

Yes I have looked at these because there is NOTHING in the guides that explain how to achieve outcomes, for basic app shell needs, not even a strategy to solving navigation. It’s a lot of guesswork currently.

1 Like

Well the framework will probably remain fringe if it doesn’t lead to productivity.

I’ve used many frameworks and it’s a long way from being productive. In almost every other framework these are solved problems, in Phoenix liveview its a mystery.

So the core components should be called, throwaway_components or sample_components, it’s hardly appropriate and somewhat misleading to be called “core” components when they aren’t.

It’s actually forced me to rethink app strategy because the gap is so wide. I can’t be alone in my assessment.

It’s less likely that phoenix or liveview will be in that future and I will instead use elixir for backend only and develop frontend in established JS frameworks which provide productivity and better UI experience for less pain.

Fair enough. I think we will eventually get there, but I don’t think it’ll be the phoenix core team providing that, but rather an external project building on top. And likely after LV 1.0.

However my experience is the opposite. I’ve yet to find a UI scaffolding project, which doesn’t suck one way or another. Usually they’re great for as long as you do exactly what they expect, but easily become a major pain if you don’t.

4 Likes

I agree that UI scoffolding is never ideal, but there should be some patterns and guides for solving the common needs of 90% of apps.

Currently the “discovery” is killing the project and I have developers using Svelte already past where got with liveview in a few houra. So we ditch the liveview, the phoenix and go API only with JS.

Frameworks have to deliver value vs the alternative and it’s not there yet.

To be clear, my proposal is really just about phx-no-feedback, but I do agree with the general sentiment that CoreComponents need to be re-thinked.

Currently they just don’t follow good component based development principles. For example, the smallest component when working with an input is a whole field (label + input + errors). While a field component should exist, it is generally a good practice to compose it from simpler, more generic, components.

Additionally, while the core components are a great showcase for the flexibility of TailwindCSS, they are not e very good example of good TailwindCSS. They make use of arbitrary values all over the place which is generally a poor practice as it reduces styling consistency. Also, they make use of margin all over the place which is almost always a bad idea in component based UI development (padding and/or having the parent provide spacing for its children, is generally considered a better approach).

My advice would be to rename CoreComponents to PhoenixComponents. Then have CoreComponents be a larger set of more generic and composable components that PhoenixComponents use. The idea being, Phoenix expects certain components with certain semantics to exist for thing like generators to work, and that’s reasonable, but that API is a level above the core components of an application. Phoenix could, initially generate a CoreComponents module as well that fills the role of generic composable components and are intended to be used by developers to form more complex UIs.

With this approach, component libraries can still provide a “drop in replacement” for PhoenixComponents, but that module would just be a module which uses the component library’s components. Developers would use the component library’s components directly.

3 Likes

I do agree with both of you to some degree.

I think Phoenix should provide a true core components module which contains generic components that can be used to make arbitrarily complex UIs (ideally these would be headless components). This could even be spun off into its own project like how LiveView is, since fundamentally its a component library that we’re asking for.

However, it’s also true that scaffolded UIs will never be particularly customizable, and for sufficiently complex applications generators will not be useful. This is okay, and does not mean generators are bad. They just serve a specific purpose, and Phoenix should embrace that. Make generators work, but don’t act like the components generators use are fundamental (core) since they’re not, they’re very specific for the specific UIs that generators are generating.

4 Likes

I should add I’m currently working on a component library called Brainz (not yet public), that seeks to fill the role of a core headless UI component library for Phoenix LiveView applications. My plan is to do as I’ve described here, and allow generating a CoreComponents module that uses Brainz components, but only for the use-case of supporting generators, not for generic UI development.

To this end, I might suggest that instead of implicitly importing CoreComponents everywhere, generators should explicitly import CoreComponents as needed (which will hopefully be called something like GeneratorComponents). This would help make it clear those components are not generic.

5 Likes

There is way too much in this thread that I have lots of thought on that keep me up every night about Phoenix and web dev in general so I’m not even going to try and respond to most of it :upside_down_face:

I agree that CoreComponents could do with a rename because I’ve seen too many people around here treat it as some kind of law.

From what I understand, the Tailwind team designed Phoenix’s core components, so right there illustrates what frustrates me with the web dev world :sweat_smile: This is a whole other convo I’d love to have, but not in this thread.

I’m definitely someone who has trouble keeping a cool head sometimes when talking programming, but some of the feedback to the core team can be presented a little brashly. I’ve never used Svelte but in the bit of searching I’ve done, Rich Harris isn’t involved in any of the Svelte UI stuff. As some detractors love to point out: Phoenix and Elixir are not backed by multi-billion dollar companies, so a lot of this falls on the community.

Clearly I’m calling you out there with that last point @adw632, though this is a bit of a stream-of-consciousness reply. I agree with a lot of your points.

It’s been clear from some posts in the past couple of months that Phoenix needs to speed up it’s opening game in many regards. This thread has zero’d in on UI concerns. Phoenix in general doesn’t have a strong presence of frontend people, which is not surprising, but I know we’re out there! Perhaps we should all try and collaborate a little more.

3 Likes

You haven’t offended me in any way.

I clearly stated I appreciate the efforts put into Phoenix and that my statements are intended to be constructive feedback and not some “ungrateful” complaint as one may have expecting a polished product backed by a multibillion dollar vendor.

I honestly don’t know how the community will move forward to get the needed productivity. I did locate a Phoenix UI component package which has some promise and some non trivial effort put into it, to handle extra classes (not ideal naming IMO) but again styles are still embedded into the code and it seems to be incomplete, like missing forms but has some form elements. It shows some promise but something along those lines is really needed, however I am seeing a trend of tailwind fail in component libraries with hard coded syles/colors.

Ultimately I think Phoenix might need to draw a line and think about UIkit and theming in an agnostic way.

UI kits would provide that final layer using various CSS frameworks rather than baking tailwind into the base framework by default. I’m not against tailwind but I’ve seen how it hasn’t worked well in core components. It just strikes me as odd to include a CSS framework by default instead of just providing necessary framework abstractions and semantics for UI kits to provide the flavor.

I think @AHBruns made some good points about arbitrary values all over the place and the preference that parent should driving spacing. Core components leaves a lot to be desired and reflects badly on tailwind in practice. I’d prefer to have real css to change styles not arbitrary choices embedded in framework generated code that I am damned if I use it and damned if I touch it as it will no doubt need to be merge updated on a future release. It’s the worst possible predicament so you have to stay far away from it and stay away from the generators that use core components too.

I think the focus should not be on generators.

The focus should be on solving the UI kit problem first as that can address the key weaknesses and stumbling blocks, and only then should generators have any effort expended.

This way we could have at least one solid UI kit layer to build real apps on, and if we must have generators we can get that too and base them on a rich UI kit.

Phoenix could be productive for building real apps whether you use generators or not. The UI generators should live with the UI kits, and they could support templates for bootstrapping the app shell, navigation systems, all sorts of powerful application generation concerns are possible, not just crud views.

1 Like

I didn’t get the impression that you are someone who is easily offended :slight_smile: Also, it’s always weird when I have a notification from andrewh because I’m Andrew H :sweat_smile:

Ya, I think the whole idea of CoreComponents just isn’t clear. My understanding of them is that they are not designed being part of a UI toolkit but as part of an app-specific constraint-based design system. For the latter, I believe being able to pass classes in for customization is a big anti-pattern—Your app’s design system should have ready-made components for different scenarios as opposed to a UI kit which you use to build said design system. I think it’s ok to have some wrapper components that take attributes for spacing, which is to say I do agree that including margins in components is a bad idea. But this was part of my elusive “this is what frustrates me about the web dev world” comment. I think that’s something people are just never going to agree on, so it’s hard to have a generated default that will please everyone.

I agree we need this but I’m not sure it should come from the core team. There needs to be a business who is all-in on LiveView who can get behind this type of thing. Unfortunately, as has been touched on in this thread, the likelihood of that is iffy. Most job postings I see are for Phoenix backend with React/other frontend. Even if LiveView is used I still see React being mentioned (which I’m assuming means they are using LiveView for their admin area and React for their app?) I think there just aren’t enough frontend people who focus on the “HTML-over-the-wire” stack since there are no jobs in general. I tried to sell myself as such last year when looking for a new job and of course was horribly unsuccessful :sweat_smile:

Proofing this I realize I’m writing with a tone as if everything I’m saying is fact—this is not the case.

The tailwind philosophy is exactly that though, use ulitity classes to do exactly what you want where you need it.

I think ultimately there needs to be a layer where you have enough composability or a theming layer where you can override the classes/theme and define variants for your particular app or site on top of that base layer. So once you define the variants your app would be built using the components and your defined variants with no class passdown.

I was also looking at liveview+svelte as a possible way forward to get better access to a broader set of UI components and a much richer UI given liveview hasn’t got there yet.

It’s kinda mind blowing what @woutdp has done with the ~V sigil to merge svelte and liveview. I am surprised this hasn’t got more attention:

This is really interesting. Especially from the point of view of page-specific javascript. Can this be an alternative to using phoenix hooks?

I think it is an alternative to surface UI with steroids attached.

Write all your UI in one file, html, state, JS, and styles.

Oh and animations too!

Ditch the hooks and things like alpine.js and just use the disappearing Svelte framework which complies to just your code. It’s very tight.

Seems like the killer combo to me.

The only thing I can see is if you decide you want some pages with svelte elements to do sever side rendering you will need to ship with the renderer like node/bun, but that is 100% optional, svelte is progressive.