matheus23 / elm-tailwind-modules

Generate Elm code for Tailwind Utilities and Components that purges using Elm's dead code elimination!
https://matheus23.github.io/elm-tailwind-modules/
117 stars 12 forks source link

Large number of generated declarations with Tailwind V3 #12

Closed dillonkearns closed 1 year ago

dillonkearns commented 2 years ago

Problem

The number of generated declarations in the elm-tailwind-modules output with Tailwind V3 increased significantly compared to V2 (see #11). This is large enough to cause problems with tooling, including editors and IDEs, as well as static analysis tools like elm-review. For the tl;dr, see the takeaway and workaround section at the bottom.

With a vanilla tailwind.config.js file, V3 is generating 23 times as many declarations in Utilities.elm as V2 did (86203 total in V3, compared to 3721 in V2).

I created a spreadsheet comparing the generated declarations in Utilities.elm in V2 compared to V3. What I found is that the bulk of the larger size is due to color-related styles. Of the total generated definitions in V3 output, 80875 (98%) have colors in the name, 1607 (2%) do not.

If I change the color palette to only have a single color in the tailwind.config.js theme, the V3 code has only 4461 generated definitions.

Breaking Down the Increase in Color Utilities

I took all of the utilities related to colors and broke them down using Bash Brace Expansion syntax.

A few notes about the syntax:

{accent,bg,border_b,border,border_{l,r,t,x,y},caret,decoration,divide,fill,from,outline,placeholder,ring,ring_offset,shadow,stroke,text,to,via}_indigo_{100,200,300,400,500,50,600,700,800,900}{,over{0,10,100,20,25,30,40,5,50,60,70,75,80,90,95}}
{bg,border,divide,from,placeholder,ring,ring_offset,text,to,via}_indigo_{100,200,300,400,500,50,600,700,800,900}

This Brace Expansion syntax is handy because you can experiment with how adding or removing variants changes the total number of generated utilities.

echo {accent,bg,border_b,border,border_{l,r,t,x,y},caret,decoration,divide,fill,from,outline,placeholder,ring,ring_offset,shadow,stroke,text,to,via}_indigo_{100,200,300,400,500,50,600,700,800,900}{,over{0,10,100,20,25,30,40,5,50,60,70,75,80,90,95}} | wc -w
3680

# if we remove the "over..." variants, the number drops significantly

echo {accent,bg,border_b,border,border_{l,r,t,x,y},caret,decoration,divide,fill,from,outline,placeholder,ring,ring_offset,shadow,stroke,text,to,via}_indigo_{100,200,300,400,500,50,600,700,800,900} | wc -w
230

Takeaway

The workaround below will likely be good enough for many users, especially since opacity modifiers aren't commonly used so you can probably turn them off without noticing it in your usage (see below workaround section).

Ultimately, the ideal fix for this would be to inline some of these modifiers as arguments rather than additional generated permutations of declarations. I think that's one of the most powerful features of the elm-tailwind-modules approach. It's really ergonomic, and makes the generated code much more manageable for both users and tools when things are turned into parameters instead of permutations because it avoids combinatoric explosions, and it's generally more readable.

To get a sense of how it would change the generated number of permutations, let's look at some brace expansion examples. We can get the number of permutations when we parameterize something by turning a brace expansion expression into a literal string (I've uppercased them to make them stick out).

# current state
echo {accent,bg,border_b,border,border_{l,r,t,x,y},caret,decoration,divide,fill,from,outline,placeholder,ring,ring_offset,shadow,stroke,text,to,via}_{black,white,slate,gray,zinc,neutral,stone,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky,blue,indigo,violet,purple,fuchsia,pink,rose}_{100,200,300,400,500,50,600,700,800,900}{,over{0,10,100,20,25,30,40,5,50,60,70,75,80,90,95}} | wc -w
   88320

# parameterize color
echo {accent,bg,border_b,border,border_{l,r,t,x,y},caret,decoration,divide,fill,from,outline,placeholder,ring,ring_offset,shadow,stroke,text,to,via}_COLOR_{100,200,300,400,500,50,600,700,800,900}{,over{0,10,100,20,25,30,40,5,50,60,70,75,80,90,95}} | wc -w
    3680

# parameterize opacity
echo {accent,bg,border_b,border,border_{l,r,t,x,y},caret,decoration,divide,fill,from,outline,placeholder,ring,ring_offset,shadow,stroke,text,to,via}_COLOR_{100,200,300,400,500,50,600,700,800,900}OPACITY | wc -w
     230

# parameterize color strength
echo {accent,bg,border_b,border,border_{l,r,t,x,y},caret,decoration,divide,fill,from,outline,placeholder,ring,ring_offset,shadow,stroke,text,to,via}_COLOR_STRENGTHOPACITY | wc -w
      23

So by parameterizing these values, the vanilla configuration could generate up to 3840x fewer declarations.

Workaround

The largest source of the increased number of generated declarations are these opacity modifiers for colors. You can significantly reduce the size by turning these off in your config.

module.exports = {
  theme: {
    // without this line to turn off opacity variants,
    // we get 86203. With this line below, it's reduced to 9148
    opacity: {},
  },
  variants: [],
  plugins: [],
};
leojpod commented 2 years ago

As a workaround you can also use safelist to target the rules you're using, so you would target only the "basic" bg, and then add the opacity modifiers you need

matheus23 commented 1 year ago

The situation has improved drastically with the new 0.5.0 alphas, now that we've built in auto-abstracting color utilities.