| name | spring-motion |
| description | Generates spring-physics UI motion — micro-interactions with weight — and ships each effect three ways: CSS, React, and a copy-paste AI-prompt, with a live stiffness/damping readout. Owns a deterministic spring→cubic-bezier engine + a library of ~19 verified recipes (card resize, magnetic button, status pill, stagger, marquee, count-up, scramble…). Use when the user wants "spring animation", "bouncy/weighted UI motion", "micro-interaction", "reproduce a Kinetics-style effect", CSS/React motion from stiffness/damping, 스프링 모션, 물리 기반 인터랙션, 마이크로 인터랙션, 통통 튀는 UI 애니메이션. Do NOT use for timeline choreography or scroll-scrubbing (use gsap), pure decorative keyframes with no spring feel (use css-animations), 3D/WebGL (use three), Lottie playback (use lottie), or video-render motion (use hyperframes). |
Spring Motion
Reproduces the engine behind Kinetics (kinetics.colorion.co): interface motion built on
spring physics instead of fixed-duration easing. You describe a UI interaction; this skill
emits it three ways — CSS + React + AI-prompt — with the tuned spring parameters shown,
so the consumer copies a real spring, not a guess. Fill free design into a validated spring
skeleton (thin-harness-fat-skills #5); the model writes intent, code owns the numbers
(sonnet-format-determinism).
The spring engine (code owns the easing — never hand-guess)
Three knobs → one CSS curve. ζ = c/(2√(km)) decides feel: <1 overshoots (springy), =1
crisp, >1 sluggish. Run the deterministic mapper for every effect:
python3 scripts/spring_to_bezier.py --stiffness 320 --damping 24
python3 scripts/spring_to_bezier.py --stiffness 320 --damping 24 --emit-css .card height
python3 scripts/spring_to_bezier.py --presets
Signature spring(320, 24) → cubic-bezier(0.34, 1.56, 0.64, 1) (matches Kinetics).
Full model, the JS spring integrator (drag/momentum/count), and presets: references/spring-physics.md.
Output contract (every request)
- Match or derive. Match the request to a recipe in
references/recipes.md; if none
fits, derive from spring-physics.md + the mapper.
- Ship three ways — a CSS block, a React component, and an AI-prompt paragraph — plus
the spring readout (
spring(k, c), regime, duration). Same three-way shape as Kinetics.
- Guard. Always include the
prefers-reduced-motion fallback (the --emit-css output
already does) and keep motion on compositor props (transform/opacity/clip-path/filter).
- Tokens. Default dark palette
--bg:#0E0E10 --accent:#5B8DEF --ok:#4CD08A; swap to the
project's frontend-design tokens when one exists.
Recipe library
references/recipes.md — ~19 verified effects across three groups, each with CSS/React/prompt:
- Interaction & Input: Card Resize, Magnetic Button, Draggable Scrubber, Swatch Picker,
Tag Input, Speed-Dial FAB, Toggle Switch.
- Feedback & State: Status Pill, Step Progress, Badge Counter, Undo Snackbar, Scramble
Reveal, Typewriter, Odometer Count-up, Signal Bars.
- Surface & Motion: Squash/Stretch Ball, Momentum Marquee, Stagger Entrance, Marquee Reveal.
The remaining ~80 Kinetics effects derive from the primitives (see recipes.md end).
Delegation (don't reinvent)
| Need | Skill |
|---|
| Timeline choreography, ScrollTrigger, scrubbing | gsap |
| Pure CSS keyframes, no spring feel | css-animations |
WAAPI (element.animate) | waapi |
| Motion principles (squash/stretch, anticipation, easing theory) | 12-principles-of-animation, animation-principles |
| Design tokens / theme | frontend-design |
| 3D / WebGL · Lottie · video-render motion | three · lottie · hyperframes |
Gallery build (presentation layer)
scripts/gallery_build.py emits a self-contained dark gallery HTML (Kinetics-style): a live
stiffness/damping/mass playground (hero box springs on click, easing recomputes as you
drag) + a grid of 7 live effect cards, each labeled with its spring readout. The JS
spring→cubic-bezier formula is mirrored from spring_to_bezier.py so sliders stay accurate.
python3 scripts/gallery_build.py --out spring-gallery.html --open
Interactive session opens it (display-generated-images rule); cron/headless omits --open.
Extend the card set by editing DEFAULT + CARD_HTML in the script.
gotchas
- Single bezier ≠ enough for interruptible/live motion (drag release, momentum, repeated
increments) → use the JS spring integrator in
spring-physics.md, not a transition.
- Overshoot clips: a springy scale/translate past its bound gets cut by
overflow:hidden
unless intended. Give it headroom.
- Height springs thrash under load — fine on small isolated cards; prefer
transform:scaleY
- inner wrapper when it must stay smooth.
- Don't fabricate spring numbers — every emitted
spring(k,c) must come from the mapper,
and its cubic-bezier must be the mapper's output. Model reports get overwritten by code.
- This is a quality-output skill (real UI motion). For a throwaway one-off transition,
a plain ease is fine — don't over-apply (token-diet-hygiene).