tailwind-csstailwind-css-4

@utility for creating to modify the repeat count of pre-declared animations


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).

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.

<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>


Solution

  • 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>