Design and Animation

Source: Emil Kowalski's design engineering philosophy. Skill at skills/engineering/design-engineering-polish/. Course: animations.dev. Cursor rule: .cursor/rules/design-animation-rules.mdc.

Source baseline: Emil Kowalski (emilkowalski/skill, animations.dev). Source: Emil Kowalski (emilkowalski/skill, animations.dev)

Core Philosophy

Taste is trained, not innate. Study why the best interfaces feel the way they do. Reverse engineer animations. Inspect interactions. Be curious. Emil Kowalski's reshare of Rasmus Andersson's taste-building thread is the rule in social form: intuitive design comes from collecting references, noticing details, and learning to name what works. Source: X/@emilkowalski, 2024-03-04; Source: X/@rsms, 2023-03-11

Hard CSS/UI rules (scrollbars, hit-area pseudo-elements, @property gradients, fluid clamp, agent lean-ctx bootstrap) live in CSS UI Enforcement - enforced across Kevin repos.

Unseen details compound. Most details users never consciously notice. That is the point. A thousand barely audible voices all singing in tune.

Beauty is a product multiplier. People select tools based on the overall experience, not just functionality. Good defaults and good animations are real differentiators.

Animation Decision Framework

1. Should this animate at all?

Frequency Decision
100+ times/day (keyboard shortcuts, command palette) No animation. Ever.
Tens of times/day (hover effects, list navigation) Remove or drastically reduce
Occasional (modals, drawers, toasts) Standard animation
Rare/first-time (onboarding, celebrations) Can add delight

Never animate keyboard-initiated actions. If the purpose is just "it looks cool" and the user will see it often, don't animate.

2. Easing Decision Flowchart

Entering or exiting viewport? -> ease-out
Moving/morphing on screen? -> ease-in-out
Hover/color change? -> ease
Constant motion (marquee, progress)? -> linear
Default -> ease-out

The reviewed quick-decision card is the pocket version of this framework: ease-out for entry/exit, ease-in-out for movement already on screen, ease for hover/color, no animation for actions users see 100+ times daily, springs for drag or interruptible gestures, longer duration for larger surfaces like drawers, and faster exits than entrances. Source: X/@emilkowalski, 2026-04-17; Source: local image artifact review, 2026-07-03

Use custom curves. Built-in CSS easings lack punch:

--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);

Never use ease-in for UI animations. It starts slow, making the interface feel sluggish. Resources: easing.dev, easings.co.

3. Duration Guidelines

Element Type Duration
Button press feedback 100-160ms
Tooltips, small popovers 125-200ms
Dropdowns, selects 150-250ms
Modals, drawers 200-500ms
Marketing/explanatory Can be longer

Rules:

  • UI animations stay under 300ms
  • Larger elements animate slower than smaller ones
  • Exit animations can be ~20% faster than entrance
  • Match duration to distance -- longer travel = longer duration

Component Rules

  1. Scale buttons on press: transform: scale(0.97) on :active with transition: transform 160ms ease-out. Scale 0.95-0.98.

  2. Never animate from scale(0): Start from scale(0.95) with opacity: 0. Nothing in the real world appears from nothing.

  3. Origin-aware popovers: Set transform-origin to trigger location. Use var(--radix-popover-content-transform-origin). Exception: modals stay centered.

  4. Skip tooltip delay on subsequent hovers: Once one is open, adjacent tooltips open instantly with [data-instant] { transition-duration: 0ms; }.

  5. CSS transitions over keyframes: Transitions are interruptible. Keyframes restart from zero. Use transitions for toasts, toggles, rapidly-triggered UI.

  6. Blur to mask crossfades: filter: blur(2px) during transition. Under 20px.

  7. Asymmetric enter/exit timing: Press slow when deliberate (hold-to-delete: 2s linear), release always snappy (200ms ease-out).

  8. Stagger animations: 30-80ms between items. Never block interaction during stagger.

  9. Use @starting-style for entry animations: Modern CSS replaces the useEffect + setMounted(true) pattern.

Page Transitions

Page transitions are navigation, not decoration. Use them only when they preserve orientation across routes: the outgoing surface should blur, fade, scale, or slide in a way that tells the user where the new surface came from. A reviewed Framer demo by Hamza Ehsan / AliGrids shows the useful version: a creamy marketing site moves from a hero state into a mission/story state through blur, fade, and scale choreography without losing the spatial thread. Source: X/@AliGrids, 2026-03-15; Source: local video artifact review, 2026-07-01

Do not cargo-cult the production stack from the reference. If the final app is a React product, route through Frontend and Design Skills, Frontend and Design Skills, or CSS view transitions. If the artifact is a Framer marketing site, preserve the reference as taste vocabulary and only port the behavior that improves comprehension.

Slow-Motion Animation Review

Fast UI motion should be reviewed slowly before it is judged. AliGrids' 0.1x CSS-animation artifact shows a dark sidebar where the Home selection pill hands off to Chat: at normal speed the hover feels polished, but the slowed pass exposes the pointer target, icon weight, moving pill, label reveal, tooltip timing, and whether the surrounding layout shifts. Source: X/@AliGrids, 2026-06-14; Source: local video contact sheet, 2026-07-02

Use 0.1x review for hover navigation, selection-pill handoffs, icon-label morphs, tooltip timing, and sidebar/dock state changes. Capture the slow pass before changing durations: if the geometry jumps, easing cannot fix it. The review target is not slower animation; it is smoother spatial continuity at production speed.

Launch Explainer Motion

