dbern

dbern

I heard on a podcast episode on Thinking |> Elixir that there could be some TailwindCSS support when generating new Phoenix projects.

https://twitter.com/j_tormey/status/1334168111675809792

I’d love to work on this feature, but before I start slinging code, I’d like some buy-in from the Phoenix team. Are there any thoughts on how Tailwind could be included in a new Phoenix project?


Should this be the default for new Phoenix projects, instead of the included Milligram CSS? I like how Milligram is super easy as a drop-in without really any work to pull it out if you wanted to use a different CSS framework. Literally delete the phoenix.css file and you’re done.


Should this be opt-in via a --tailwind flag on the phx.new generator? I’m inclined to go this way, since including Tailwind also has some workflow changes, and several files. Adam Wathan seems to suggest avoiding using Sass when also using PostCSS to avoid some glitches; so I’m also thinking that this --tailwind flag would also not include sass by default and instead setup Sass-like features through PostCSS

  • postcss-import
  • postcss-nested

other thoughts? Also heads up, there’s an in-progress guide for integrating TailwindCSS into Phoenix from the tailwindcss.com site

Showing Posts 34 to 25

niccolox

niccolox

feels more and more like a phx generator contrib pattern requirement

sasajuric

sasajuric

Author of Elixir In Action

I used to think that too until I started consulting an agency, and what I saw made me change my minds. My clients start new projects fairly frequently. They want to keep the different projects technically as similar as possible to each other (language, frameworks, libraries, CI/CD). When I started working with them they have already built their own custom generator which expanded on phx.new generating some custom stuff (like e.g. deploy pipeline).

One of my main tasks has been introducing common Elixir style & practices to their projects, with the purpose of assisting with project switches. A company-wide custom generator has been indispensable in making this happen, so I’ve spent significant amount of time expanding it. Examples of simpler things we’re doing include generating our own custom credo configuration, or setting up default CI checks (formatter, credo, dialyzer, migrations reversibility, OTP release).

In addition, the generator also performs some more complicated changes on top of phx.new, such as moving db/endpoint configuration to init callbacks, renaming some files, most notably everything under the web folder, together with renaming corresponding modules. Changing some configurations in config scripts, etc. Such changes are made using a hacky combination of regex search & replaces, file operations (e.g. rename), or in some cases by completely overwriting the generated files.

It all feels fragile, and I sometimes wonder if we should completely part ways with phx.new and generate everything ourselves. However, the main challenge is that it’s unclear what exactly should be done to add different layers of Phoenix to the existing non-Phoenix project. Last time I was doing that, I invoked mix phx.new my_existing_project in /tmp, then copied that over the existing project, and carefully analyzed git differences, which was far from perfect. Moreover, once such generator is built, I fear that upgrading it to the next Phoenix is going to be much harder.

A comprehensive step-by-step guide explaining how to add Phoenix to existing project to existing project might be of great help here. I’d expect such guide to cover various scenarios, starting with the basic API, and then expanding with HTML, LiveView, and webpack, and of course including recipes for std configuration (e.g. dev-only live reload, debug errors, etc.). Understanding the changes between two Phoenix version could then amount to diffing the guides, which should be easier to comprehend than diff of the generator project.

Other than that, I’m not really sure what kind of support could be offered by the core generators to simplify the kind of changes we’re doing.

srowley

srowley

This is what I imagined too, sort of. I imagined that someone would run phx.new and then run a series of tasks that had been installed locally, e.g.:

mix phx.new my_app --live
cd my_app
mix phx.add.tailwind
mix phx.add.alpine

I wrote up something last night just to see how this might work. I put in on GitHub just as a talking point (not as an example of what I would recommend or a good implementation underneath).

Note I specifically called it “add” and not “gen” only to distinguish it from generators that are part of Phoenix proper or otherwise “official.”

Thinking about this further, I might instead want something like:

mix phx.custom my_app  --live --tailwind --no-ecto --alpine

Where this task parses the options, runs phx.new with the applicable options, and then runs locally archived tasks similar to the first example for the other options. Of course phx.new could do the same thing at some point.

That preserves access to the options that were specified when the stock app was created, which can be helpful. For example, there are some changes that need to be made to app.js for Alpine to work in Live View apps, that aren’t necessary otherwise. It would also give all of the custom generators access what other generators were included and in what order. Theoretically then, if you knew that a generator conflicted with another one, you (or the generator author) could handle that. Which is not to say it isn’t still all brittle.

One problem with this is for generators that have their own arguments (like phx.gen.auth). That could be messy. I suppose you could use prompts as one solution, or you could allow a file to be specified with a list of custom generators and installation switches. I don’t know that I love either of those ideas.

outlog

outlog

although I’m a fan of including tailwind, I also acknowledge the criticism… and really like the “gen” idea..

to further complicate the “gen” idea - I feel like it would be also great to have gen “heroku”/“buildpacks” (buildpacks used by various paas platforms,dokku etc), and a gen “docker” (and possibility for others, even react/vue etc down the line)

or the very least include those future requirements, in however the gen “tailwind” solution is crafted..

wojtekmach

wojtekmach

Hex Core Team

Hmm, I thought mix phx.new.tailwind would use the phx.new task under the hood but I misunderstood the intention. If it would work that way, though, fwiw we’d have one less step: (not that I personally think having multiple steps is a big deal)

$ mix archive.install hex phx_new
$ mix archive.install hex phx_new_tailwind
$ mix phx.new.tailwind foo
$ cd foo

Archives can’t have deps so we need to explicitly first install phx_new. Again, since we can’t have deps we can’t pin to a specific phoenix version either, however I suppose phx_new_tailwind could vendor a specific phx_new version for maximum compatibility in which case we’d have:

$ mix archive.install hex phx_new_tailwind
$ mix phx.new.tailwind foo
$ cd foo

If the idea for the tailwind generator is to be used on an existing app, I’d consider calling it mix phx.gen.tailwind. A very subtle difference but perhaps worth making.

wojtekmach

wojtekmach

Hex Core Team

Regarding generators, there’s perhaps one more piece of context worth adding and a distinction to be made. We have:

  1. application generators like mix phx.new or the potential new mix phx.new.tailwind
  2. phx.gen.* generators, like mix phx.gen.html, mix phx.gen.live, etc.

The latter happen to be configurable to some extent, by default they read templates from Phoenix’s priv/templates directory, but if your app has it’s own priv/templates, that’d take priority. Thus, you can customise the template files like this:

$ cp -R _build/dev/lib/phoenix/priv/templates priv/
$ open priv/templates/phx.gen.live/index.html.leex
$ mix phx.gen.live Blog Post posts title:string # uses your local overrides

I couldn’t find this behaviour documented anywhere and thus there’s probably no explicit contract between mix phx.gen.* and the respective templates, so there’s no compatibility guarantee going forward. However, if there’s a mix phx.gen.tailwind task, that is pinned to a specific Phoenix version, it could generate priv/templates that are augmented with tailwind related changes if that’d be useful.

hpjm

hpjm

I love Tailwind, and use it a lot, but I’m not sure that this proposal is a good idea. I realise that there are lots of other folks who would prefer to use Bulma, or Bootstrap or [insert CSS framework here].

I’ve also set up Tailwind with Phoenix, and it really isn’t that difficult if you know your way around Webpack. In fact all you need is the default how to on the Tailwind page.

hauleth

hauleth

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 xkcd: Automation and xkcd: Is It Worth the Time? describes why it is not worth my time.

cnck1387

cnck1387

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.

chrismccord

chrismccord

Creator of Phoenix

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.

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews