Animate Skill Build Sequence: The 7-Step Motion Pipeline in emilkowalski/skills
The animate skill follows a strict 7-stage build sequence—frequency gating, purpose definition, tool selection, property constraints, easing/duration choices, interruption handling, and accessibility gating—that transforms motion design requests into production-ready, review-passing code.
The animate skill in the emilkowalski/skills repository is a construction-only automation that enforces disciplined motion design through a fixed, ordered pipeline. Unlike flexible creative tools, this skill rejects requests that skip steps or violate constraints. The entire animate build sequence is codified in [skills/animate/SKILL.md](https://github.com/emilkowalski/skills/blob/main/skills/animate/SKILL.md) (lines 30-86), with ready-to-use implementations available in [skills/animate/RECIPES.md](https://github.com/emilkowalski/skills/blob/main/skills/animate/RECIPES.md).
The 7-Stage Animate Skill Build Sequence
Each animation request must progress through the following stages in exact order. The skill aborts or redirects if any stage fails.
Stage 1: Frequency Gating — Should This Animate at All?
The first gate prevents unnecessary motion using a frequency-tier table:
| Trigger frequency | Animation decision |
|---|---|
| Keyboard shortcuts | No animation — return static UI |
| Hover & list navigation | Near-imperceptible micro-motion |
| Modals, drawers, toasts | Standard motion |
| Onboarding, first-run | Delight — full animation allowed |
If the tier returns "no animation," the skill stops immediately. This protects against wasted render cycles and preempts prefers-reduced-motion violations.
Stage 2: Purpose Definition — Why Does This Motion Exist?
Every animation must map to exactly one purpose keyword:
- Feedback — confirm user action completed
- Spatial consistency — maintain orientation during navigation
- State indication — signal mode change (enabled/disabled)
- Preventing a jarring change — soften abrupt layout shifts
- Explanation — teach interaction model
- Delight — reward or surprise (reserved for low-frequency moments)
If the requester cannot name a purpose, the skill aborts. This rule eliminates decorative animation that lacks user-centric justification.
Stage 3: Tool Selection — Cheapest That Works
The skill enforces a tool hierarchy and selects the first satisfactory option:
- CSS
transition— for simple state changes - CSS
@starting-style— for entry animations without JavaScript - CSS
@keyframesanimation — for multi-step or looping motion - WAAPI (Web Animations API) — for dynamic control, synchronization
- Motion library — only for complex gesture-driven physics
This "cheapest that works" rule prevents heavy library imports for fades and slides.
Stage 4: Property Constraints — GPU-Accelerated Only
Allowed properties are strictly limited:
| Property | Usage |
|---|---|
transform |
Primary — translate(), scale(), rotate() |
opacity |
Secondary — fades, layering |
clip-path |
Exceptional — special reveal effects only |
Specific rules from the specification:
- Use
scale(0.9–0.97)combined withopacity: 0for entrance animations - Set explicit
transform-originfor asymmetric scaling - Prefer percentages in
translate()for responsiveness - Use full
transformstrings when working with the Motion library
This constraint set guarantees compositor-only animations and eliminates layout thrashing.
Stage 5: Easing and Duration — Curated Token System
The skill pulls from predefined tables rather than accepting arbitrary values:
Easing curves:
ease-out—cubic-bezier(0.23, 1, 0.32, 1)for exits and feedbackease-in-out— for symmetric state togglesease— sparingly, for subtle emphasislinear— only for opacity fades or infinite loops
Duration matrix (selected examples):
| Interaction | Duration range |
|---|---|
| Button press feedback | 100–160 ms |
| Dropdown/menu | 150–250 ms |
| Modal entrance/exit | 200–300 ms |
| Page transitions | 300–500 ms |
For gesture-driven or "alive" motion, the skill substitutes spring physics with configurable stiffness and damping instead of fixed durations.
Stage 6: Interruption and Exit — Predictable Behavior
This stage defines behavior during rapid retriggering and state reversal:
| Scenario | Implementation |
|---|---|
| Rapidly toggled elements | CSS transitions (interruptible mid-flight) |
| Gesture-driven motion | Springs (natural physics on release) |
| Standard exit | Symmetric timing to entrance |
| Hold-to-confirm patterns | Asymmetric — slow enter, instant exit |
Symmetric exits prevent visual "jumps" when dismissing elements. Asymmetric patterns reinforce deliberate action for destructive operations.
Stage 7: Reduced-Motion and Pointer Gating — Accessibility Compliance
Final code must wrap motion in conditional media queries:
@media (prefers-reduced-motion: reduce) {
/* Eliminate or simplify motion */
}
@media (hover: hover) and (pointer: fine) {
/* Enable hover-dependent motion only for fine pointers */
}
For JavaScript-driven animation, the skill emits useReducedMotion() hooks to respect system preferences dynamically.
Complete Implementation Example
The following applies all seven stages to a button feedback animation:
<button class="save-button">Save</button>
:root {
/* Stage 5: Curated tokens */
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--duration-short: 120ms;
}
.save-button {
/* Stage 3: CSS transition (cheapest tool) */
/* Stage 4: transform + opacity only */
transition:
opacity var(--duration-short) var(--ease-out),
transform var(--duration-short) var(--ease-out);
transform-origin: center center;
}
/* Stage 7: Reduced motion support */
@media (prefers-reduced-motion: reduce) {
.save-button {
transition: opacity 80ms var(--ease-out);
transform: none !important;
}
}
/* Stage 7: Pointer gating */
@media (hover: hover) and (pointer: fine) {
.save-button:hover {
/* Stages 4-5-6 combined */
opacity: 0.9;
transform: translateY(-2px);
}
}
The JavaScript logic (abbreviated) that precedes this CSS:
// Stage 1: Frequency check
if (interactionCount > 100) {
return { motion: false, implementation: 'static' };
}
// Stage 2: Purpose validation
const PURPOSE_KEYWORDS = ['Feedback', 'Spatial consistency', 'State indication',
'Preventing a jarring change', 'Explanation', 'Delight'];
const purpose = 'Feedback'; // Must match exactly
Skill Output Format
Upon completing the seven stages, the animate skill emits:
- Implementation code — the actual CSS/JS/WAAPI/Motion implementation
- Gate result summary — which stages passed/failed and why
- Ingredients list — tool, properties, easing, duration, and spring config used
- Feel-check guidance — manual verification steps for the animation's perceived quality
Key Files in the Animate Skill
| File | Purpose |
|---|---|
[skills/animate/SKILL.md](https://github.com/emilkowalski/skills/blob/main/skills/animate/SKILL.md) |
Complete specification with build sequence, hard rules, and output format |
[skills/animate/RECIPES.md](https://github.com/emilkowalski/skills/blob/main/skills/animate/RECIPES.md) |
Production-ready implementations for buttons, dropdowns, toasts, modals, and gestures |
Summary
- The
animateskill enforces a fixed 7-stage pipeline: frequency gating → purpose → tool → properties → easing/duration → interruption → accessibility gating - Construction-only operation: the skill builds code but does not execute or preview animations
- Strict abort conditions: missing purpose, inappropriate frequency, or wrong property choices halt the pipeline
- Cheapest-tool rule: CSS transitions preferred over WAAPI; WAAPI preferred over Motion library
- GPU-only properties:
transformandopacity(withclip-pathexceptions) keep animations on the compositor - Mandatory accessibility:
prefers-reduced-motionand pointer-type media queries are non-optional outputs
Frequently Asked Questions
What happens if I try to use top or left instead of transform in the animate skill?
The skill rejects the request. According to [skills/animate/SKILL.md](https://github.com/emilkowalski/skills/blob/main/skills/animate/SKILL.md), only transform and opacity are permitted for standard animations. Using layout-triggering properties like top, left, width, or height forces recalculations and violates the compositor-only rule. The skill enforces translate() for position changes.
Can I skip the purpose definition stage if the animation is obviously needed?
No. Stage 2 is mandatory and abort-on-failure. The skill requires one of six exact purpose keywords—Feedback, Spatial consistency, State indication, Preventing a jarring change, Explanation, or Delight. This prevents decorative motion without user-centric justification and ensures every animation can be traced to a specific design intent.
How does the skill handle reduced motion preferences differently from pointer-type gating?
The skill treats these as separate but mandatory Stage 7 outputs. prefers-reduced-motion: reduce typically simplifies or eliminates transform-based motion while preserving opacity fades. The hover: hover and pointer: fine media query prevents hover-dependent transforms on touch devices where they feel broken. Both conditions wrap independent code blocks in the final CSS output.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →