Skip to content

Want to skip the docs? Check out pandamastery.com - the best way to learn Panda CSS

Advanced

Keyframes

Define inline, component-local CSS animations with the keyframes() factory. It returns the animation name and emits the @keyframes block for you.

How it works

keyframes() names an inline @keyframes (opens in a new tab) block and returns the animation name to put in animationName or the animation shorthand. Use it for an ad-hoc, component-local animation that does not need to live in theme.keyframes.

import { css, keyframes } from 'styled-system/css'
 
const spin = keyframes({
  from: { transform: 'rotate(0deg)' },
  to: { transform: 'rotate(360deg)' }
})
// → "kf_xxx"
 
css({ animation: `${spin} 1s linear infinite` })
@keyframes kf_xxx {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

Panda folds the call at build time, so a static keyframes() inlines to its name and drops the import. Unused inline keyframes stay out of the CSS, the same way theme.keyframes are pruned when removeUnusedKeyframes is on.

Using the result

The returned name is a normal string, so it composes in your own style object. Put it in animationName, or interpolate it into the animation shorthand:

import { css, keyframes } from 'styled-system/css'
 
const fade = keyframes({
  from: { opacity: 0 },
  to: { opacity: 1 }
})
 
css({ animationName: fade, animationDuration: '0.6s', animationTimingFunction: 'ease-out' })

Object form only

Unlike viewTransition() and positionTry(), keyframes() has no named form. A bare animationName: 'spin' already resolves a theme.keyframes entry and tree-shakes, so a keyframes('spin') call would be redundant. The factory takes an object and only an object.

Theme keyframes

Shared, design-system animations still belong in theme.keyframes, referenced by name. The factory is additive, for the inline case a shared theme entry cannot express:

panda.config.ts

import { defineConfig, defineKeyframes } from '@pandacss/dev'
 
export default defineConfig({
  theme: {
    extend: {
      keyframes: defineKeyframes({
        spin: {
          from: { transform: 'rotate(0deg)' },
          to: { transform: 'rotate(360deg)' }
        }
      })
    }
  }
})
import { css } from 'styled-system/css'
 
css({ animationName: 'spin' })

Design systems

Export an inline animation the same way you'd export a shared cva:

export const spin = keyframes({ to: { transform: 'rotate(360deg)' } })

panda lib puts it in build info. Consuming apps get the @keyframes CSS without re-scanning your source, and tree-shaking follows imports like recipes. Or register shared animations in a preset's theme.keyframes.

Edit this page on GitHubView as markdown
Last updated on