Apple's approach to interface design and fluid, physical motion, translated for the web. Use when building or reviewing gesture-driven UI, spring animations, drag/swipe/sheet interactions, momentum and interruptible transitions, translucent materials and depth, typography (optical sizing, tracking, leading), reduced-motion, or the design foundations (feedback, spatial consistency, restraint) behind Apple-style interfaces. Source: github.com/emilkowalski/skills.
Scanned 9/1/2026
Install to Claude Code
npx -y skills add alunadev/ald-skills --skill apple-design --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Apple Design?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/alunadev-apple-design)More formats (shields.io, HTML) on the badges page.
---
name: apple-design
description: >
Apple's approach to interface design and fluid, physical motion, translated for the web. Use
when building or reviewing gesture-driven UI, spring animations, drag/swipe/sheet
interactions, momentum and interruptible transitions, translucent materials and depth,
typography (optical sizing, tracking, leading), reduced-motion, or the design foundations
(feedback, spatial consistency, restraint) behind Apple-style interfaces.
Source: github.com/emilkowalski/skills.
---
# Apple Design
How Apple builds interfaces that stop feeling like a computer and start feeling like an
extension of you. This knowledge comes from Apple's WWDC design talks — chiefly *Designing
Fluid Interfaces* (WWDC 2018) — distilled and translated into the web platform (CSS, Pointer
Events, `requestAnimationFrame`, spring libraries like Motion/Framer Motion).
The through-line: an interface feels alive when motion starts from the current on-screen
value, inherits the user's velocity, projects momentum forward, and can be grabbed and
reversed at any instant. Springs are the tool that makes all of this natural, because they are
inherently interruptible and velocity-aware.
## The Core Idea
> "When we align the interface to the way we think and move, something magical happens — it
> stops feeling like a computer and starts feeling like a seamless extension of us."
An interface is fluid when it behaves like the physical world: things respond instantly, move
continuously, carry momentum, resist at boundaries, and can be redirected mid-motion.
Apple frames design as serving four human needs: safety/predictability, understanding,
achievement, and joy. Every rule here serves one of them.
## 1. Response — kill latency
The moment lag appears, the feeling of directness "falls off a cliff." Response is the
foundation everything else is built on.
- Respond on pointer-down, not on release. Highlight a button the instant it's pressed.
- Be vigilant about every latency — debounces, artificial timers, transition waits, the
~300ms tap delay. Anything on the input path that isn't essential is a regression.
- Feedback must be continuous during the interaction, not just at the end. For a drag, slider,
or drawer, update the UI 1:1 with the pointer the whole way through.
```css
/* Feedback lives on the press, and it's instant */
.button:active { transform: scale(0.97); transition: transform 100ms ease-out; }
```
## 2. Direct manipulation — 1:1 tracking
> "Touch and content should move together."
When the user drags something, it must stay glued to the finger — and respect the offset from
where they grabbed it. Snapping to the element's center on grab breaks the illusion
immediately.
- Use Pointer Events with `setPointerCapture` so tracking continues even when the pointer
leaves the element's bounds.
- Track a short velocity/position history (last few `pointermove` events) — you'll need
velocity at release.
```js
el.addEventListener('pointerdown', (e) => {
el.setPointerCapture(e.pointerId);
const grabOffset = e.clientY - el.getBoundingClientRect().top; // respect where they grabbed
// ...track position + timestamp history for velocity
});
```
## 3. Interruptibility — the single most important principle
> "The thought and the gesture happen in parallel."
Every animation must be interruptible and redirectable at any moment. A user must be able to
grab a moving element mid-flight and reverse it without waiting for the animation to finish. A
closing modal the user grabs again should follow the finger — not finish closing first, then
reopen.
- Never lock out input during a transition.
- Always animate from the presentation (current) value, never the target value. On interrupt,
read the element's live on-screen transform and start the new animation from there. Starting
from the logical/target value causes a visible jump.
- Avoid CSS transitions and `@keyframes` for anything gesture-driven — they can't be smoothly
grabbed and reversed mid-flight. Springs animate from the current value by default, which is
exactly what interruption needs.
- When a gesture reverses, blend velocity — don't hard-cut it. Replacing one animation with
another at a reversal creates a velocity discontinuity, a "brick wall." Spring libraries that
carry velocity through a re-target avoid it.
- Decompose 2D motion into independent X and Y springs. A single spring on a 2D distance
desyncs when X and Y have different velocities.
## 4. Behavior over animation — use springs
> "Think of animation as a conversation between you and the object, not something prescribed
> by the interface."
A pre-scripted, fixed-duration animation can't respond to new input. A spring can — new input
just changes the target, and the motion stays continuous. Reach for springs for anything a
user can touch.
Apple deliberately replaced the physics triplet (mass/stiffness/damping) with two
designer-friendly parameters:
- **Damping ratio** — controls overshoot. 1.0 = critically damped, no bounce, smooth settle.
< 1.0 = overshoots and oscillates. Lower = bouncier.
- **Response** — how quickly the value reaches the target, in seconds. Lower = snappier. This
is not "duration" — a spring has no fixed duration; its settle time emerges from the
parameters.
Defaults: start most UI at damping 1.0 (critically damped) — graceful and non-distracting. Add
bounce (damping ~0.8) only when the gesture itself carried momentum (a flick, a throw, a drag
release). Overshoot on a menu that just faded in feels wrong; overshoot on a card you flicked
feels right.
Concrete values Apple ships:
| Interaction | Damping | Response |
| --- | --- | --- |
| Move / reposition (e.g. PiP) | 1.0 | 0.4 |
| Rotation | 0.8 | 0.4 |
| Drawer / sheet | 0.8 | 0.3 |
Web mapping (Motion / Framer Motion): the `bounce` + `duration` spring API maps closely to
Apple's damping + response. A safe house style is `damping: 1.0` springs everywhere by
default; reserve bounce for momentum-driven, physical interactions.
```js
import { animate } from 'motion';
// Critically damped default (no overshoot)
animate(el, { y: 0 }, { type: 'spring', bounce: 0, duration: 0.4 });
// Momentum interaction — a little bounce, only because a flick preceded it
animate(el, { y: target }, { type: 'spring', bounce: 0.2, duration: 0.4 });
```
## 5. Velocity handoff — the seam between drag and animation
When a gesture ends, the animation must continue at the finger's exact velocity, so there's no
visible seam between dragging and animating. This is the detail that most separates "fluid"
from "fine."
Pass the pointer's release velocity as the spring's initial velocity. Some spring APIs want
relative velocity — normalize it by the remaining distance to the target:
```
relativeVelocity = gestureVelocity / (targetValue − currentValue)
```
Example: element at y=50, target y=150 (100px to go), finger moving 50px/s → initial spring
velocity = 50 / 100 = 0.5. Framer Motion / Motion take absolute px/s velocity directly (the
`velocity` option), so you usually hand it the raw value.
## 6. Momentum projection — animate to where the gesture is going
> "Take a small input and make a big output."
Don't snap to the nearest boundary from the release point. Use velocity to project the resting
position — exactly like scroll deceleration — then snap to the target nearest that projected
point. This is what makes a flick feel like it throws the element.
Apple's exact projection function (from the *Designing Fluid Interfaces* sample code):
```js
// decelerationRate ≈ 0.998 for normal scroll feel; 0.99 for snappier
function project(initialVelocity /* px/s */, decelerationRate = 0.998) {
return (initialVelocity / 1000) * decelerationRate / (1 - decelerationRate);
}
const projectedEndpoint = currentPosition + project(releaseVelocity);
const target = nearestSnapPoint(projectedEndpoint); // choose target from the projection
animateSpringTo(target, { velocity: releaseVelocity }); // then hand off velocity (§5)
```
Note: the physics-textbook `v²/(2·decel)` is not what Apple ships — use the exponential-decay
form above. This is the standard behavior in good bottom-sheets and carousels (Vaul, Embla).
## 7. Spatial consistency — symmetric paths, anchored origins
> "If something disappears one way, we expect it to emerge from where it came."
- Enter and exit along the same path. A panel that slides in from the right must dismiss to
the right.
- Anchor interactions to their source. A menu, popover, or sheet should originate from the
element that triggered it — set `transform-origin` to the trigger (see `emil-design-eng`'s
origin-aware popover rule).
- Mirror the easing on reversible transitions so the outbound path matches the return path
(use inverse cubic-bézier control points for the two directions).
## 8. Hint in the direction of the gesture
Humans predict a final state from a trajectory. Intermediate motion should telegraph where
things are going — Control Center modules "grow up and out toward your finger." Make the
in-between frames point at the outcome, not just interpolate blindly to it.
## 9. Rubber-banding — soft boundaries
At an edge, resist progressively instead of stopping hard. A hard stop reads as "frozen";
continuous resistance reads as "responsive, but there's nothing more here."
```js
// The further past the bound, the less the element follows — real things slow before they stop
function rubberband(overshoot, dimension, constant = 0.55) {
return (overshoot * dimension * constant) / (dimension + constant * Math.abs(overshoot));
}
```
## 10. Gesture design details (the "feel" checklist)
- **Tap**: highlight on touch-down (instant), commit on touch-up. Add ~10px of
hysteresis/hit padding around the target, and allow cancel-by-dragging-away and back.
- **Drag/swipe**: require a small movement threshold (hysteresis, ~10px) before committing to
a direction, then track 1:1.
- Detect all plausible gestures in parallel from the first move, then confidently cancel the
losers once intent is clear. Avoid recognizers that only report a final state
(`swipeleft`-type events) — they throw away the continuous tracking you need for feedback.
- Minimize disambiguation delays. Double-tap detection unavoidably delays single taps; only
pay that cost where double-tap truly exists.
## 11. Frame-level smoothness
Smoothness is about what's in the frames, not just the frame rate.
- Keep the per-frame positional change below the perception threshold to avoid strobing.
- For very fast motion, a subtle motion blur / stretch encodes speed and reads better than a
hard sharp streak.
- `requestAnimationFrame` is the web's display-synced clock (Apple uses CADisplayLink).
Animate only compositor-friendly properties — `transform` and `opacity` — and hint with
`will-change` where motion is imminent.
## 12. Materials & depth — translucency conveys hierarchy
Apple uses translucent materials as a floating functional layer that brings structure without
stealing focus. On the web, approximate with `backdrop-filter`.
- Build nav/toolbars/sheets as translucent layers (`backdrop-filter: blur()` + a
semi-transparent background) with content scrolling underneath — not opaque bars.
- Material weight encodes hierarchy: darker/heavier materials separate structural regions
(sidebars); lighter materials draw attention to interactive elements (buttons). Never stack
a light translucent surface on another — legibility collapses.
- Bigger surfaces should read as thicker: stronger blur + a deeper shadow than small chips.
- Dim to focus, separate to keep flow. A modal task pairs the surface with a dimming scrim and
pushes the background back/down. A parallel, non-blocking panel uses translucency and offset
without a scrim. For stacked sheets, progressively dim and push back each parent layer.
- Vibrancy keeps text legible over changing backgrounds. Over blurred/translucent surfaces,
don't use flat gray text — use higher-contrast, slightly heavier weight, and a small
letter-spacing bump. Put color on a solid layer, not the translucent foreground.
- Scroll edge effects, not hard dividers. Instead of a 1px border under a sticky header, fade
a small blur/gradient mask where content meets floating chrome.
- Materialize, don't just fade. For glass/blur surfaces, animate blur radius and scale
together on enter/exit, so the surface reads as a real material arriving.
```css
.toolbar {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(20px) saturate(180%);
border-top: 1px solid rgba(255, 255, 255, 0.4); /* bright top edge = light catching the material */
}
```
## 13. Multimodal feedback — motion + sound + haptics
Three rules for combining senses (from *Designing Audio-Haptic Experiences*):
- **Causality** — it must be obvious what caused the feedback. Trigger it on the actual causal
event, and match its character to the action's physicality.
- **Harmony** — the visual, the sound, and the haptic must fire on the same frame. Latency
between them destroys the illusion.
- **Utility** — add feedback only where it earns its place. Reserve haptics/sound for
meaningful moments (success, error, commit, snap). Over-feedback trains users to ignore all
of it.
## 14. Reduced motion & accessibility
Reduced motion doesn't mean no feedback — it means a gentler, non-vestibular equivalent.
Respond to three independent signals:
- `prefers-reduced-motion: reduce` — replace slides/springs/parallax with short opacity
cross-fades or static transitions. Drop elastic/overshoot. Keep opacity/color changes that
aid comprehension.
- `prefers-reduced-transparency: reduce` — make translucent surfaces frostier/solid: raise
background opacity, drop the blur.
- `prefers-contrast: more` — near-solid backgrounds with a defined, contrasting border.
Also: avoid full-viewport moving backgrounds, slow looping oscillations (near 0.2 Hz / one
cycle per 5s), and abrupt brightness jumps (ease dark↔light theme changes).
```css
@media (prefers-reduced-motion: reduce) {
.sheet { transition: opacity 200ms ease; transform: none !important; }
}
@media (prefers-reduced-transparency: reduce) {
.toolbar { background: white; backdrop-filter: none; }
}
```
## 15. Typography — optical sizing, tracking, leading
Apple designs type to change shape with size; the same discipline applies on the web. (From
*The Details of UI Typography*, WWDC 2020.)
- **Tracking** (letter-spacing) is size-specific — never one value for all sizes. Large
display text wants negative tracking; small text wants slightly positive tracking. Tighten
headings, leave body near 0.
- **Leading** (line-height) tracks size inversely. Tight on large headings, looser on body
copy.
- Build hierarchy from weight + size + leading as a set, not size alone. Emphasize with
weight — it adds presence without taking more space.
- Respect the user's text-size setting (Dynamic Type). Scale layout with the text — spacing in
rem/em, not fixed px.
- Default to the platform's system font before a custom face; it already ships optical
sizing, tracking tables, and legibility tuning.
```css
:root { font: 100%/1.5 system-ui, sans-serif; } /* body: system font, comfortable leading */
.display {
font-size: clamp(2rem, 5vw, 4rem);
line-height: 1.05; /* tight leading for large text */
letter-spacing: -0.02em; /* negative tracking as it grows */
font-optical-sizing: auto;
}
```
## 16. Design foundations — the eight principles
The motion and craft above serve Apple's eight design principles (*Principles of Great
Design*, WWDC 2026):
1. **Purpose.** Make with intention; decide what not to build. Every feature asks for the
user's time, attention, and trust.
2. **Agency.** Keep people in control: offer choices, don't force a single path. Easy undo for
slips; a confirmation dialog only for genuinely destructive, irreversible actions.
3. **Responsibility.** Act in the user's interest. Privacy at the right moment, only for
what's needed. Anticipate misuse and harm, especially with AI.
4. **Familiarity.** Build on what people already know. Consistency: things that look the same
must behave the same and live in the same place. Only break a pattern if you can prove it's
better.
5. **Flexibility.** Design for different contexts, devices, and the full range of abilities.
Adapt to the platform and situation; let people personalize when no single layout fits.
6. **Simplicity — not minimalism.** Strip the unnecessary so the core purpose shines. Be
concise (plain language, fewer steps) and clear (hierarchy — order, spacing, contrast).
Show the common path first, advanced options one level deeper.
7. **Craft.** Uncompromising attention to detail builds trust. Nothing is random — every
spacing, timing, and alignment value is a deliberate choice you can defend.
8. **Delight.** The result of getting the other seven right, not confetti tacked on top.
Decide the emotion you want people to feel and reinforce it in every decision.
Tactical rules that serve these:
- **Feedback comes in four kinds**: status, completion, warning, error. Confirm meaningful
actions, expose ongoing status, warn before problems, validate inline (not on submit).
- **Wayfinding.** Every screen should answer: Where am I? Where can I go? What's there? How do
I get out? Never trap the user.
- **Grouping & mapping.** Proximity implies relationship; place a control near what it
affects. If you need a label to explain a control, the mapping is weak.
- **Direct, specific labels beat safe generic ones.** Name nav items for their contents
("Progress", "Library"), not vague umbrellas ("Home").
## 17. Process
- **Prototype interactively** — an interactive demo is worth "a million static designs." A
working prototype also sets a concrete bar that prevents a mediocre final implementation.
- **Design interaction and visuals together.** "You shouldn't be able to tell where one ends
and the other begins." Motion is not a layer added after the pixels.
- **Test with real people in real context**, and review motion with fresh eyes — play it in
slow motion / frame-by-frame to catch what's invisible at full speed.
## Quick Reference
| Need | Technique | Concrete value |
| --- | --- | --- |
| Default UI spring | Critically damped, no overshoot | damping 1.0, response 0.3–0.4 |
| Momentum / flick spring | Under-damped, slight bounce | damping ~0.8, response 0.3–0.4 |
| Gesture → spring velocity | Hand off release velocity | `gestureVelocity / (target − current)` if normalized |
| Flick landing point | Project momentum | `current + (v/1000)·d/(1−d)`, d ≈ 0.998 |
| Interrupt cleanly | Start from presentation (live) value | read the on-screen transform |
| Avoid reversal "brick wall" | Carry velocity through re-target | spring that blends velocity |
| Reversible transition | Mirror the easing curve | inverse cubic-bézier |
| Decide reverse vs. commit | Use velocity sign, not position | at release |
| 1:1 drag | Pointer Events + capture | respect the grab offset |
| Feedback | On pointer-down, continuous | never only at the end |
| Boundary | Rubber-band, don't hard-stop | progressive resistance |
| Translucent chrome | `backdrop-filter` layer | content scrolls under |
| Type tracking | Size-specific, never fixed | tighten large text (-0.02em), body near 0 |
| Reduced motion | Cross-fade, not slide/spring | `@media (prefers-reduced-motion)` |
## See Also
- `emil-design-eng` — the general-purpose animation philosophy this skill's gesture/spring
model supplements.
- `animate` — plugs these values into the build sequence's step 5-6.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!