I wanted to declare a @utility
called animate-repeat-{number}
, whose function is to modify a variable at usage time; a variable I tie to the repeat count in all my animations. This works.
@utility animate-repeat-* {
--animate-repeat-count: --value(integer);
}
@utility animate-repeat-infinite {
--animate-repeat-count: infinite;
}
However, when I declare the animation in @theme
(or if it exists by default, like animate-bounce), the .animate-bounce { ... }
class receives the animation via a variable:
.animate-bounce {
animation: var(--animate-bounce);
}
The problem here is that even though I override the value of --animate-bounce
, DevTools (F12) still highlights it as bounce 1s infinite
instead of bounce 1s var(--animate-repeat-count, infinite)
.
Not working example with @theme |
|
---|---|
Not working example TailwindCSS v4 Playground (with @theme ) |
|
![]() |
![]() |
If I declare the animation in @utility
, the issue is that the default animation from @theme
takes precedence. Overriding it with !important works fine.
Naturally, without !important
, the theme
layer is stronger, and I can't override it; that's why it works with !important
.
Not working | Working with important |
---|---|
Not working example TailwindCSS v4 Playground (without important) | Successfully example TailwindCSS v4 Playground (with important) |
![]() |
![]() |
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@utility animate-bounce {
animation: bounce 1s var(--animate-repeat-count, infinite) !important;
}
@utility animate-repeat-* {
--animate-repeat-count: --value(integer);
}
@utility animate-repeat-infinite {
--animate-repeat-count: infinite;
}
</style>
<div class="p-10 text-center">
<button class="mx-auto animate-bounce bg-green-300 px-8 py-2 animate-repeat-2">
Bounce animation repeated 2 times in 1s.<br>(It only works with !important because utilities are weaker than the theme layer.)
</button>
</div>
How can I make the animation declared in @theme
work correctly by default, without having to use !important? I would expect the following code to work the same way as the last one from the three shared playgrounds:
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@theme {
--animate-bounce: bounce 1s var(--animate-repeat-count, infinite);
}
@utility animate-repeat-* {
--animate-repeat-count: --value(integer);
}
@utility animate-repeat-infinite {
--animate-repeat-count: infinite;
}
</style>
<div class="p-10 text-center">
<button class="mx-auto animate-bounce bg-green-300 px-8 py-2 animate-repeat-2">
Bounce animation repeated 2 times in 1s. (Not working)
</button>
</div>
You could consider using @theme inline
. This ensures the --animate-repeat-count
is resolved from the element where the animate-bounce
class name is used on.
<script src="https://unpkg.com/@tailwindcss/browser@4.1.3"></script>
<style type="text/tailwindcss">
@theme inline {
--animate-bounce: bounce 1s var(--animate-repeat-count, infinite);
}
@utility animate-repeat-* {
--animate-repeat-count: --value(integer);
}
@utility animate-repeat-infinite {
--animate-repeat-count: infinite;
}
</style>
<div class="p-10 text-center">
<button class="mx-auto animate-bounce bg-green-300 px-8 py-2 animate-repeat-2">Bounce animation repeated 2 times in 1s.</button>
</div>