zachdaniel/tails

Weird class mangling with alphabetical sort

ravensiris opened this issue · 7 comments

Describe the bug
Tails sorts the variants alphabetically causing the generated Tailwind CSS to not work.

To Reproduce
My custom variant:

plugins: [
  plugin(({ matchVariant }) => {
    matchVariant('nth', value => `&>*:nth-child(${value})`, {
      values: {
        '1+': 'n+1',
        '2+': 'n+2',
        '3+': 'n+3',
        '4+': 'n+4',
        '5+': 'n+5',
        '1-': '-n+1',
        '2-': '-n+2',
        '3-': '-n+3',
        '4-': '-n+4',
        '5-': '-n+5'
      }
    })
  }),
]

How Tails mangles the classes:

iex(1)> import Tails
Tails
iex(2)> classes("sm:nth-2+:hidden sm:nth-2-:flex")
"nth-2-:sm:flex nth-2+:sm:hidden"
iex(3)> classes("md:nth-2+:hidden md:nth-2-:flex")
"md:nth-2-:flex md:nth-2+:hidden"
iex(4)> classes("xl:nth-2+:hidden xl:nth-2-:flex")
"nth-2-:xl:flex nth-2+:xl:hidden"
iex(5)> classes("2xl:nth-2+:hidden 2xl:nth-2-:flex")
"2xl:nth-2-:flex 2xl:nth-2+:hidden"

Relevant CSS generated by tailwind for "sm:nth-2+:hidden sm:nth-2-:flex":

  .sm\:nth-2\+\:hidden>*:nth-child(n+2) {
    display: none;
  }

  .sm\:nth-2-\:flex>*:nth-child(-n+2) {
    display: flex;
  }

Expected behavior
The variant order would be preserved to match the CSS output.

Runtime

  • Elixir version: Elixir 1.15.7 (compiled with Erlang/OTP 26)
  • Erlang version: Erlang/OTP 26
  • OS: GNU/Linux
  • Tails version: 0.1.8

Additional context
I use a tailwind formatter in my project and it seems to swap nth-2-:md:flex to md:nth-2-:flex(always putting the breakpoint variants on lhs). Which makes Tails a bit difficult to please.

🤔 that is frustrating. We currently store variant in a map to handle merging properly, so the order on the way out is effectively random. The big question is whether or not the provided order needs to be preserved, or if there is a semantically correct order. For example, your code would be "wrong" if you wrote: nth-2-:sm:flex. So perhaps we should just sort the variants according to their position in a list, and put everything else after.

Also, if you have custom variants, you should configure them, have you done that?

config :tails, custom_variants: ["1+", ...]

Thanks for the quick response.

@custom_variants Application.compile_env(otp_app, :variants) || []

Seems like the key is called variants not custom_variants

Tried setting:

config :tails, variants: ["1+", ...]

and

config :tails, variants: ["nth-1+", ...]

And didn't notice anything changing. What is it supposed to do?

The order needs to be preserved for the generated CSS to work.
I don't think a tool like Tails should be making calls what is semantically correct order.
That should be an individual choice done either manually or by a formatter.

So the strange thing is that, according to the docs usually the order doesn't matter: https://tailwindcss.com/docs/hover-focus-and-other-states#ordering-stacked-modifiers

But sometimes it does. It feels like tails should merge sm:hover:class with hover:sm:class. Otherwise it would be pretty easy to accidentally not merge classes, right? Unfortunately, the "sometimes order matters" bit makes me think we don't have a choice.

And didn't notice anything changing. What is it supposed to do?

For this, it doesn't recognize, variants at the beginning of a class list unless they are in the variants list. So any custom variants should be added to that config.

I'll remove the sorting that we do for now, just a bummer that its going to make it easier to make certain kinds of mistakes.

The sorting of variants has been removed in main.