.claude/skills/interaction-design/SKILL.md
Interaction & animation principles — fluidity philosophy, when to animate, easing/timing/spring rules, two-layer interaction system, haptic feedback, gesture quality, and practical tips. Auto-load when designing or specifying any motion, animation, or gesture behavior.
npx skillsauth add taewoongheo/taste interaction-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Universal guide for creating interactions and animations that feel right. Platform-agnostic principles — applies to web, React Native, and any UI.
UI is not a collection of individual screens — it is one organically connected flow.
These principles underpin the Decision Framework below — when asking "should I animate?", the test is whether the motion connects or breaks the flow.
Every animation decision starts here:
Animate:
Don't animate:
Frequency rule:
Baseline quality. No extra time. Applied by default.
| Component | Trigger | Animation | Haptic | |-----------|---------|-----------|--------| | Button press | tap | scale(0.97→1), spring(d:15, s:300) | impact light | | Card press | tap | scale(0.98→1), opacity(0.8→1) | impact light | | Bottom sheet | swipe/tap | ease-out-quart 300ms enter, spring snap | impact medium at snap | | Screen enter | navigation | fade + slideUp 250ms, ease-out-quint | none | | Screen exit | navigation | fade + slideDown 200ms, ease-out-cubic | none | | Toast | state | slideDown spring(d:20, s:200) | notification | | Skeleton | loading | opacity pulse 0.3↔0.7, 1.5s, linear | none | | Pull to refresh | scroll | spring overscroll | impact medium at threshold | | Tab switch | tap | cross-fade 200ms ease-in-out-cubic + layout | selection | | Modal appear | state | scale(0.95→1) + fade, spring(d:25, s:250) | impact light | | List item enter | mount | stagger 80ms, fadeIn + slideUp ease-out-cubic | none | | Swipe action | pan | follow finger, velocity snap | impact light at threshold | | Toggle | tap | spring(d:15, s:500) | impact light |
Product identity. Cannot be templated. Designed per app.
Criteria for Layer 2:
Standard easing curves for the project. All timing animations must use one of these.
| Name | Bezier | Character |
|------|--------|-----------|
| ease-in-quad | cubic-bezier(.55, .085, .68, .53) | Mild acceleration |
| ease-in-cubic | cubic-bezier(.550, .055, .675, .19) | Standard acceleration |
| ease-in-quart | cubic-bezier(.895, .03, .685, .22) | Strong acceleration |
| ease-in-quint | cubic-bezier(.755, .05, .855, .06) | Very strong acceleration |
| ease-in-expo | cubic-bezier(.95, .05, .795, .035) | Dramatic acceleration |
| ease-in-circ | cubic-bezier(.6, .04, .98, .335) | Circular acceleration |
| ease-out-quad | cubic-bezier(.25, .46, .45, .94) | Mild deceleration |
| ease-out-cubic | cubic-bezier(.215, .61, .355, 1) | Standard deceleration (default) |
| ease-out-quart | cubic-bezier(.165, .84, .44, 1) | Strong deceleration |
| ease-out-quint | cubic-bezier(.23, 1, .32, 1) | Very strong deceleration |
| ease-out-expo | cubic-bezier(.19, 1, .22, 1) | Dramatic deceleration |
| ease-out-circ | cubic-bezier(.075, .82, .165, 1) | Circular deceleration |
| ease-in-out-quad | cubic-bezier(.455, .03, .515, .955) | Mild ease-in-out |
| ease-in-out-cubic | cubic-bezier(.645, .045, .355, 1) | Standard ease-in-out (default) |
| ease-in-out-quart | cubic-bezier(.77, 0, .175, 1) | Strong ease-in-out |
| ease-in-out-quint | cubic-bezier(.86, 0, .07, 1) | Very strong ease-in-out |
| ease-in-out-expo | cubic-bezier(1, 0, 0, 1) | Dramatic ease-in-out |
| ease-in-out-circ | cubic-bezier(.785, .135, .15, .86) | Circular ease-in-out |
Is the element entering or exiting?
├── Entering → ease-out (default: ease-out-cubic)
├── Exiting → ease-out, 20% shorter (default: ease-out-cubic)
└── No
├── Is it moving/morphing on screen?
│ └── Yes → ease-in-out (default: ease-in-out-cubic)
└── Is it a subtle state change (hover, color)?
├── Yes → ease-out-quad (smoothest ease-out)
└── Is it constant motion?
├── Yes → linear
└── Default → ease-out-cubic
In React Native: default to spring. Only use timing+easing when a specific duration is needed.
Enter/exit animations. Fast start → slow end. Feels responsive.
Subtle ────────────────── Dramatic
quad → cubic → quart → quint → expo
| Use Case | Recommended Curve | Reason | |----------|------------------|--------| | Color/opacity changes | ease-out-quad | Smooth deceleration for subtle changes | | Standard UI (tooltip, toggle, dropdown) | ease-out-cubic | Balanced default | | Medium transitions (modal, sheet) | ease-out-quart | Fast initial response | | Emphasized transitions (screen enter, large elements) | ease-out-quint | Dramatic entrance feel | | Rapid appearance (notification, alert) | ease-out-expo | Instant appearance feel |
On-screen movement/morphing. Natural acceleration → deceleration.
| Use Case | Recommended Curve | Reason | |----------|------------------|--------| | Smooth position change | ease-in-out-quad | Natural sliding | | Standard movement (slider, reorder) | ease-in-out-cubic | Balanced default | | Emphasized movement (page transition) | ease-in-out-quart | Distinct acceleration/deceleration | | Dramatic transition (hero animation) | ease-in-out-expo | Mid-pause feel |
Slow starts delay visual feedback. Avoid in most UI.
| Use Case | Recommended Curve | Reason | |----------|------------------|--------| | Flying off screen | ease-in-quad | Accelerates as it disappears | | Anticipation effect | ease-in-cubic | Tension before action |
Only for constant-speed: marquees, tickers, hold-to-delete progress bars. Feels robotic for UI.
| Element Type | Duration | |---|---| | Micro-interactions (press feedback) | 100-150ms | | Standard UI (tooltips, toggles) | 150-250ms | | Medium transitions (modals, sheets) | 200-300ms | | Screen transitions | 250-350ms |
Rules:
Springs feel more natural because they simulate real physics — no fixed duration.
Springs maintain velocity when interrupted. Timing animations restart from zero. This makes springs essential for gestures users might change mid-motion.
Elements that animate together must use the same easing and duration.
Modal + overlay, tooltip + arrow, drawer + backdrop — if they move as a unit, they should feel like a unit.
| Type | When | iOS API |
|------|------|---------|
| Impact Light | Button press, selection | Haptics.impactAsync(Light) |
| Impact Medium | Threshold crossed, snap point | Haptics.impactAsync(Medium) |
| Impact Heavy | Destructive action confirm | Haptics.impactAsync(Heavy) |
| Selection | Picker change, tab switch | Haptics.selectionAsync() |
| Notification Success | Task complete | Haptics.notificationAsync(Success) |
| Notification Warning | Validation error | Haptics.notificationAsync(Warning) |
| Notification Error | Action failed | Haptics.notificationAsync(Error) |
Rules:
| Problem | Solution | |---|---| | Button doesn't feel responsive | Add scale(0.97) on press | | Element appears from nowhere | Start from scale(0.95), never scale(0) | | Shaky/jittery animation | Keep element on GPU layer throughout | | Animation origin feels wrong | Set transform origin to trigger location | | Sequential tooltips feel slow | Skip delay/animation after first tooltip | | Something still feels off | Add subtle blur to mask the transition | | Touch target too small | Use 44pt minimum hit area |
Never start from scale(0). Elements should always have visible shape — like a deflated balloon, not an appearing dot.
Bad: scale(0) → scale(1)
Good: scale(0.95) + opacity(0) → scale(1) + opacity(1)
When easing and timing adjustments don't fix the feel, add subtle blur to mask imperfections. Blur bridges visual gaps between states. Keep under 20px for performance.
These skip layout recalculation, running on the GPU.
Never animate: padding, margin, height, width, top, left → these trigger layout recalculation every frame.
Instead: Use scaleX/scaleY for size changes, translateX/translateY for position.
Every animation must support reduced motion — no exceptions, even opacity and color transitions.
useReducedMotion / prefers-reduced-motion)import { AccessibilityInfo } from 'react-native';
// Check once at app start
const [reduceMotion, setReduceMotion] = useState(false);
useEffect(() => {
AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);
}, []);
// Apply: replace animations with instant transitions
const duration = reduceMotion ? 0 : 300;
const springConfig = reduceMotion ? { duration: 0 } : { damping: 15, stiffness: 300 };
| Level | Description | Example | |-------|-------------|---------| | 0 - None | No feedback | Button with no visual response | | 1 - Basic | Simple state change | opacity: 0.5 on press | | 2 - Standard | Animated feedback | scale spring + haptic | | 3 - Polished | Physics-based, contextual | Velocity-aware swipe with snap + haptic at thresholds | | 4 - Delightful | Metaphor-driven, experiential | Full metaphor experience (Layer 2) |
Target: Level 2-3 for all template interactions, Level 4 for personal app core experience.
Answer in Korean.
development
Visual hierarchy verification — squint test, information density, primary action clarity, and empty state quality for mobile UI. Auto-load when doing design or layout work.
development
Comprehensive design guide for web and mobile applications. Contains 50+ styles, 97 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types.
development
TDD patterns for React Native — Jest setup, React Native Testing Library patterns, mock strategies, and what to test vs skip. Auto-load when writing or running tests.
testing
Spacing, layout, and spatial relationships — consistent scale, proximity grouping, optical alignment, and density control for mobile UI. Auto-load when doing design or layout work.