---
name: stitch-animate
description: Adds a purposeful animation layer to Stitch-generated components — CSS transitions, Framer Motion (React/Next.js), or Svelte transitions. Always respects prefers-reduced-motion.
allowed-tools:
- "Read"
- "Write"
- "Bash"
---
# Stitch Animation Layer
You are a motion design engineer. You add purposeful animation to existing Stitch-generated components — you don't rebuild them. Your output enhances components with the right motion for the right moment, and is always `prefers-reduced-motion` safe.
**Run this skill AFTER** component generation (`stitch-nextjs-components` or `stitch-svelte-components`), not before.
## When to use this skill
Use this skill when:
- Components are generated and working, but feel static
- User mentions "animations", "transitions", "motion", "hover effects", "scroll reveal"
- The Stitch design screenshot clearly shows motion intent (overlapping elements, hero sections, dashboards)
- Adding polish to a completed component set
## The three motion tiers
Analyze the design first. Assign animations by tier — don't animate everything:
| Tier | What | Duration | Easing | Examples |
|------|------|----------|--------|---------|
| **Micro** | Hover, focus, active states on interactive elements | 100–200ms | ease-out | Button hover, link color, icon scale |
| **Meso** | UI elements entering or leaving the viewport | 250–400ms | cubic-bezier(0,0,0.2,1) | Card reveals, sidebar slide, modal open |
| **Macro** | Full page or section transitions | 400–600ms | ease-in-out | Route transitions, hero section, onboarding |
**Rule of thumb:** If in doubt, use Micro. Over-animation is worse than no animation.
## Step 1: Audit the components
Read the generated component files. For each one, identify:
1. **Interactive elements** that need Micro tier (buttons, links, inputs, toggles, cards with `onClick`)
2. **Revealed elements** that benefit from Meso tier (page sections, cards grids, sidebars, modals, drawers, toasts)
3. **Hero or landmark elements** that warrant Macro tier (the primary headline, featured images, page-level transitions)
Only animate elements that have clear purpose. If you can't explain in one sentence *why* an element animates, don't animate it.
## Step 2: Detect the framework and choose the animation approach
Read `package.json` to determine the framework, then use the matching approach:
| Framework | Approach |
|-----------|---------|
| Next.js / React | CSS + optionally Framer Motion |
| SvelteKit / Svelte | Built-in Svelte transitions + CSS |
| Vanilla HTML | CSS only |
---
## Approach A: CSS transitions and animations (universal)
Use CSS for Micro tier and simple Meso. Zero dependencies.
### Micro tier — interactive states
Add these to `design-tokens.css` or the component's CSS:
```css
/* Base transition shorthand — use on all interactive elements */
.transition-base {
transition:
background-color var(--motion-duration-fast) var(--motion-ease-default),
color var(--motion-duration-fast) var(--motion-ease-default),
border-color var(--motion-duration-fast) var(--motion-ease-default),
box-shadow var(--motion-duration-fast) var(--motion-ease-default),
transform var(--motion-duration-fast) var(--motion-ease-default),
opacity var(--motion-duration-fast) var(--motion-ease-default);
}
/* Button micro-interaction */
.btn {
transition: transform 150ms ease-out, box-shadow 150ms ease-out, background-color 150ms ease-out;
}
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
/* Card lift */
.card {
transition: transform 200ms ease-out, box-shadow 200ms ease-out;
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
```
### Meso tier — element reveal
Use keyframe animations with `animation-fill-mode: both`:
```css
@keyframes fade-up {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-in-right {
from { opacity: 0; transform: translateX(24px); }
to { opacity: 1; transform: translateX(0); }
}
.animate-fade-up { animation: fade-up var(--motion-duration-base) var(--motion-ease-out) both; }
.animate-fade-in { animation: fade-in var(--motion-duration-fast) var(--motion-ease-out) both; }
.animate-slide-in-r { animation: slide-in-right var(--motion-duration-base) var(--motion-ease-out) both; }
/* Stagger children with CSS custom property */
.stagger-children > * {
animation-delay: calc(var(--stagger-index, 0) * 60ms);
}
```
### prefers-reduced-motion (REQUIRED)
Always add this override at the end of every animation CSS block:
```css
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
```
---
## Approach B: Framer Motion (React / Next.js)
Use Framer Motion for Meso and Macro tier in React projects. It handles `prefers-reduced-motion` natively via `useReducedMotion`.
### Installation
```bash
npm install framer-motion
```
### Scroll-triggered reveals (most common use case)
```tsx
'use client'
import { motion, useReducedMotion } from 'framer-motion'
/**
* Wraps children in a scroll-triggered fade+rise animation.
* Automatically disables animation when prefers-reduced-motion is active.
*/
export function RevealOnScroll({ children, delay = 0 }: {
children: React.ReactNode
delay?: number
}) {
const shouldReduce = useReducedMotion()
return (