Product-launch videos are not only marketing assets; they are compressed interaction specs. Mark Vassilevskiy's 2D animation reel shows why: the artifact uses soft brand fields, product screenshots, abstract data islands, phone frames, and animated type to explain Replit Slides, Bud's AI Human Emulator positioning, and other SaaS concepts without requiring a full product tour. Source: X/@MarkKnd, 2026-05-02

Use this pattern when a product is conceptually hard but visually simple: show the abstract workflow as motion, keep the UI frames crisp, and let the video carry the explanation. Do not copy the soft pastel style by default; the durable rule is sequencing and compression, not the palette.

Scroll Parallax References

The Framer "3 clicks" parallax bookmark is useful as a taste reference, not as evidence that every production site should be built in Framer. The sampled video shows a dark AI-agent landing page where the hero product panel and landscape foreground move at different scroll rates, then resolve into supporting copy and partner marks before returning to the hero. The durable motion rule is restrained depth: parallax should create spatial separation between content layers, not hide the copy or turn the page into a product tour. Source: X/@stfnco, 2026-03-05; Source: local video contact sheet, 2026-07-03

Parametric Motion Tuning

When decorative motion has more than a handful of spatial parameters, expose those parameters in a temporary tuning console instead of guessing values in code. Soren Blank's Linear parallax-bar recreation demonstrates the pattern: a floating DialKit-like panel adjusts bar count, x/y step, diagonal offsets, peak nodes, peak height, falloff, and floor while the bar geometry updates live. Source: X/@sorenblank and local video contact sheet, 2026-07-03

Use this for parallax, waves, soft extrusion, layered bars, and complex decorative geometry. Ship the resulting constants or design tokens; do not ship the dev tuning panel unless it is explicitly a user-facing creative tool.

Spring Animations

Springs feel more natural because they simulate real physics. No fixed duration -- they settle based on physical parameters.

Novikoff's spring-curve artifact gives a useful visual taxonomy: bouncy overshoots and returns, smooth reaches the target quickly without overshoot, and flattened eases in with a long slow approach. In Motion-style terms, use bounce: 0 for most polished UI springs; it starts fast, has a long tail, and avoids the toy feel of visible rebound. Negative-bounce or flattened curves can work for quiet decorative motion, but they often feel sluggish on controls. Source: X/@novikoff and local image review, 2026-07-03

When to use: drag interactions with momentum, elements that feel "alive", gestures that can be interrupted, decorative mouse-tracking.

const springRotation = useSpring(mouseX * 0.1, { stiffness: 100, damping: 10 });

Apple's approach (recommended): { type: "spring", duration: 0.5, bounce: 0.2 }. Keep bounce subtle (0.1-0.3). Avoid bounce in most UI contexts.

Springs maintain velocity when interrupted -- CSS animations restart from zero.

clip-path Techniques

clip-path: inset(top right bottom left) is one of the most capable animation tools. Techniques: hold-to-delete overlays, tab color transitions (duplicate + clip), image reveals on scroll, comparison sliders.

Gesture Rules

  • Momentum-based dismissal: calculate velocity (distance / time). If > 0.11, dismiss.
  • Damping at boundaries: the more they drag past the edge, the less it moves.
  • Pointer capture once dragging starts (setPointerCapture).
  • Multi-touch protection: ignore additional touch points after initial drag.
  • Friction instead of hard stops.

Performance

  • Only animate transform and opacity (GPU-composited).
  • Framer Motion x/y/scale are NOT hardware-accelerated. Use transform: "translateX(100px)" for GPU.
  • CSS animations run off main thread. Use CSS for predetermined, JS for dynamic.
  • Don't animate CSS variables on parents -- recalculates all children.
  • WAAPI (element.animate()) gives JS control with CSS performance.

Accessibility

  • prefers-reduced-motion: keep opacity/color, remove movement.
  • Gate hover behind @media (hover: hover) and (pointer: fine).
  • 44px minimum hit area on touch targets.

Practical Tips

Scenario Solution
Make buttons feel responsive transform: scale(0.97) on :active
Element appears from nowhere Start from scale(0.95), not scale(0)
Shaky/jittery animations Add will-change: transform
Hover causes flicker Animate child element, not parent
Popover scales from wrong point Set transform-origin to trigger location
Sequential tooltips feel slow Skip delay/animation after first tooltip
Small buttons hard to tap Use 44px minimum hit area (pseudo-element)
Something still feels off Add subtle blur (under 20px) to mask it
Hover triggers on mobile Use @media (hover: hover) and (pointer: fine)

Review Checklist

Issue Fix
transition: all Specify exact properties
scale(0) entry scale(0.95) with opacity: 0
ease-in on UI element ease-out or custom curve
transform-origin: center on popover Trigger location (modals exempt)
Animation on keyboard action Remove entirely
Duration > 300ms on UI element 150-250ms
Hover without media query @media (hover: hover) and (pointer: fine)
Keyframes on rapid triggers CSS transitions
Framer Motion x/y under load transform: "translateX()"
Same enter/exit speed Exit ~20% faster
Everything appears at once Stagger 30-80ms

The Sonner Principles

From building Sonner (13M+ weekly npm downloads):

  1. No hooks, no context, no setup. Insert once, call from anywhere.
  2. Good defaults matter more than options. Ship beautiful out of the box.
  3. Handle edge cases invisibly. Pause timers on hidden tabs. Fill gaps between stacked toasts. Capture pointer during drag.
  4. Match motion to mood. Playful = bouncier. Dashboard = crisp and fast.
  5. Review animations the next day with fresh eyes.

Timeline