Interaction Design | Aura Skills

Interaction Design

Design and implement purposeful UI motion, microinteractions, and feedback patterns to enhance usability and user delight.

Views

64

Uses

8

Updated

February 3, 2026

Author

wshobson
wshobson

Skill creator

Meng ToAdded by Meng To
PropertyValue
nameinteraction-design
descriptionDesign and implement microinteractions, motion design, transitions, and user feedback patterns. Use when adding polish to UI interactions, implementing loading states, or creating delightful user experiences.
keywordsux-design, ui-design, animation, micro-interactions, transitions, best-practices, javascript, css

Interaction Design

Create engaging, intuitive interactions through motion, feedback, and thoughtful state transitions that enhance usability and delight users.

When to Use This Skill

  • Adding microinteractions to enhance user feedback
  • Implementing smooth page and component transitions
  • Designing loading states and skeleton screens
  • Creating gesture-based interactions
  • Building notification and toast systems
  • Implementing drag-and-drop interfaces
  • Adding scroll-triggered animations
  • Designing hover and focus states

Core Principles

1. Purposeful Motion

Motion should communicate, not decorate:

  • Feedback: Confirm user actions occurred
  • Orientation: Show where elements come from/go to
  • Focus: Direct attention to important changes
  • Continuity: Maintain context during transitions

2. Timing Guidelines

DurationUse Case
100-150msMicro-feedback (hovers, clicks)
200-300msSmall transitions (toggles, dropdowns)
300-500msMedium transitions (modals, page changes)
500ms+Complex choreographed animations

3. Easing Functions

/* Common easings */ --ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* Decelerate - entering */ --ease-in: cubic-bezier(0.55, 0, 1, 0.45); /* Accelerate - exiting */ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* Both - moving between */ --spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* Overshoot - playful */

Quick Start: Button Microinteraction

import { motion } from "framer-motion"; export function InteractiveButton({ children, onClick }) { return ( <motion.button onClick={onClick} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} transition={{ type: "spring", stiffness: 400, damping: 17 }} className="px-4 py-2 bg-blue-600 text-white rounded-lg" > {children} </motion.button> ); }

Interaction Patterns

1. Loading States

Skeleton Screens: Preserve layout while loading

function CardSkeleton() { return ( <div className="animate-pulse"> <div className="h-48 bg-gray-200 rounded-lg" /> <div className="mt-4 h-4 bg-gray-200 rounded w-3/4" /> <div className="mt-2 h-4 bg-gray-200 rounded w-1/2" /> </div> ); }

Progress Indicators: Show determinate progress

function ProgressBar({ progress }: { progress: number }) { return ( <div className="h-2 bg-gray-200 rounded-full overflow-hidden"> <motion.div className="h-full bg-blue-600" initial={{ width: 0 }} animate={{ width: `${progress}%` }} transition={{ ease: "easeOut" }} /> </div> ); }

2. State Transitions

Toggle with smooth transition:

function Toggle({ checked, onChange }) { return ( <button role="switch" aria-checked={checked} onClick={() => onChange(!checked)} className={` relative w-12 h-6 rounded-full transition-colors duration-200 ${checked ? "bg-blue-600" : "bg-gray-300"} `} > <motion.span className="absolute top-1 left-1 w-4 h-4 bg-white rounded-full shadow" animate={{ x: checked ? 24 : 0 }} transition={{ type: "spring", stiffness: 500, damping: 30 }} /> </button> ); }

3. Page Transitions

Framer Motion layout animations:

import { AnimatePresence, motion } from "framer-motion"; function PageTransition({ children, key }) { return ( <AnimatePresence mode="wait"> <motion.div key={key} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3 }} > {children} </motion.div> </AnimatePresence> ); }

4. Feedback Patterns

Ripple effect on click:

function RippleButton({ children, onClick }) { const [ripples, setRipples] = useState([]); const handleClick = (e) => { const rect = e.currentTarget.getBoundingClientRect(); const ripple = { x: e.clientX - rect.left, y: e.clientY - rect.top, id: Date.now(), }; setRipples((prev) => [...prev, ripple]); setTimeout(() => { setRipples((prev) => prev.filter((r) => r.id !== ripple.id)); }, 600); onClick?.(e); }; return ( <button onClick={handleClick} className="relative overflow-hidden"> {children} {ripples.map((ripple) => ( <span key={ripple.id} className="absolute bg-white/30 rounded-full animate-ripple" style={{ left: ripple.x, top: ripple.y }} /> ))} </button> ); }

5. Gesture Interactions

Swipe to dismiss:

function SwipeCard({ children, onDismiss }) { return ( <motion.div drag="x" dragConstraints={{ left: 0, right: 0 }} onDragEnd={(_, info) => { if (Math.abs(info.offset.x) > 100) { onDismiss(); } }} className="cursor-grab active:cursor-grabbing" > {children} </motion.div> ); }

CSS Animation Patterns

Keyframe Animations

@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes spin { to { transform: rotate(360deg); } } .animate-fadeIn { animation: fadeIn 0.3s ease-out; } .animate-pulse { animation: pulse 2s ease-in-out infinite; } .animate-spin { animation: spin 1s linear infinite; }

CSS Transitions

.card { transition: transform 0.2s ease-out, box-shadow 0.2s ease-out; } .card:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1); }

Accessibility Considerations

/* Respect user motion preferences */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } }
function AnimatedComponent() { const prefersReducedMotion = window.matchMedia( "(prefers-reduced-motion: reduce)", ).matches; return ( <motion.div animate={{ opacity: 1 }} transition={{ duration: prefersReducedMotion ? 0 : 0.3 }} /> ); }

Best Practices

  1. Performance First: Use transform and opacity for smooth 60fps
  2. Reduce Motion Support: Always respect prefers-reduced-motion
  3. Consistent Timing: Use a timing scale across the app
  4. Natural Physics: Prefer spring animations over linear
  5. Interruptible: Allow users to cancel long animations
  6. Progressive Enhancement: Work without JS animations
  7. Test on Devices: Performance varies significantly

Common Issues

  • Janky Animations: Avoid animating width, height, top, left
  • Over-animation: Too much motion causes fatigue
  • Blocking Interactions: Never prevent user input during animations
  • Memory Leaks: Clean up animation listeners on unmount
  • Flash of Content: Use will-change sparingly for optimization

Resources