Back to skills

Agent Skill

Css Animations

css-animations

FrameVideo can seek CSS keyframe animations through its `css` runtime adapter. Use this for simple repeated motifs, background motion, shimmer, glow, masks, and non-sequenced decoration.

Heygen ComFrontendTypeScriptAiAnimationFfmpegFrameworkGsapHtmlMcpPuppeteerRenderingTypescriptVideo

74K installs

chanjing-ai/framevideo

by Heygen Com

Score

8.5

/ 10

Installs

74K

Repo Stars

43.6K

Last Updated

0d ago

Fresh

Quality Ratio

91%

Description

Verified

Language

TypeScript

First Published

Apr 2026

Summary

The Css Animations agent skill guides developers on authoring CSS keyframe animations, timing, and fill modes to ensure they are deterministically seekable within the HyperFrames rendering environment. This skill is invaluable for frontend developers and motion designers working with HyperFrames to create precise, seekable web animations for elements like simple repeated motifs, background motion, shimmer, and glow. It is a skill with 3K installs, indicating a significant user base. It establishes a contract, requiring animated elements to be in the DOM before runtime initialization and to include a `data-start` value for time synchronization. Developers are instructed to use finite `animation-duration` and `animation-iteration-count` along with `animation-fill-mode: both` for consistent seeking. The skill specifically advises against wall-clock JavaScript, hover-triggered states, and class toggles that depend on user events for render-critical motion. It also recommends avoiding infinite CSS animations unless WAAPI-backed and preferring transforms over layout properties for animation.

Skill Definition

FrameVideo can seek CSS keyframe animations through its css runtime adapter. Use this for simple repeated motifs, background motion, shimmer, glow, masks, and non-sequenced decoration.

For scene choreography, GSAP is usually clearer. CSS animations work best when the motion belongs to one element and has a fixed duration.

When To Use

Use CSS animations for:

  • Decorative layers — background patterns, shimmer effects, grain, subtle parallax that doesn't need precise timing
  • Simple loops — pulse, glow, breathing effects with known repeat counts
  • Per-element motion — entrance animations where each element animates independently
  • Zero-dependency motion — when you want animation without loading GSAP or other libraries
  • GPU-accelerated effects — transform and opacity animations that browsers optimize automatically

Do NOT Use

Avoid CSS animations for:

  • Scene choreography — multi-element sequences with precise timing → use gsap
  • Complex timelines — anything needing pause/resume control or dynamic duration → use gsap
  • Infinite loops — unless verified that browser exposes seekable WAAPI handles (rare in headless render)
  • Interactive animations — hover, scroll, or user-triggered motion won't render deterministically
  • Layout animations — animating top, left, width, height causes reflow → use transforms instead

Contract

  • Put the animated element in the DOM before runtime initialization finishes.
  • Give timed elements a data-start value so local animation time matches the clip.
  • Use finite animation-duration and animation-iteration-count because the negative-delay fallback cannot represent unbounded duration in environments without WAAPI-backed CSS animations.
  • Prefer animation-fill-mode: both so seeked states hold before and after active motion.
  • Avoid wall-clock JavaScript, hover-triggered state, and class toggles that depend on user events.

The adapter discovers elements with computed animation-name, seeks their browser Animation handles when available, and falls back to pausing with negative animation-delay.

Basic Pattern

<div
  id="pulse-ring"
  class="clip pulse-ring"
  data-start="0"
  data-duration="4"
  data-track-index="2"
></div>

<style>
  .pulse-ring {
    width: 280px;
    height: 280px;
    border: 4px solid rgba(255, 255, 255, 0.7);
    border-radius: 50%;
    animation-name: pulse-ring;
    animation-duration: 1200ms;
    animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
    animation-iteration-count: 3;
    animation-fill-mode: both;
  }

  @keyframes pulse-ring {
    from {
      opacity: 0;
      transform: scale(0.82);
    }
    35% {
      opacity: 1;
    }
    to {
      opacity: 0;
      transform: scale(1.18);
    }
  }
</style>

Stagger Pattern

Use CSS custom properties to avoid duplicating keyframes:

<div class="clip dots" data-start="1" data-duration="3" data-track-index="3">
  <span style="--i: 0"></span>
  <span style="--i: 1"></span>
  <span style="--i: 2"></span>
</div>

