Adding `--tailwind` flag to phx.new

think you can relatively easy abstract those long and reusable lines of (tailwind)classes https://tailwindcss.com/docs/extracting-components#extracting-component-classes-with-apply

I think tailwind out of the box is an excellent choice, especially for beginners where installing npm packages and changing a webpack config can be quite the challenge…

5 Likes

I do this! I use @apply for most styling and then make small tweaks in the templates when they differ in some way from the normal theme.

2 Likes

Whole heartedly agree that we shouldn’t make it part of Phoenix generators.

I think Tailwind is based on an anti pattern and encourages coupling of styling to markup. It is a leaky abstraction and, respectfully, I don’t think as a community we should encourage using it by embedding it into the framework. It should be fairly easy for those who to opt in to paste a line of html and import it from a CDN or install it as a node module.

I REALLY like Elm, but I won’t propose it will be part of the default Phoenix generators, because I know for a lot of projects it is absolutely not the right choice. And Elm can be harder to setup on a new project compared to Tailwind but I still don’t see a justification for that.

At the end of the day it is an opinion to use it and has been a contentious one in a few companies I worked for (not just because of my opinions on it, many agreed that we should not be using it for similar reasons).

2 Likes

If you think tailwind is an anti-pattern - you must have missed the CSS in JS ‘hype’… CSS-in-JS - Wikipedia

Like most, I thought tailwind looked a bit rough - but after only a few hours using it - I have become a convert.

Even the creator of tailwind agrees at first glance it does not look good:

I’ve written a few thousand words on why traditional “semantic class names” are the reason CSS is hard to maintain, but the truth is you’re never going to believe me until you actually try it. If you can suppress the urge to retch long enough to give it a chance, I really think you’ll wonder how you ever worked with CSS any other way.

They have an entire carousel on the tailwind site of testimonials of folks who thought it weird at first sight but once using it recognize its’ potential.

Tailwind lets you make style changes without ever opening a css file. I think most people agree that HTML + CSS + JS all fall into the UI concern category - and as demonstrated with CSS in JS - it is not an anti pattern to mix these together. Tools like alpine take it a step further, and all UI concerns live in your HTML.

I think one lesson from tailwind is that technologies like LESS/SASS are falling out of favour and are being replaced with PostCSS - PostCSS - Wikipedia

The phoenix team should consider adopting postcss + some plugins and removing node-sass + sass-loader. That would make adopting tailwind a lot easier than it is today.

When migrating phoenix to webpack 5, might be worth also tackling this^

Am fighting a bit with a webpack5 + tailwind phoenix setup currently - could make a PR if things stabilize a bit. Might still be too early for webpack 5…

Yea including it in phoenix is probably not the best idea - but making adopting it easier would be nice…

9 Likes

There is a PR for webpack 5, it’s from this post… Upgrade to webpack 5

I can tell the resulting webpack.config.js is more simple.

1 Like

Me thinks you are looking for this link. Upgrade to webpack 5

1 Like

Have you seen webpacker though?

I agree. I don’t see any reason the Phoenix team should devote their time to adding individual CSS frameworks. The same goes for JS. Just let people include their Webpack bundles in the static assets easily like you are supposed to do.

Why is there even a need for Phoenix to “integrate” with any specific CSS framework?

1 Like

I think it would be much better to focus on making the generators as easy to extend as possible. This way people who want things like tailwind or anything else can create the necessary extensions to do that. Unfortunately every time I’ve tried to take a stab at customizing the generators, I get a little overwhelmed. I need to take a week or so and dedicate all my attention to learning how to create my own generators.

2 Likes

I 100% agree, though it’s nice and convenient when people build generators to consider such frameworks it also comes with the cost of maintaining that dependency.

At the end of the day, this all has everything to do with webpack/postcss and tailwind and almost nothing todo with phoenix. If someone wants to make a nice generator or setup wizard for installing postcss and all its dependency along with tailwind into webpack that would be most likely the sole concern of webpack.

2 Likes

I have yet to try this project out myself and unfortunately it does not appear to be open source (which would allow the community to extend it) but I feel it is worth mentioning on this thread, as it claims to provide the ability to generate a new phoenix project with Tailwind and a few other customizations https://fullstackphoenix.com/boilerplates/new

