arcanemachine

arcanemachine

Do you prefer to use Tailwind in your Phoenix projects? (Poll)

This was the first question on my mind when I saw this other thread by @AstonJ.

I’m curious how many Phoenix users actually like using Tailwind.

Do you prefer to use Tailwind in your Phoenix projects?

  • Yes
  • No
0 voters

Most Liked

Eiji

Eiji

I don’t like it for a few reasons:

  1. It’s forced by default in a way it’s not easy to remove. Standalone it would be not a big deal if we talk only about a single generator, but if we have to rewrite each generated template then we simply waste time and it’s against Elixir’s 10x rule where we focus on writing the app’s logic.

  2. .css files is not some kind of an old standard that’s currently useless - it exist for a reason. Styles are supposed to be separated from templates and not be part of them. If that would be the case then we would use style attribute instead of class for Tailwind. The Tailwind is overusing class attribute making it harder to read especially for people that does not know all of it’s shorter naming.

  3. I’m definitely ok with an idea to keep template code and it’s styles in the same place as I said in another post, but same place (with extra macro above template function or so) does not mean part of template. This makes templates code longer and therefore harder to read. Think if someone would not format templates and would put all element attributes in same line with class as a first attribute. This way we would have to scroll the code just to find things like phx-click which sounds more like anti-pattern than a standard.

  4. Tailwind defines a shortcuts for styles. It does not introduce anything besides it. It would be nice if above template function there is a single line macro, but in a more complex case it’s just a long string of abbrevations our brain has to parse over and over again. It’s not using advanced CSS and SCSS rules like nesting, variables, counters, scopes and many more. It’s kind of pre-generated CSS abbrevations you have to configure and a framework that forces you a way of writing your code. It’s more like a framework that forces you to do things in specific way rather than a library that gives you useful tools.

  5. I remember that José Valim mentioned that the environment could be simplified (by getting rid of a node staff and so on) and it really happened and then they have added their own dependencies (Tailwind, DaisyUI). I would prefer to see Phoenix templates using SCSS features and variables that we can simply reuse in our code rather than what we have right now. The DaisyUI could be replaced with variables and a simple Colocated Hook, that saves theme to LocalStorage. Not only it would be a very good for learning purposes by using existing LiveView patterns, but also it would not be a theme framework that works over other framework.

  6. Tailwind may have compatibility bugs. It happened at least once and it was mentioned some time ago on forum for old macOS version. Neither CSS nor SCSS causes such a problems. Such problems are unacceptable for me. I don’t want that a JS file would not work, because I have too old or too new hardware..

I just wanted to explain that Tailwind and DaisyUI projects solves some problems in a way that does not fits Elixir/Phoenix/LiveView well. SCSS mixins and other functions are more than enough to make styles much shorter. esbuild for JavaScript and dart_sass for SCSS seams like more than enough dependencies we really need. The rest are some extra macros to make templates next to styles and a set of Phoenix variables, SCSS mixins and so on …

Please pay attention how SCSS extends CSS and how Tailwind limits CSS. There are many things that Tailwind needs a workaround to make some things working which just means more short naming than ever needed. If you ever wrote a CSS do you really feel a need to make @media query any shorter? What about a new CSS features that would be not supported until Tailwind would introduce another workaround for them? What’s the reason of using a terrible long class attribute if at the end in complex styles you still write your own CSS?

Tailwind was never an option for me. It’s not about how good or bad it really is. It’s about the way you want to write a code. It’s not like that everyday you fill a need to write some OOP code in Elixir, right?

12
Post #3
garrison

garrison

Separating the styles from the markup was always a terrible idea. There was a notion that you could mark up your documents “semantically” with elements and then re-use styles across documents and documents across styles. This has systematically failed. Nobody does this.

So instead you’re just left cross-referencing a giant CSS file with a giant set of HTML templates. Total mess. Zero benefit. Bad idea.

In fact, just using the style attribute would be a wonderful solution, except for the fact that it sucks. It’s ancient and it wasn’t designed for this.

The correct way to do composability and re-use is not to apply the same styles to different elements but to group elements and their styles into components and compose there. This is what everyone does now. We do it with Phoenix too.

Tailwind, as a library, solves three problems:

  • Styles should be co-located with components
  • Styles should be composable (no name conflicts)
  • Design systems should work out of the box

