2

The problem:

class="hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-400 hover:text-white"

here you see, there is the same prefix repetition.
hover:foo hover:bar hover:hello hover:world hover:something hover:another

I want to know if is there a way to not write multiple times the hover: prefix?


The idea:

is do something like:

hover:(class class class class class)

with brackets or something like that, so all the classes inside the () will be like one class and automatically added to the hover:

I think this idea there is in tailwind but I don't know the syntax for that.


if is possible this solution needs to work also with all the other prefixes

enter image description here


simple example demo:

// not important, only for deleting the console.warn() 
console.clear();
<script src="https://cdn.tailwindcss.com"></script>

<body class="flex h-screen">
  <button class="m-auto p-4 rounded-md  bg-blue-200 transition hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-400 hover:text-white">
     hello world
   </button>
</body>

I saw all the docs, that is not talking about this concept: https://tailwindcss.com/docs/hover-focus-and-other-states#hover-focus-and-active

if there is someone experienced in this thing, it will be helpful!

stackdeveloper
  • 352
  • 2
  • 21
  • 1
    afaik those are just classes that happens to have an identifier containing the corresponding pseudoselector. Actually it's not possible to use the real pseudo selectors in inline styling but only on css rulesets. So that's just a "trick" and also reading the [tailwind docs](https://tailwindcss.com/docs/hover-focus-and-other-states#pseudo-classes) there's no mention on grouping them and the examples also show the repetition – Diego D Sep 06 '22 at 14:01
  • @diegod ok, yeah I saw all docs, it not talk about this concept at all. I think is impossible – stackdeveloper Sep 06 '22 at 14:04
  • 2
    yes I just tried to be as more comprehensive. The class attribute just can contain a list of classes determining to which class a given element belongs to .. and the real pseudo-selectors belongs to the rulesets addressing those classes. I previously talked about inline styling in a wrong way since defining classes for an element is not that. This further comment was pretty superfluos I just meant to be more exact. The question was legit but unfortunately the closest thing coming to my mind is crafting a js strategy that assigns the real tw classes using a grouping method – Diego D Sep 06 '22 at 14:08
  • I'm glad someone took the effort of writing the (good and successful) js strategy I suggested... at the same time I feel like I'm not sure that was the only way to achieve the same result because I'm actually no expert of tailwind and all of its possibilities. At some point I felt like I was not perfectly sure I was giving the right expertise to something I'm not perfectly aknowledged about and basing my comments on assumptions that maybe ignored some further truths I ignored. Just for the sake of records – Diego D Sep 06 '22 at 14:57

3 Answers3

5

like he said @diego in the comment, this is technically not possible with tailwind only.


tailwind framework alternative

maybe you use a tailwind framework like windiCSS https://windicss.org/features/variant-groups.html

that have this functionality:

<div class="hover:(bg-gray-400 font-medium) bg-white font-light"/>

javascript vanilla (simple script)

want tailwind only?

so I think that maybe we can create a simple JS script to solve this problem.

enter image description here

twHover();

function twHover() {
  // get only the elements that have the hover attribute
  let hoverEls = document.querySelectorAll("[data-hover]");

  // loop through the elements that have the hover attribute
  hoverEls.forEach((el) => {
    // we get the string inside the attribute
    // and then make it into a array
    let twHoverClasses = `${el.dataset.hover}`.split(" ");

    // loop through the classes inside the element's attributes
    twHoverClasses.forEach((className) => {
      // add the class for you `hover:className`
      el.classList.add(`hover:${className}`);
    });
  });
}
<script src="https://cdn.tailwindcss.com"></script>

<body class="flex h-screen">
  <!-- original -->
  <button class="m-auto p-4 rounded-md  bg-blue-200 transition hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-40 hover:text-white">original</button>
  <!-- with script -->
  <button data-hover="bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-40 text-white" class="m-auto p-4 rounded-md  bg-blue-200 transition">with script</button>
</body>

want more from the JS script?

  • also :focus, :lg, :sm, and so on.

use this:

// this can be any preudo class that tailwind can have
twPseudo("focus");
// if there is nothing as parameter, we use hover
twPseudo();

function twPseudo(pseudo = "hover") {
  // get only the elements that have the hover attribute
  let hoverEls = document.querySelectorAll(`[data-${pseudo}]`);

  // loop through the elements that have the hover attribute
  hoverEls.forEach((el) => {
    // we get the string inside the attribute
    // and then make it into a array
    let twHoverClasses = `${el.dataset[pseudo]}`.split(" ");

    // loop through the classes inside the element's attributes
    twHoverClasses.forEach((className) => {
      // add the class for you `hover:className`
      el.classList.add(`${pseudo}:${className}`);
    });
  });
}
<script src="https://cdn.tailwindcss.com"></script>

<body class="grid grid-cols-2 place-items-center h-screen">
  <!-- original -->
  <div>
    <h2 class="text-3xl font-bold text-blue-500 mb-4">original</h2>

    <!-- hover -->
    <button class="m-auto p-4 rounded-md bg-blue-200 transition hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-40 hover:text-white">hover</button>

    <!-- focus -->
    <button class="m-auto p-4 rounded-md bg-blue-200 transition focus:bg-blue-400 focus:-translate-y-2 focus:-translate-x-2 focus:scale-110 focus:shadow-2xl focus:shadow-blue-40 focus:text-white">focus</button>
  </div>

  <!-- with script -->
  <div>
    <h2 class="text-3xl font-bold text-blue-500  mb-4">with script</h2>

    <!-- hover -->
    <button data-hover="bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-40 text-white" class="m-auto p-4 rounded-md bg-blue-200 transition">hover</button>

    <!-- focus -->
    <button data-focus="bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-40 text-white" class="m-auto p-4 rounded-md bg-blue-200 transition">focus</button>
  </div>
</body>

enter image description here

also make sure to put the script code at the end of the page or inside a DomContentLoaded event


advantages:

  • less repetitive characters to type

enter image description here

more than 25 chars saved (only in your example)

  • multiple line attribute

enter image description here

As you can see you can write your classes in one line,
and the hover logic in another line. making it easy to debug.

  • works out of the box.
    just copy and paste, and call the function.
    with the correct parameter (focus, sm, lg, xl, 2xl) or without any parameter (will be hover)
// just call it at the end of the page
twPseudo();

Laaouatni Anas
  • 4,199
  • 2
  • 7
  • 26
  • your code surely correctly implements what I was suggesting and thanks for spending your time to get to such solution. At some point I was just having doubts I was not telling the whole truth since I'm not expert of tailwind and its framework, directives and composition strategy. By the way your solution surely gets there somehow. Of course as long as the classes get applied on document loads. – Diego D Sep 06 '22 at 15:00
  • I think this would not work actually using npm package. Tailwind looking for a full class names to compile them in CSS file. `hover:${className}` is not correct Tailwind class. I believe your example is working just because you have original button example with correct `hover:classes` - if you remove "original" button, styles would not be applied – Ihar Aliakseyenka Sep 06 '22 at 15:11
  • Really great answer. I still highly recommend a CSS-only solution for performance reason (blocking the main thread can be quite annoying with DOMContentLoaded). – kissu Sep 17 '22 at 00:52
4

You can just create a new class in a <style> block in your page or template. And then use @apply to use the needed tailwind classes. Like:

<style>
.mybutton {
    @apply m-auto p-4 rounded-md  bg-blue-200 transition
}

.mybutton:hover {
    @apply bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-400 text-white
}
</style>

Now, if you set the mybutton class on the button, the hover will also work.

You can also add these classes to the main css file of your project. This is not the preferred way of tailwind, though. See Tailwind documentation.

Gabe
  • 2,168
  • 2
  • 4
  • 16
  • I know the apply trick, but don't want to use it for the reasons on official docs: https://tailwindcss.com/docs/reusing-styles at the end of the page – stackdeveloper Sep 06 '22 at 15:05
  • @stackdeveloper you are correct, you commented while I was editing my answer to add that. But I also think that adding javascript to handle it is not preferable. With tailwind, you just have the hassle of a lot of classes, but the value it adds weights much higher I think. – Gabe Sep 06 '22 at 15:08
  • 1
    the problem is that I need to create an external CSS file every time, and also a new class name for every button. yeah cool idea, but the wrong thing is create a different file. I want to write everything on the same component – stackdeveloper Sep 06 '22 at 15:34
  • is there a way to do `
    `? with apply logic but only in the same component, in a tailwind, if yes I will upvote because using javascript can slow the website and you are right (but for now seems the easiest one because I only need to copy the script and call it and that it)?
    – stackdeveloper Sep 06 '22 at 15:47
  • thanks. now I am using a js framework called svelte, and your way seems the better one in that case. – stackdeveloper Oct 02 '22 at 15:40
0

For people using react who stumble upon the question, there is a better approach for the following:

const pseudoJoin = (selector, str) => {
  return selector+":"+str.split(" ").join(" "+selector+":")
}

Now you can call it anywhere like:

<div className=`${pseudoJoin('hover','classes you want on hover')} some more classes`>Hello World!</div>

Or when you are using classnames framework:

<div className={ classnames(
   pseudoJoin('hover', 'classes you want on hover'),
  "Other classes here"
)}>Hello World!</div>
Blind Spot
  • 191
  • 10