Skill 18 · Hyperframes Animation
Subchapter 18.10
adapters/css-animations.mdMarkdown5 KBView on GitHub
HyperFrames 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.
data-start value so local animation time matches the clip.animation-duration and animation-iteration-count because the negative-delay fallback cannot represent unbounded duration in environments without WAAPI-backed CSS animations.animation-fill-mode: both so seeked states hold before and after active motion.The adapter discovers elements with computed animation-name, seeks their browser Animation handles when available, and falls back to pausing with negative animation-delay.
<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>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>infinite, add data-duration to the root element — see Composition Duration below.top, left, width, or height when transforms work.The render engine needs to know the composition’s total length. GSAP timelines report this automatically; CSS-only compositions have no timeline object, so the runtime infers duration from the longest running animation’s computed end time (animation-delay + animation-duration × finite animation-iteration-count, per element with data-start added as an offset). data-duration on the root element is optional whenever every CSS animation on the page is finite — you don’t need to add it just because the composition is CSS-driven.
animation-iteration-count: infinite (or any unresolved/unbounded animation) has no finite end time, so it cannot be auto-inferred. If the composition’s only animation is infinite, you must add data-duration="<seconds>" to the root [data-composition-id] element with your intended total length — npx hyperframes lint errors on this case (root_composition_missing_duration_source) precisely because there is nothing for the runtime to infer.
<div
data-composition-id="root"
data-start="0"
data-duration="6"
data-width="1920"
data-height="1080"
>
<div class="clip spinner" data-start="0" style="animation: spin 1s linear infinite"></div>
</div>After editing CSS animation compositions:
npx hyperframes lint
npx hyperframes checkpackages/core/src/runtime/adapters/css.ts.packages/core/src/runtime/init.ts (resolveAdapterDurationFloorSeconds), getInferredDurationSeconds in the adapter above.animation-fill-mode: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode (opens in a new tab)