Search code examples
csstailwind-csstailwind-ui

Applying an effect to multiple pseudo classes at once in tailwind


I am looking for a good shorthand to apply the same effect to multiple pseudo classes in tailwind.

For example, I may want to apply a blue background to both :hover and :focus states on a div.

Currently I'd have to write the following:

<div className="hover:bg-blue focus:bg-blue>Text<div>

OR, i could use apply to build out a custom class like this:

.hover-focus-bg-blue {
  @apply hover:bg-blue focus:bg-blue
}

But neither of these are great options when i have to apply complex states (in my current project I need to cover 11 states on one element (rest/hover/active/focus/focus-visible/focus-visible && hover etc).

The apply method only saves code if there are multiple uses of it.

What I would like to see is something like:

<div className="[hover, focus]:bg-blue">Text</div>

Does anyone know of some syntax like that? Can't find it anywhere.


Solution

  • This is a question that is asked frequently. Mostly it's about grouping classes into one grouped syntax (for example, hover:(bg-red-500 border-2).

    This is the answer I received after searching for a solution to this question. In short, this functionality doesn't exist for now. (Edit: It's possible to achieve now with some different tooling, see the end of the answer.) Even though this answer refers to the grouping of utilities into one selector, the same logic should apply to the grouping of pseudo-selectors:

    Wongjn's answer

    The Tailwind maintainers did look at this feature at one point but ultimately decided to put it on hold for now. See this Twitter thread: https://twitter.com/adamwathan/status/1461519820411789314

    So, as you can see from the answer and the Twitter feed, it's currently on hold. In the Twitter thread, the developers were testing this feature regarding performance, and it looks like this feature, at its current state duplicates CSS compared to using individual utilities:

    So we did a test where we converted every single Tailwind UI template (over 500 files) to use the grouped syntax to see how much bandwidth grouping would save you when serving HTML.

    This makes sense when you think about it, because using the grouped syntax (like focus:(font-bold,underline)) leads to fewer repeated symbols in the document, because there are now more unique class names.

    Using the non-grouped syntax, every instance of focus:font-bold can be compressed out and replaced with a short placeholder (say %). Using grouped syntax, focus:font-bold and focus:(font-bold,underline) can't be compressed out, because they are no longer the same. Plot twist: After compression, the files are actually bigger, not smaller!

    Bottom line:

    So the takeaway here is that although the grouped syntax looks like less code when you're authoring it, it actually creates both a bigger CSS file and a bigger HTML file in production, making it a very black and white performance anti-pattern.

    It's nicer to write though, and the performance cost isn't a huge one, so still a chance we develop it further just for the developer experience for the people who like it. But admittedly hesitant to encourage anything that's bad for performance.

    Update

    The disadvantage about the variant group resulting in larger CSS files only holds true if the CSS class in your HTML/JSX are simply left as is (e.g. focus:(font-bold,underline)). But if it was just a syntactic sugar shorthand that is unfolded into the full variant (focus:font-bold focus: underline) at build time, then for the bundle size it's the same as manually typing it out, so no disadvantage, while having the advantage of faster coding and more readable source code.

    As TailwindCSS is limited to purely generating CSS files, it cannot do such a transformation. However, there are new tools available such as UnoCSS that integrate with your build system (such as Vite or Webpack) to also inspect your HTML/JSX and unfold the syntactic sugar. Check out this playground to try it out.