Expert knowledge for physics-based spring animations - natural, organic motion that follows real-world physics principles for delightful user experiences.
From react-animation-studionpx claudepluginhub markus41/claude --plugin react-animation-studioThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Details PluginEval's skill quality evaluation: 3 layers (static, LLM judge), 10 dimensions, rubrics, formulas, anti-patterns, badges. Use to interpret scores, improve triggering, calibrate thresholds.
Expert knowledge for physics-based spring animations - natural, organic motion that follows real-world physics principles for delightful user experiences.
Activate this skill when:
**/*.tsx with spring configurations**/hooks/use*Spring*.ts**/animations/springs.ts@react-spring/web importstype: "spring"Springs simulate real-world physics where motion is determined by:
High Stiffness + Low Damping = Bouncy
High Stiffness + High Damping = Snappy
Low Stiffness + High Damping = Sluggish
Low Stiffness + Low Damping = Wobbly
import { motion } from 'framer-motion';
// Basic spring
<motion.div
animate={{ x: 100 }}
transition={{
type: 'spring',
stiffness: 400,
damping: 30,
}}
/>
// Spring presets
const springPresets = {
// Snappy - immediate response
snappy: { type: 'spring', stiffness: 400, damping: 30 },
// Bouncy - playful feel
bouncy: { type: 'spring', stiffness: 300, damping: 10 },
// Gentle - soft transitions
gentle: { type: 'spring', stiffness: 100, damping: 20 },
// Stiff - no bounce
stiff: { type: 'spring', stiffness: 500, damping: 50 },
// Wobbly - jelly-like
wobbly: { type: 'spring', stiffness: 150, damping: 8 },
};
// Duration-based spring (auto-calculates physics)
transition: {
type: 'spring',
duration: 0.5,
bounce: 0.25, // 0 = no bounce, 1 = very bouncy
}
import { useSpring, animated } from '@react-spring/web';
function Component() {
const springs = useSpring({
from: { opacity: 0, y: 20 },
to: { opacity: 1, y: 0 },
config: {
mass: 1,
tension: 280, // Similar to stiffness
friction: 20, // Similar to damping
// Or use preset
// ...config.gentle
},
});
return <animated.div style={springs}>Content</animated.div>;
}
// React Spring presets
import { config } from '@react-spring/web';
config.default // { mass: 1, tension: 170, friction: 26 }
config.gentle // { mass: 1, tension: 120, friction: 14 }
config.wobbly // { mass: 1, tension: 180, friction: 12 }
config.stiff // { mass: 1, tension: 210, friction: 20 }
config.slow // { mass: 1, tension: 280, friction: 60 }
config.molasses // { mass: 1, tension: 280, friction: 120 }
Stiffness (tension):
100 |▓░░░░░░░░░| Slow, gentle
200 |▓▓░░░░░░░░| Moderate
400 |▓▓▓▓░░░░░░| Quick, responsive (recommended default)
600 |▓▓▓▓▓▓░░░░| Fast, snappy
1000 |▓▓▓▓▓▓▓▓▓▓| Very fast, almost instant
Damping (friction):
5 |▓░░░░░░░░░| Very bouncy, oscillates many times
15 |▓▓░░░░░░░░| Bouncy, 2-3 oscillations
30 |▓▓▓▓░░░░░░| Slight overshoot, settles quickly (recommended)
50 |▓▓▓▓▓▓░░░░| No bounce, smooth stop
100 |▓▓▓▓▓▓▓▓▓▓| Very damped, sluggish feel
Mass:
0.5 |▓░░░░░░░░░| Light, quick response
1 |▓▓▓▓▓░░░░░| Normal (default)
2 |▓▓▓▓▓▓▓▓░░| Heavy, more momentum
const springConfigs = {
// UI Micro-interactions (buttons, toggles)
micro: { stiffness: 500, damping: 35 },
// Page transitions, modals
page: { stiffness: 300, damping: 30 },
// Drag and drop
drag: { stiffness: 400, damping: 40 },
// Tooltips, popovers
tooltip: { stiffness: 600, damping: 40 },
// Loading indicators
loading: { stiffness: 200, damping: 20 },
// Playful/fun interfaces
playful: { stiffness: 300, damping: 15 },
// Professional/corporate
corporate: { stiffness: 400, damping: 45 },
// Mobile/touch interfaces
mobile: { stiffness: 350, damping: 30 },
};
import { motion, useSpring } from 'framer-motion';
function InterruptibleBox() {
const x = useSpring(0, { stiffness: 300, damping: 30 });
return (
<motion.div
style={{ x }}
onTap={() => {
// Spring handles interruption naturally
x.set(x.get() === 0 ? 200 : 0);
}}
/>
);
}
import { useTrail, animated } from '@react-spring/web';
function StaggeredList({ items }: { items: string[] }) {
const trail = useTrail(items.length, {
from: { opacity: 0, y: 20 },
to: { opacity: 1, y: 0 },
config: { mass: 1, tension: 280, friction: 20 },
});
return (
<div>
{trail.map((props, index) => (
<animated.div key={index} style={props}>
{items[index]}
</animated.div>
))}
</div>
);
}
import { motion, useMotionValue, useSpring } from 'framer-motion';
function DraggableCard() {
const x = useMotionValue(0);
const y = useMotionValue(0);
// Smooth following with spring
const springX = useSpring(x, { stiffness: 400, damping: 30 });
const springY = useSpring(y, { stiffness: 400, damping: 30 });
return (
<motion.div
style={{ x: springX, y: springY }}
drag
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
onDrag={(_, info) => {
x.set(info.point.x);
y.set(info.point.y);
}}
/>
);
}
import { motion, useMotionValue, useVelocity, useSpring } from 'framer-motion';
function VelocityAwareElement() {
const x = useMotionValue(0);
const velocity = useVelocity(x);
// Tilt based on velocity
const rotation = useSpring(
useTransform(velocity, [-500, 0, 500], [-15, 0, 15]),
{ stiffness: 300, damping: 30 }
);
return (
<motion.div
style={{ x, rotate: rotation }}
drag="x"
/>
);
}
// Conservative, precise movement
const enterprise = {
stiffness: 400,
damping: 45,
mass: 1,
};
// Feels: Reliable, trustworthy, efficient
// Bouncy, energetic movement
const playful = {
stiffness: 300,
damping: 15,
mass: 0.8,
};
// Feels: Fun, engaging, delightful
// Slow, graceful movement
const luxury = {
stiffness: 150,
damping: 25,
mass: 1.2,
};
// Feels: Elegant, sophisticated, refined
// Quick, no-nonsense movement
const fintech = {
stiffness: 500,
damping: 40,
mass: 1,
};
// Feels: Accurate, responsive, trustworthy
const springX = useSpring(cursor.x, { stiffness: 150, damping: 20 }); const springY = useSpring(cursor.y, { stiffness: 150, damping: 20 });
useEffect(() => { const handleMouse = (e: MouseEvent) => { setCursor({ x: e.clientX, y: e.clientY }); }; window.addEventListener('mousemove', handleMouse); return () => window.removeEventListener('mousemove', handleMouse); }, []);
return ( <motion.div className="fixed w-8 h-8 rounded-full bg-blue-500 pointer-events-none" style={{ x: springX, y: springY, translateX: '-50%', translateY: '-50%' }} /> ); }
</example>
## Related Skills
- **framer-motion** - Full library documentation
- **interaction-specialist** - Gesture patterns
- **gsap** - Alternative physics with inertia
## Author
Created by Brookside BI as part of React Animation Studio