Skip to content

Keyframes

The core:keyframes plugin manages CSS @keyframes animations. It generates @keyframes rules as preflight CSS and provides autocomplete for animationName and animation properties.

How It Works

  1. Keyframe definitions are collected from config.keyframes.keyframes during rawConfigConfigured.
  2. During configureEngine, each definition is resolved and registered:
    • Autocomplete entries are added for animationName and animation.
    • Entries with frames are stored in engine.keyframes.store.
  3. A preflight function scans atomic styles for animation references to determine which keyframes are actually used.
  4. Only used keyframes (or those with pruneUnused: false) are emitted to CSS output.

Config

ts
interface KeyframesConfig {
  /** Array of keyframe definitions. */
  keyframes: Keyframes[]
  /** Whether to prune unused keyframes from CSS output. @default true */
  pruneUnused?: boolean
}

Keyframe Definition Formats

PikaCSS supports three forms for defining keyframes:

1. String Form

Registers only the keyframe name for autocomplete — no @keyframes block is generated.

ts
'external-animation'

2. Tuple Form

ts
type TupleForm = [name: string, frames?: KeyframesProgress, autocomplete?: string[], pruneUnused?: boolean]

3. Object Form

ts
interface ObjectForm { name: string, frames?: KeyframesProgress, autocomplete?: string[], pruneUnused?: boolean }

KeyframesProgress

The frames object maps animation stops to CSS properties:

  • from — alias for 0%
  • to — alias for 100%
  • `${number}%` — any percentage stop (e.g., '25%', '50%')

Full Example

ts
// pika.config.ts
import { defineEngineConfig } from '@pikacss/unplugin-pikacss'

export default defineEngineConfig({
	keyframes: {
		keyframes: [
			// Tuple form: [name, frames, autocomplete?, pruneUnused?]
			['fade-in', {
				from: { opacity: '0' },
				to: { opacity: '1' },
			}, ['fade-in 0.3s ease']],

			// Object form
			{
				name: 'slide-up',
				frames: {
					from: { transform: 'translateY(100%)' },
					to: { transform: 'translateY(0)' },
				},
				autocomplete: ['slide-up 0.5s ease-out'],
				pruneUnused: false, // always include in CSS output
			},

			// Percentage-based keyframes
			['bounce', {
				'0%': { transform: 'translateY(0)' },
				'50%': { transform: 'translateY(-20px)' },
				'100%': { transform: 'translateY(0)' },
			}],

			// String-only form: register an external animation name for autocomplete
			'external-animation',
		],
		// Prune keyframes not referenced in atomic styles (default: true)
		pruneUnused: true,
	},
})

Usage with pika()

Reference defined keyframes in animationName or the animation shorthand:

ts
const className = pika({
	animation: 'fade-in 0.3s ease',
})

Generated CSS output:

css
/* Preflight: @keyframes definitions */
@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes slide-up {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

/* Atomic style generated by pika() */
.a { animation: fade-in 0.3s ease; }

Pruning Unused Keyframes

By default, pruneUnused is true. Only keyframes whose names appear in animationName or animation atomic style values are included in CSS output.

  • Global setting: keyframes.pruneUnused applies to all entries.
  • Per-keyframe override: Set pruneUnused on an individual entry.
  • Entries without frames are never output (they only affect autocomplete).

Autocomplete

The plugin registers these autocomplete values automatically:

  • animationName — the keyframe name (e.g., fade-in)
  • animation — the name followed by a space (e.g., fade-in ) to prompt for duration/easing
  • Custom autocomplete strings are also added as animation suggestions

Engine API

Plugins can manage keyframes programmatically:

  • engine.keyframes.storeMap<string, ResolvedKeyframesConfig> of all registered keyframes with frames
  • engine.keyframes.add(...list) — add keyframe definitions at runtime (accepts all three forms)

Source Reference

  • packages/core/src/internal/plugins/keyframes.ts

Next