2 Likes

is there any reason why a Tailwind generator couldn’t be outside of core but a contrib on Hex?

2 Likes

orthogonal https://github.com/pragdave/mix_templates

3 Likes

As the person who literally implemented the Rails Application Templates in the incarnation still used today, they have many pitfalls that may not be obvious unless you maintain them and/or write anything mildly complex with them. For example:

  1. Brittle. Whenever you change the generated code, which is often common, there is a chance template code will break or it will generate the wrong application

  2. They don’t compose. As application templates get more and more complex, it is unlikely you can run multiple of them

  3. Untested. Back then I don’t remember seeing a single application generator which comes with a comprehensive test suite (which would help addressing previous points)

  4. Hard to maintain. It is really hard to maintain an application template as you extend it because they lack structure

But most importantly, I think application templates most often miss the point: what are the odds that you need to execute something exactly when the application is being generated? For example, take mix phx.gen.auth, it could be designed as an --auth flag to phx.new but it is just so much better as its own standalone project.

So what about Tailwind support on Phoenix if it is not part of Phoenix? How would someone approach that? Easy:

$ mix archive.install hex phx_new
$ mix archive.install hex phx_new_tailwind
$ mix phx.new foo
$ cd foo
$ mix phx.new.tailwind # change everything required for tailwind support

While this approach doesn’t necessarily address point 1, because this type of code will always be brittle, it pretty much does better in all other points mentioned above. The beauty of phx_new_tailwind is:

  1. Is its own project, with proper structure, and hopefully tests

  2. Has its own command, which means it has a proper place to document all of its options and behaviour (something you don’t get with application templates iirc)

I am sure this process is nowhere as fleshed out as Rails Application Templates, but I would rather use this approach as a starting point and improve it based on feedback than pick a solution that has many known pitfalls. In any case, it is 100% possible today.

This is not even specific to Phoenix… as the same can be achieved with Rails generators and it is pretty much the approach I preferred back then: devise and simple_form used rails devise:install and rails simple_form:install commands instead of templates. I remember maintaining some front-end based installers way back then too.

14 Likes

I’ve maintained one project for years. Funny enough it ended up being one of my most popular projects on GitHub. I think folks just like the idea of starting off with something instead of a blank slate. From a code organization POV I didn’t find it tough to maintain and it was pretty complex.

At the time I had integration tests too, basically running the CLI tool to generate the app and then run assertions on that generated app to make sure certain things existed. It also even ran the test suite of the generated app to make sure all of its tests passed (which I had a lot of).

Was it perfect? No. Is it the best code ever written? Hell no. I ended up making this thing shortly after learning Rails and building a few real world apps. I figured rolling up all of those patterns I learned into something someone else can use to bootstrap their app quicker with a bit of opinions felt useful.

Totally, but there’s at least 1 missing link.

Right now everyone needs to go off and do their own thing, creating their own custom generators with no unified DSL for manipulating files. That’s why you have all of that generic file and project manipulation code tucked away in the gen auth project. IMO it would be much more beneficial to have that stuff available in an official package (or built into mix, etc.) that anyone can use for their own generators.

Agreed. I ran into this problem with orats even while working on only 1 template and eventually switched away from application templates to just having a skeleton of a project available, and a thin CLI wrapper to customize names of things.

Although Rails Bytes seems to be quite popular nowadays where the community can find and contribute templates.

Maybe the missing links isn’t so much Rails application templates for Phoenix as a direct port, but more so a really slick set of functions to CRUD text in an Elixir / Phoenix project (the DSL) to begin with?

At the end of the day, generating the initial project becomes a trigger to execute the application template. Technically another trigger could do this, but it feels natural to happen during the generation of a new project.

I think running commands is also a key component besides the functions that give you a nice DSL to CRUD files because think back to your Devise gem. It had a generator that would insert files into your project.

A Rails application template could get triggered from creating a new project, add devise to the gemfile, install everything, run the devise generator and edit the files that generator created. This only becomes convenient and possible when you have access to both the DSL for CRUDing files and a means to run commands.

