1

The two blocks behave differently when applying tailwind's "rotate(**deg)" and vanilla css "transform: rotate(**deg)". Please just hover the blue blocks to reproduce.

https://play.tailwindcss.com/Rgf2GJ6mim

Since I sometimes use css in @layer utilities to write nested styles, so could someone please help me understand this? Big Thanks!!

1 Answers1

0

Despite it looks like both examples do the same thing it's not quite true. Let's find out the difference. All classes in your example are same but the last one

hover:[transform:rotate(1020deg)] generates this

.hover\:\[transform\:rotate\(1020deg\)\]:hover {
  transform: rotate(1020deg);
}

while hover:rotate-[1020deg] this

.hover\:rotate-\[1020deg\]:hover {
  --tw-rotate: 1020deg;
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

Or if you fill in Tailwind variables with its values it all comes to comparison between

.hover\:\[transform\:rotate\(1020deg\)\]:hover {
  transform: rotate(1020deg);
}

// and

.hover\:rotate-\[1020deg\]:hover {
  transform: translate(0, 0) rotate(1020deg) skewX(0) skewY(0) scaleX(1) scaleY(1);
}

We're forgot about one VERY important class - rotate-0. It actually sets the starting point of CSS transition

.rotate-0 {
  --tw-rotate: 0deg;
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

Just remove rotate-0 from both of your examples and now there is no difference in transition. So what is happening?

It all comes in CSS transition from state 1 to state 2. (Let's remove last parts with skew and scale)

  • First example - from translate(0, 0) rotate(0deg) to rotate(1020deg)
  • Second - from translate(0, 0) rotate(0deg) to translate(0, 0) rotate(1020deg)

MDN says

The transform functions are multiplied in order from left to right, meaning that composite transforms are effectively applied in order from right to left.

See example: red square just rotating. Yellow - rotates but returns back to default position even on hover we do NOT changing translate property. We're assuming it will left the same but this is not how CSS transition works. When there are multiple transform occurrence the last one will override previous. That's why translate is not applied anymore on hover - we're "erasing" it. In order to fix it we need to keep translate on hover (blue example)

.example {
  width: 60px;
  height: 60px;
  margin: 40px;
  transition: 1000ms;
}

.example-1 {
  background-color: red;
  transform: rotate(0);
}

.example-2 {
  background-color: yellowgreen;
  transform: translate(100px) rotate(0deg);
}

.example-3 {
  background-color: blue;
  transform: translate(100px) rotate(0);
}

.example-1:hover {
  transform: rotate(45deg);
}

.example-2:hover {
  transform: rotate(45deg);
}

.example-3:hover {
  background-color: blue;
  transform: translate(100px) rotate(45deg);
}
<div class="example example-1"></div>
<div class="example example-2"></div>
<div class="example example-3"></div>

And that's exactly what happening in your example - you are missing translate function in compiled CSS and changing the default state of transformed object (it is not transitioning anymore - it just places the new state). We need to keep the order of the chaining functions in transform property to ensure everything will work as expected

So, few ways to fix it in Tailwind keeping initial state (rotate-0 class), both requires to change hover:[transform:rotate(1020deg)] class

  • First - add missing translate function - change class into hover:[transform:translate(0,0)_rotate(1020deg)]
  • Second - not so obvious - change --tw-rotate variable value, basically convert class into hover:[--tw-rotate:1020deg]

And finally as I said - just remove initial state (rotate-0) but sometimes it is not an option

See examples

It's not the best explanation but I tried to point you in some direction where the difference comes from

Ihar Aliakseyenka
  • 9,587
  • 4
  • 28
  • 38
  • Big thanks for your detailed answers, @Ihar. The taliwind team also confirmed here. https://github.com/tailwindlabs/tailwindcss/issues/9292. For users who are using tailwind for animation css, they need to be careful to check those tailwind classes. – fullstackneo Sep 12 '22 at 00:19
  • For someone who are tortured by tailwind. There is another trick I would like to share about tailwind class. "hidden" will always weigh over "block" because of the tailwind parse order, which mean you can not simply add "block" to display your "hidden" element. Unless you are using "!block","md:block" or "dark:block" which has more weight than "hidden" – fullstackneo Sep 12 '22 at 00:26