Unfortunately, the utility class approach is fundamentally degenerate: it can never fully express CSS, and CSS is the thing which browser vendors actually support. So you are always playing catch-up.

The series of increasingly ridiculous hacks they have come up with to fit CSS into a class-shaped hole is something to behold. First they needed a compiler to strip the unused classes. Then they started dynamically generating styles at runtime with a made-up class syntax. So then they had to start shipping binaries just to compile CSS, and now they’ve even had to introduce a new Rust codebase to keep things performant.

There is a much simpler solution: web frameworks are already responsible for providing a component framework (we have HEEx). They should also be responsible for providing a scoped CSS implementation to go with it, and an extensible design system out of the box.

(See my comment in the other thread (linked in the OP) for an example of how this can be done.)

10
Post #5
hauleth

hauleth

I do not see the value of Tailwind at all. It still requires me to learn CSS, but on top of that I also need to learn their own custom nomenclature full of abbreviations and custom units. If I need to do that, then why not just simply learn CSS and skip all the Tailwind magic?

When I do p-7 what is that 7 mean (or maybe that is -7? Who knows)? How does it relate to something like size-48? If I want to have some colour to stuff I need to use grey-700? How does that relate to grey-300 or orange-300? If I do grey-700 on purple-300 will it have enough contrast or what?

The numbers, Mason. What do they mean?

Modern CSS also adds a lot of useful stuff that provides a simple way to avoid most of issues that people had previously.

There are even more upcoming things like typed attr() CSS function, which will allow me to use just HTML attributes for customisation if needed.

In addition to above - with semantic CSS I can style using ARIA attributes, so it will “force” me to implement proper a11y features from day one, and when I come back to the code, I can read what does the values mean instead of deciphering arcane shortcuts.

<form>
  <input type="text">
  <button type="submit">Action</button>
</form>

Vs

<div class="container display-flex my-md mx-sm">
  <form class="form shadow-md my-md mx-sm align-center">
    <div class="input-wrapper border-radius-sm">
      <input type="text" class="input text-color-gray placeholder-color-light-gray focus-outline-blue">
    </div>
    <div class="button-wrapper border-radius-sm">
      <button type="submit" class="button bg-color-blue text-color-white focus-light-blue hover-light-blue">
        Action
      </button>
    </div>
  </form>
</div>

Honestly - TailwindCSS, with all tooling that is created around it, seems like designed for AI slop and to move complexity around (and IMHO in the wrong direction).

And now, whenever I start new Phoenix project, it is yet another thing that I need to go through codebase and remove. That is becoming so painful, that I probably will need to sit and just write my own generator, because “default one” became so full of pointless cruft.

garrison

garrison

This is exactly the same attitude I was referring to above. I do not understand this insistence on refusing to study the good parts of a popular solution. You are essentially just telling people “I don’t care about the problems you have” and then are surprised when they ignore you and use the thing that solves the problems they have!

Here are the problems, again:

  1. Styles should be co-located with components
  2. Styles should be composable (no name conflicts)
  3. There should be a configurable design system that works out of the box

Let’s now review how modern CSS solves zero of these problems:

  1. There is no way to co-locate styles with components (the style attribute is bad)
  2. Nesting does not solve the naming problem and also still does not compose properly (think about what would happen if you used HEEX slots)
  3. Lol, lmao even. The only genuine improvement here is oklch, and that’s a stretch.

It’s literally just a scale. It’s configurable! What does rem mean? How many physical pixels are in a logical pixel?

They are just color scales, chosen by hand to look decent as it turns out that used to be really hard. Maybe we can finally move on from this nightmare with oklch, though. But this has nothing to do with Tailwind, many do the same thing with variables (I do).

This is nonsense, Tailwind became mega-popular well before the LLM apocalypse.

Agreed. But does --no-tailwind not work anymore? I haven’t generated a Phoenix project recently.

frankdugan3

frankdugan3

First, I feel like a few things need to be clarified about Tailwind:

  • Tailwind prefixes however you like: @import "tailwindcss" prefix(tw); <div class="tw:flex">
  • As of v3, it no longer uses tree-shaking. Utilities are only added when detected being used
  • As of v4, it uses native CSS layers, so it’s very amenable to using alongside semantic CSS as a utility class library
  • As of v4, all of the utilities are based on native CSS variables. You can add to these or selectively include what you like. This makes it very easy to leverage in semantic CSS.
  • When searching the documentation, p-4 immediately refers to the padding section as the first result. Using a full class will be much more successful in terms of results.
  • Reverse-searching is also pretty reliable: padding will bring you directly to the padding section as the first result.