2 Likes

:+1:

Exactly. I would 100% prefer to focus on tackling this problem than providing templates as a whole. Otherwise I expect anyone using templates in anger to run into the same issues as you and eventually move away.

Mix does ship with a Mix.Generator module but its API today is tiny. We definitely can augment it but I also suspect a high-level API that knows about Phoenix specific constructs would be necessary.

1 Like

Wonderful! This phx_new_tailwind may need to pin itself against versions of Phoenix to ensure compatibility since Phoenix generates with foo (for example Webpack) by default, but I don’t see that as a huge problem.

The unfortunate part about separate projects like this is that it requires more instruction to the user and has less visibility.

  • re: visibility: If there is a family of generators like this, it would be helpful to have a section on the Phoenix readme/guides that points to these generators since they’re potentially important to the user when starting a project, so they don’t run mix phx.new before they realize these other starters are available.

  • re: user instruction: if the user is expected to run mix phx.new first and then mix phx.new.tailwind, then that’s adding more friction to these generators that flags like --live don’t have. I’m not suggesting those flags also be separated; just saying that experience is nicer to new users. An alternative is to have the user only run mix phx.new.tailwind, which passes original flags onto mix phx.new and then proceeds to add its own templates and adjustments.

If you have implementation suggestions, I’d love them. Like I said in the original post, I’d love to work on this feature. Thanks for your thoughts!


separately, I found a need to see the diffs between generated Phoenix projects. Anyone know of a tool that shows these? Something like http://railsdiff.org. I’d like to add support for these in https://diff.hex.pm somehow.

2 Likes

Whether we use tailwind itself has other considerations for the team which we are still exploring, so hang tight for now! :slight_smile:

From a maintenance perspective alone on our side, I encourage folks to go the separate project route if they want to ship specialized generators. I don’t foresee us going an extensible template route in any near term future.

3 Likes

I think that would go a long ways and yes, having knowledge about Phoenix would be very helpful. Although I’m not sure how that would pan out in the end for both end users and generator authors.

For example earlier you mentioned this workflow:

$ mix archive.install hex phx_new
$ mix archive.install hex phx_new_tailwind
$ mix phx.new foo
$ cd foo
$ mix phx.new.tailwind # change everything required for tailwind support

As an end user that’s a few extra steps to add Tailwind to a project. Realistically it’ll probably be more steps because I would guess that most folks would install phx_new_tailwind as a dependency of their existing project, generate the files and then remove the dependency since all it did was produce files in a specific location.

I mostly agree with you that despite Bytepack existing, realistically I can’t see anyone cherry picking 15 tiny isolated templates and having them all play nice together with little to no user intervention. There’s just too many conflicts about where things may or may not exist in a file once you have 10 other things writing to that file that you have no control over.

And that leads full circle back to having pre-created applications that have whatever opinions you want baked in, and you take it or leave it with no generators. This works and it’s what I do but it also has its own set of problems like wanting to customize the names of things, so you end up with some crazy shell script to do find / replaces in a bunch of files and directories.

I’ll admit it’s not an easy problem to solve, and tailwind support is just an introduction to the idea of wanting to quickly add custom optional features to an existing base application.

1 Like

My 2 cents on that is:

Most users do not start projects which needs so many customisations from day one (and if they do, then I would say, they do something wrong). In a lot of cases people will do mix phx.new once, and then they will go with the project, because let’s be honest, it is not that often that you start new projects from the ground up. For sure it is less often than adding features to existing projects.

For example, I am using completely different approach for naming my controllers and stuff (instead of MyAppWeb.FooController I use MyAppWeb.Controllers.Foo to follow naming convention used almost all other Elixir projects) which requires me to do “few” manual steps. I do it by the hand each time, and TBH I never had a problem with that, because in last year I needed to do it twice. Oh, and by the way, I am not using Webpack at all, so I need to change it to Parcel as well, so this is quite some time for preparing that. Having such generator maybe would be useful, but I do all of that so rarely, that https://xkcd.com/1319/ and https://xkcd.com/1205/ describes why it is not worth my time.

2 Likes