Large number of generated declarations with Tailwind V3
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:
- These are valid, executable bash expressions
- Some of these expressions are nested, which means big numbers (think nested for loops)
- Brace expressions starting with a bare comma means that it includes nothing as well as what comes after that.
- I didn't expand the indigo to different colors, but you can replace "indigo" with
{indigo,blue,green}or something like that to include colors from the color palette if you want to
{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: [],
};
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
The situation has improved drastically with the new 0.5.0 alphas, now that we've built in auto-abstracting color utilities.