tailwindlabs/tailwindcss · error · Error

You cannot use `@apply` inside `@keyframes`.

Error message

You cannot use `@apply` inside `@keyframes`.

What it means

During `@apply` substitution, the AST walker specially handles `@keyframes` rules and forbids any `@apply` at-rule nested inside them. `@keyframes` defines keyframe stops (percentages / `from` / `to`), which cannot themselves receive utility declarations, so applying utilities there is semantically invalid. The walker skips the keyframes subtree after checking, throwing on the first offending `@apply`.

Source

Thrown at packages/tailwindcss/src/apply.ts:36

  // Track all nodes containing `@apply`
  let parents = new Set<AstNode>()

  // Track all the dependencies of an `AstNode`
  let dependencies = new DefaultMap<AstNode, Set<string>>(() => new Set<string>())

  // Track all `@utility` definitions by its root (name)
  let definitions = new DefaultMap(() => new Set<AstNode>())

  // Collect all new `@utility` definitions and all `@apply` rules first
  walk([root], (node, ctx) => {
    if (node.kind !== 'at-rule') return

    // Do not allow `@apply` rules inside `@keyframes` rules.
    if (node.name === '@keyframes') {
      walk(node.nodes, (child) => {
        if (child.kind === 'at-rule' && child.name === '@apply') {
          throw new Error(`You cannot use \`@apply\` inside \`@keyframes\`.`)
        }
      })
      return WalkAction.Skip
    }

    // `@utility` defines a utility, which is important information in order to
    // do a correct topological sort later on.
    if (node.name === '@utility') {
      let name = node.params.replace(/-\*$/, '')
      definitions.get(name).add(node)

      // In case `@apply` rules are used inside `@utility` rules.
      walk(node.nodes, (child) => {
        if (child.kind !== 'at-rule' || child.name !== '@apply') return

        parents.add(node)

        for (let dependency of resolveApplyDependencies(child, designSystem)) {

View on GitHub (pinned to 16e94cbf7f)

Solutions

  1. Replace the `@apply` inside `@keyframes` with the literal CSS property (e.g. `opacity: 0`).
  2. Define the utility styles in the element/animation target and let `@keyframes` only set raw property values.

Example fix

/* before */
@keyframes fade {
  from { @apply opacity-0; }
  to   { @apply opacity-100; }
}

/* after */
@keyframes fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
Defensive patterns

Strategy: validation

Validate before calling

import postcss from 'postcss'
function assertNoApplyInKeyframes(css: string) {
  const root = postcss.parse(css)
  root.walkAtRules('keyframes', kf => {
    kf.walkAtRules('apply', () => {
      throw new Error(`@apply is forbidden inside @keyframes at line ${kf.source?.start?.line}`)
    })
  })
}

Prevention

When it happens

Trigger: CSS like `@keyframes fade { from { @apply opacity-0; } }`. The `@keyframes` branch at apply.ts:33 walks its children, finds an `@apply` at-rule, and throws at apply.ts:36.

Common situations: Trying to animate using utility values inside `@keyframes`, or copy-pasting utility-based rules into a keyframes block during a refactor.

Related errors


AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12). Data as JSON: /api/errors/8242164cdd28bad8. Report an issue: GitHub.