LiveView phx-no-feedback without tailwind

I want to create CoreComponents based on Bootstrap 5 instead of tailwind. I’m having issues with form validation. It seems like the only way Phoenix has of telling the client that feedback should be shown or not on a given form field is through the phx-no-feedback class. Apparently, tailwindcss does some kind of dark magic with class pseudoselectors (?) in which things work (or maybe it’s the subset of tailwind used by Phoenix? I don’t know much about tailwind and my bootstrap client adventures are mainly a way of avoiding having to learn about tailwind and the respective build tools).

With tailwind, one does something like phx-no-feedback:some-tailwind-utility-class, but that only works due to tailding JS and CSS magic, right?

If I am to use Bootstrap 5, what is the right way to add the is-valid and is-invalid bootstrap classes to my input components to mimic the behaviour of the default CoreComponents file? I’m not very knowledgeable about CSS but I’m willing to learn enough to make it work if anyone can point me to the right sources. I believe there is probably a way of doing this that doesn’t depend on taiwlind and doesn’t require rewriting the original bootstrap CSS file to make it work.

What version of Phoenix are you on?

LiveView, or?

All the dark magic of tailwind does is apply certain styles only if the phx-no-feedback class is present or not present.

So phx-no-feedback:border-zinc-300 becomes

.phx-no-feedback.border-zinc-300 { border-color: …; }

Given how component frameworks like bootstrap work you cannot easily make their is-valid/is-invalid class be scoped to the existance of another class. You’d either need to copy their class and add the necessary scoping or use some JS to react to phoenix adding or removing the phx-no-feedback class.

1 Like

That’s what I expected, unfortunately. Is the server aware of which fields are supposed to have no feedback or is it everything client side?

It’s client side. That‘s the whole reason for this being done with a class: It doesn‘t require interaction with the server. If you focus, but not change an input any errors will show up on blur.

1 Like

We did Bootstrap 4 with LV quite some time ago. Some of the lessons may still apply: Using Bootstrap Native with Phoenix LiveView - Dashbit Blog - the code was also open sourced on the bytepack repo in our org.

1 Like

Thanks, your examples were very useful for the modals, which I still haven’t managed to get working as well as I want, but at least it gives me something to work with (I believe some of the “problems” are due to the fact that your examples use bootstrap 4 and I want to use bootstrap 5, which has some subtle differences. I had already managed to get the form validation functionality working by doing what @LostKobrakai had suggested (i.e. adding some extra scoping rules).

The hard part was dealing with the fact that to support all Bootswatch themes I had to find the default border color and focus shadow color for the form inputs, but I managed to scrape that from the bootswatch.css files using some hacky regexes and with that I managed to build the class overrides programmatically. This means I now have an end-to-end elixir script which can download the bootswatch.css files, extract the “no feedback” border colors and generate the class overrides which are either included as inline CSS in my components file (if the user decides to load bootswatch from a CDN) or append it (at build-time, obviously) to the static files if the user wants to serve the CSS files from the app.

For completeness:

  1. I’ve replaced heroicons with bootstrap icons for better integration, which I’m serving as inline CSS. Bootstrap already uses their own icons for form validation, and using bootstrap icons
  2. I’m working on defining a PhoenixCoreComponents behaviour which should support all functionality used in the default phoenix generators so that one can use external CoreComponents files and have everything work seamlessly
  3. I’ve made it so that the user can do the equivalent of use Bootstrap5Components in their MyAppWeb.CoreComponents and have everything work by default, but also easily dump the original source code of Bootstrap5Components into the file in order to override the default components. This uses my new project: CodeGen - simple, succint and customizable code generation for your libraries - #17 by tmbb

I am not CSS/SCSS/SASS/LESS expert, but could you use something like @apply .other-class-i-want-to-override and then define the styles before or after?

To be clear, the icons won’t be part of the HTML, right? We generally want to avoid that but you should be able to use CSS masks like Heroicons do it on Phoenix v1.7.2.

I’m even less of a CSS expert than you… I like my css pre-built and pretty by default and I don’t want to write CSS unless I really have to (that’s part of the motivation of using bootstrap instead of tailwind - writing tailwind feels too much like writing CSS). I don’t think I can do what you suggest without editing the original bootswatch.css files (there are a lot of them), which I’d like to avoid. I don’t care about appending CSS to those files, but actually editing them by hand seems painful.

I’ve coded it so that my component inserts inline SVG into the HTML, but I can change it so that it uses an icon font (which bootstrap also provides) so that it is maximally customizable with CSS only. It would be probably even easier (?). I’ll have to look into what the Elixir heroicons package does, which I haven’t done yet. Why do you want to avoid literal SVG inside the HTML? It seems simpler than CSS masks.

PS. it seems like heroicons is dumping literal SVG into the HTML too: heroicons_elixir/heroicons.ex at main · mveytsman/heroicons_elixir · GitHub

Yes, Phoenix v1.7.2 stopped using heroicons because we have noticed that we would send the same SVG multiple times. This approach makes total sense for a front-end framework but the lack of caching makes it a no-go for server-side. Icon fonts would be preferable here (or use masks like we did for 1.7.2).

The SVGs are quite small, but I guess even still caching might be worth it (even inf the initial download size for the icon font is much bigger).

I’m thinking of turning the icon function into something like the gettext macro which stores the icons that are actually used and allows the user to build an optimized icons.css file in the application’s static directory.

Ideally such file could be generated at compilation time without user intervention

It seems a bit brittle but still better than complication the assets pipeline further…

Just encountered the same issue with combining Bootstrap with Live View. If SASS is involved, there is a workaround by extending the .is-invalid class:

:not(.phx-no-feedback) > .has-error {
    @extend .is-invalid;

Then apply newly created .has-error instead of .is-invalid.

1 Like