The v4 release of Tailwind undid a lot of the weird Tailwind magic, and now it’s a small handful of directives and the rest is normal CSS. As long as you don’t use @apply, it’s actually very easy to migrate a project away from Tailwind simply by copying in the variables and utilities from your last build.

I find Tailwind convenient, if used in a more CSS-centric way than the Tailwind authors recommend. I feel the following value is provided:

  • A standard suite of color, spacing and size variables
  • A standard suite of utility classes
  • A fast, standalone preprocessor
  • Easy customization of built-in themes, utilities, etc.

The value of the above is magnified by the fact the majority of developers are already familiar with these standards.

I definitely agree with the distaste for piling all the utilities into the class attributes. Although that’s what’s recommended, with v4 there’s no real reason to do that just to use Tailwind. Here’s a simplified version of how I like to use Tailwind.

def simple_form(assigns) do
  ~H"""
  <form class={["core-simple-form", @class]}>
    <!-- ... -->
  </form>
  """
end

:root {
  // Synchronize CSS with Tailwind's dark mode -- works with any dark mode strategy
  color-scheme: light;
  @variant dark {
    color-scheme: dark;
  }
}

// Using this layer preserves full functionality of utility class overrides
@layer components {
  .core-simple-form {
    display: grid;
    padding: calc(var(--spacing) * 4);
    background: light-dark(var(--color-zinc-50), var(--color-zinc-950));
  }
}

As mentioned earlier, native CSS @scope or simply very specific selectors can easily be used to handle scoping component selectors from leaking into slots.

Now, I can use the generic form component in a particular place, and have the value of one-off utilities. In particular, breakpoints:

<.simple_form class="lg:grid-cols-2 2xl:grid-cols-3">
  <fieldset class="col-span-full" field={@form[:wide]}>

I have found this to be a very productive way to do things. The reusable components have clean CSS, and I can keep the one-off styling neatly in the component with a handfull of classes.

Regarding co-locating the CSS, the hooks/JS implementation uses a generic MacroComponent. It’s currently undocumented because I think the API and some details are still under revision. Once it’s ready for prime-time, we will be able to easily implement CSS colocation via a MacroComponent of our choosing. Personally, I don’t mind writing my classes for components in a CSS file all that much. I also don’t find much difficulty with naming component classes. Some variant of module + component name is pretty effective for namespacing in any app I’ve worked on (ERP, E-commerce, etc.) That said, a macro component can solve both of those problems in any way the developer sees fit.

I’m not really interested in convincing anyone whether to love/hate use/don’t use Tailwind. It’s a tool, I can work with and without it. Just sharing what I’ve found convenient and useful.

TLDR: We can have both semantic CSS and Tailwind, and it’s actually pretty ergonomic IMO.

Where Next?

Popular in Polls Top

josefrichter
There are two polls and a third question… 1) Displays poll 2) Orientation poll 3) Configuration Please let us know how you are using you...
New
AstonJ
What is your primary dev OS? And is there any software you’d like to recommend that could make development easier? (Apart from the code e...
New
AstonJ
I think I’ve used LittleSnitch since my first ever Mac - does anyone else use it or whatever the equivalent is on your OS? What does it ...
New
spicychickensauce
There are many v0 libraries in the elixir echo system, where at least officially, semantic versioning means nothing. However, it seems t...
New
AstonJ
Following on from a conversation in the Tidewave thread - how useful do you think AI dev tools are right now and how useful do you think ...
New
AstonJ
Polls are a great way to easily get a snapshot of things, and we’ve had some really interesting polls over the years! Here are some tips...
New
ryanswapp
I think it would be interesting to see how most people are using Phoenix in production. Do you use it as an API backend for a SPA? Or do ...
New
AstonJ
After seeing some of the responses in this thread, I’m curious how much RAM you’re opting for and whether there is any specific reason fo...
New
axelson
I’m working on VSCode ElixirLs syntax highlighting, and different themes support different syntax definitions. So I am curious about whic...
New
AstonJ
The stereotype for ‘geeks’ is multiple monitors! But let’s see… poll
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New

We're in Beta

About us Mission Statement