$npx -y skills add hanamizuki/solopreneur --skill apple-designApple'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 (optica
| 1 | # Apple Design |
| 2 | |
| 3 | 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). |
| 4 | |
| 5 | 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. |
| 6 | |
| 7 | ## The Core Idea |
| 8 | |
| 9 | > "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." |
| 10 | |
| 11 | 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. Everything below is a way to get closer to that. |
| 12 | |
| 13 | Apple frames design as serving four human needs: **safety/predictability, understanding, achievement, and joy.** Every rule here serves one of them. |
| 14 | |
| 15 | ## 1. Response — kill latency |
| 16 | |
| 17 | The moment lag appears, the feeling of directness "falls off a cliff." Response is the foundation everything else is built on. |
| 18 | |
| 19 | - **Respond on pointer-down, not on release.** Highlight a button the instant it's pressed. Waiting for `click`/touch-up to show feedback feels dead. |
| 20 | - **Be vigilant about every latency.** Audit debounces, artificial timers, transition waits, and the ~300ms tap delay. Anything on the input path that isn't essential is a regression. |
| 21 | - **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 — never animate only when the gesture completes. |
| 22 | |
| 23 | ```css |
| 24 | /* Feedback lives on the press, and it's instant */ |
| 25 | .button:active { |
| 26 | transform: scale(0.97); |
| 27 | transition: transform 100ms ease-out; |
| 28 | } |
| 29 | ``` |
| 30 | |
| 31 | ## 2. Direct manipulation — 1:1 tracking |
| 32 | |
| 33 | > "Touch and content should move together." |
| 34 | |
| 35 | 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. |
| 36 | |
| 37 | - Use Pointer Events with `setPointerCapture` so tracking continues even when the pointer leaves the element's bounds. |
| 38 | - Track a short **velocity/position history** (last few `pointermove` events), not just the current point — you'll need velocity at release. |
| 39 | |
| 40 | ```js |
| 41 | el.addEventListener('pointerdown', (e) => { |
| 42 | el.setPointerCapture(e.pointerId); |
| 43 | const grabOffset = e.clientY - el.getBoundingClientRect().top; // respect where they grabbed |
| 44 | // ...track position + timestamp history for velocity |
| 45 | }); |
| 46 | ``` |
| 47 | |
| 48 | ## 3. Interruptibility — the single most important principle |
| 49 | |
| 50 | > "The thought and the gesture happen in parallel." |
| 51 | |
| 52 | 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. |
| 53 | |
| 54 | - **Never lock out input during a transition.** |
| 55 | - **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. |
| 56 | - **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. |
| 57 | - **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. (This is what iOS's *additive animations* do natively; on the web, choose a spring library that re-targets from the current velocity.) |
| 58 | - **Decompose 2D motion into independent X and Y springs.** A single spring on a 2D distance desyncs when X and Y have different velocities. |
| 59 | |
| 60 | ## 4. Behavior over animation — use springs |
| 61 | |
| 62 | > "Think of animation as a conversation between you and the object, not something prescribed by the interface." |
| 63 | |
| 64 | A pre-scripted, fixed-durati |