<style>
  .dots span {
    display: inline-block;
    width: 18px;
    height: 18px;
    margin-right: 10px;
    border-radius: 50%;
    background: currentColor;
    animation: dot-pop 900ms ease-out both;
    animation-delay: calc(var(--i) * 120ms);
  }

  @keyframes dot-pop {
    from {
      opacity: 0;
      transform: translateY(18px) scale(0.75);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
  }
</style>

Good Uses

  • Decorative loops with a known repeat count.
  • Mask, glow, shimmer, grain, and subtle parallax layers.
  • Simple one-element entrances where a full JS timeline would be excessive.

Combining with GSAP

CSS animations and GSAP timelines can coexist in the same composition. The division of labor:

CSS handles decoration:

  • Background patterns, grain, noise
  • Ambient glow, shimmer, pulse effects
  • Mask reveals, vignettes
  • Subtle environment motion (floating particles, slow drift)

GSAP handles choreography:

  • Caption timing and sequencing
  • Scene transitions and crossfades
  • UI element entrances coordinated with audio
  • Multi-element stagger with precise control

Sync Pattern

CSS animation timing aligns with clip data-start:

<div class="clip bg-shimmer" data-start="0" data-duration="10">
  <!-- CSS animation runs 0-10s, synced to clip -->
</div>

<script>
  const tl = gsap.timeline();
  tl.to("#caption-1", { opacity: 1, duration: 0.5 }, 2);
  tl.to("#caption-2", { opacity: 1, duration: 0.5 }, 5);
  // GSAP controls captions, CSS handles background
</script>

The CSS adapter and GSAP adapter run independently but both respect FrameVideo's global timeline, so they stay in sync during preview and render.

Advanced Patterns

Complex Stagger with Custom Properties

For stagger based on data attributes or computed values:

<div class="grid">
  <div class="cell" style="--row: 0; --col: 0"></div>
  <div class="cell" style="--row: 0; --col: 1"></div>
  <!-- ... -->
</div>

<style>
  .cell {
    animation: cell-reveal 600ms ease-out both;
    animation-delay: calc((var(--row) * 3 + var(--col)) * 80ms);
  }

  @keyframes cell-reveal {
    from { opacity: 0; transform: scale(0.8); }
    to { opacity: 1; transform: scale(1); }
  }
</style>

Finite Loops for Infinite Patterns

When a pattern looks infinite but must be finite for rendering:

.background-drift {
  /* Appears infinite but is actually 50 iterations over 100s */
  animation: drift 2000ms linear 50;
}

@keyframes drift {
  from { transform: translateX(0); }
  to { transform: translateX(-100px); }
}

Rule: Calculate iteration count = clip duration / animation duration, rounded up. For a 30s clip with 2s animation: animation-iteration-count: 15.

Animation Chains

Run multiple animations sequentially on one element:

.logo {
  animation: 
    fade-in 800ms ease-out 0s both,
    pulse 1200ms ease-in-out 1s 3 both,
    fade-out 800ms ease-in 4.6s both;
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }

Delays stack: fade-in at 0s, pulse at 1s, fade-out at 4.6s.

Performance Optimization

.optimized {
  /* Tell browser to promote to GPU layer */
  will-change: transform, opacity;
  
  /* Prefer transform over position */
  animation: slide-in 600ms ease-out both;
}

@keyframes slide-in {
  /* Good: GPU-accelerated */
  from { transform: translateX(-100px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
  
  /* Avoid: causes reflow */
  /* from { left: -100px; } to { left: 0; } */
}

Only animate transform and opacity when possible. Avoid width, height, top, left, margin, padding.

Performance Comparison

MetricCSS AnimationsGSAPWAAPIAnime.js
Bundle size0 KB45 KB0 KB17 KB
Init time0 ms8 ms0 ms4 ms
Seek performance⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
GPU accelerationAutoManualAutoManual
Best forDecorationSequencingLightweightCompact

When CSS is faster:

  • Simple loops (pulse, glow, rotate)
  • GPU-accelerated properties only (transform, opacity)
  • No timeline control needed

When GSAP is faster:

  • Complex sequences with 10+ tweens
  • Precise timing control and labels
  • Dynamic duration or playback speed
  • Nested timelines

Avoid

  • Infinite CSS animations unless you have verified the browser exposes seekable WAAPI-backed CSS animation handles. Prefer a finite iteration count covering the visible duration.
  • Animating layout properties like top, left, width, or height when transforms work.
  • Relying on hover, focus, scroll, or media queries to trigger render-critical motion.
  • Changing animation classes after startup unless another deterministic timeline controls that change.

Validation

After editing CSS animation compositions:

npx framevideo lint      # Check timing contract
npx framevideo validate  # Check runtime errors

Manual checks:

  1. Seek test — scrub preview timeline, verify animation holds at each frame
  2. Iteration count — ensure animation-iteration-count covers clip duration (no blank frames at end)
  3. Fill mode — confirm animation-fill-mode: both so seeked states hold before/after
  4. GPU layers — open DevTools Performance, record render, verify no layout thrashing

Real-World Examples

Example 1: Background Shimmer

Use case: Add subtle shimmer to product card background without loading animation library.

<div class="clip product-card" data-start="0" data-duration="5">
  <div class="shimmer-layer"></div>
  <h2>Product Name</h2>
</div>

<style>
  .shimmer-layer {
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
    animation: shimmer 2s linear infinite;
  }
  @keyframes shimmer {
    from { transform: translateX(-100%); }
    to { transform: translateX(100%); }
  }
</style>

Result: Smooth shimmer effect, 0 KB overhead, GPU-accelerated.

Example 2: Loading Dots

Use case: Animated loading indicator during composition build phase.

<div class="dots">
  <span style="--i: 0"></span>
  <span style="--i: 1"></span>
  <span style="--i: 2"></span>
</div>

<style>
  .dots span {
    animation: dot-bounce 1.4s ease-in-out infinite both;
    animation-delay: calc(var(--i) * 160ms);
  }
  @keyframes dot-bounce {
    0%, 80%, 100% { transform: scale(0); }
    40% { transform: scale(1); }
  }
</style>

Result: Classic three-dot animation with CSS-only stagger.

Example 3: Pulse Ring on Logo

Use case: Draw attention to logo with pulsing ring effect.

<div class="clip logo-intro" data-start="0" data-duration="4">
  <div class="logo-container">
    <img src="logo.svg" alt="Logo">
    <div class="pulse-ring"></div>
  </div>
</div>

<style>
  .pulse-ring {
    position: absolute;
    inset: -20px;
    border: 3px solid rgba(255,255,255,0.6);
    border-radius: 50%;
    animation: pulse 1.5s cubic-bezier(0.2, 0, 0, 1) 2 both;
  }
  @keyframes pulse {
    from { opacity: 0; transform: scale(0.8); }
    35% { opacity: 1; }
    to { opacity: 0; transform: scale(1.2); }
  }
</style>

Result: Logo appears with two expanding rings, total 3s animation.

Credits And References

How to Use

Use in O-mega

Claude Code

npx skills add chanjing-ai/framevideo css-animations