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
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

New
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
matt-savvy
I see some people adopt this convention when naming fields or variables where the value is a boolean. I personally see this as an antipat...
New
AstonJ
poll asdf Extendable version manager with support for Ruby, Node.js, Elixir, Erlang &amp; more Manage multiple runtime versions with a ...
New
matt-savvy
Now that the compiler is giving us some reporting about type mismatches, I’m wondering about people’s use of Dialyzer. For anyone on Eli...
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
How often do you buy a new dev machine? poll If there is any particular reason why you’ve settled on the frequency you have, please shar...
New
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
We last posted a poll like this in 2017 - here’s an updated version for 2022 - this one is slightly different.. You can select up to 3 -...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
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 31194 112
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

We're in Beta

About us Mission Statement