aptinio
canonical_tailwind - Tailwind class canonicalization via mix format
I just published canonical_tailwind, a formatter plugin that canonicalizes Tailwind CSS utility classes in HEEx templates.
- mr-4 custom-btn flex ml-[1rem] flex
+ custom-btn mx-4 flex
It delegates to the tailwindcss CLI’s new canonicalize subcommand — the same engine that powers the Prettier plugin. So you get sorting, normalization, and duplicate collapsing powered directly by the Tailwind CSS engine.
It hooks into mix format via Phoenix LiveView 1.1’s attribute_formatters API and works with LSP format-on-save (tested with Expert).
Setup is two lines — one dep, one formatter config:
# mix.exs
{:canonical_tailwind, "~> 0.1.0", only: [:dev, :test], runtime: false}
# .formatter.exs
[
plugins: [Phoenix.LiveView.HTMLFormatter],
attribute_formatters: %{class: CanonicalTailwind},
]
If you’re already using the :tailwind hex package, it detects your binary and profile automatically. Make sure your tailwind version in config/config.exs is at least 4.2.2 and run mix tailwind.install after changing it.
Requires Elixir ~> 1.18, Phoenix LiveView ~> 1.1, and tailwindcss CLI >= 4.2.2 (the first version with canonicalize). The canonicalize --stream flag that makes this possible was merged in tailwindcss#19796.
Hex: canonical_tailwind | Hex
Docs: canonical_tailwind v0.3.0 — Documentation
Most Liked
aptinio
It was a long time coming. I first opened a PR for Phoenix LiveView asking the team to make HTMLFormatter.tokenize/1 public. José Valim came back with a better idea — attribute_formatters (phoenix_live_view#3781) — which gave formatters a proper hook into the template pipeline.
The initial implementation parsed Tailwind’s output CSS to derive the sort order. It worked, but I was never happy with it, especially with format-on-save enabled in an editor. The ideal solution was to hand the formatting off to Tailwind itself, and that became possible once the canonicalize subcommand landed. I contributed a --stream flag (tailwindcss#19796) so the CLI could stay alive as a long-running process, and after that the library was the easy part.
Hope you find it useful!
aptinio
Just shipped 0.1.5. The headline: if format-on-save just wasn’t working for you under Expert or ElixirLS, 0.1.5 fixes it.
Also tightened pool reliability: workers that go missing now recover (long-running LSP sessions used to break after a crash), cold start is serialized to avoid mixed-config workers, and config drift after pool start raises a loud error instead of silently using stale config (a small breaking change for umbrella setups that depended on the old quiet behavior).
And if you haven’t tried canonical_tailwind yet, this is a good moment. The canonicalizer in tailwindcss has gotten noticeably smarter over the past few releases: combining text-* with leading-*, collapsing matching width/height into size-*, modernizing calc(var(--spacing)*…) into --spacing(…), and now in v4.3.0, migrating [&:has(…)] to has-[…] and preserving significant whitespace and original units in arbitrary values. canonical_tailwind delegates to the user’s installed binary, so all of that lands for you the moment you bump :tailwind.
Changelog: Changelog — canonical_tailwind v0.3.0
Hope you find it useful!
aptinio
Just shipped 0.3.0. There’s a new ~TW sigil.
canonical_tailwind already canonicalizes class attributes in HEEx. But Tailwind classes often live in plain .ex code too: helper functions, module attributes, class-builder lists. A formatter can’t safely touch those on its own, since it can’t tell a Tailwind class string from any other string.
~TW lets you mark a string as a class list, so mix format canonicalizes it:
defp button_class, do: ~TW"px-4 py-2 inline-flex rounded-md bg-brand"
# becomes, on mix format:
defp button_class, do: ~TW"inline-flex rounded-md bg-brand px-4 py-2"
It works anywhere a string literal does. It’s opt-in per string, so you canonicalize the strings you want and leave the lists you’ve ordered by hand.
Add CanonicalTailwind to your formatter :plugins and import CanonicalTailwind.Sigil to use it.
Hope you find it useful!
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









