Best for
- 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)…
emilkowalski/skills/skills/apple-design/SKILL.md
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.
Decision brief
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, r…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/emilkowalski/skills --skill "skills/apple-design"Inspect the Agent Skill "apple-design" from https://github.com/emilkowalski/skills/blob/de33dbed000212b54400a33767d1e4d03654db2a/skills/apple-design/SKILL.md at commit de33dbed000212b54400a33767d1e4d03654db2a. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Prototype interactively — an interactive demo is worth "a million static designs." You discover the interface by building and playing with it; a working prototype also sets a concrete bar that prevents a mediocre final…
"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."
The moment lag appears, the feeling of directness "falls off a cliff." Response is the foundation everything else is built on.
"Touch and content should move together."
"The thought and the gesture happen in parallel."
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 95/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 25,890 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
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.
"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. Everything below is a way to get closer to that.
Apple frames design as serving four human needs: safety/predictability, understanding, achievement, and joy. Every rule here serves one of them.
The moment lag appears, the feeling of directness "falls off a cliff." Response is the foundation everything else is built on.
click/touch-up to show feedback feels dead./* Feedback lives on the press, and it's instant */
.button:active {
transform: scale(0.97);
transition: transform 100ms ease-out;
}
"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.
setPointerCapture so tracking continues even when the pointer leaves the element's bounds.pointermove events), not just the current point — you'll need velocity at release.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
});
"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.
@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."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. Think in these:
1.0 = critically damped, no bounce, smooth settle. < 1.0 = overshoots and oscillates. Lower = bouncier.Defaults:
1.0 (critically damped) — graceful and non-distracting.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.
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 });
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 (velocity option), so you usually hand it the raw value.
"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):
// 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).
"If something disappears one way, we expect it to emerge from where it came."
transform-origin to the trigger, so the spatial relationship between button and content is obvious. (This is the same origin-awareness point as popovers scaling from their trigger, not their center.)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.
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." Apply damping that increases the further past the boundary the user drags.
// 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));
}
swipeleft-type events) — they throw away the continuous tracking you need for feedback.Smoothness is about what's in the frames, not just the frame rate.
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.Apple uses translucent materials as a floating functional layer that brings structure without stealing focus. On the web, approximate with backdrop-filter.
backdrop-filter: blur() + a semi-transparent background) with content scrolling underneath — not opaque bars that consume a fixed strip..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 */
}
Three rules for combining senses (from Designing Audio-Haptic Experiences):
Reduced motion doesn't mean no feedback — it means a gentler, non-vestibular equivalent. Respond to three independent signals and bake them into your components:
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). Make large moving objects semi-transparent while they travel, and fade big surfaces out during a large reposition and back in once settled.
@media (prefers-reduced-motion: reduce) {
.sheet { transition: opacity 200ms ease; transform: none !important; }
}
@media (prefers-reduced-transparency: reduce) {
.toolbar { background: white; backdrop-filter: none; }
}
Apple designs type to change shape with size; the same discipline applies on the web. (From The Details of UI Typography, WWDC 2020.)
letter-spacing is wrong somewhere. Tighten headings, leave body near 0.rem/em, not fixed px — so a larger font doesn't break the layout.: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;
}
The motion and craft above serve Apple's eight design principles (Principles of Great Design, WWDC 2026). Use these as the names you reason with:
Tactical rules that serve these:
| 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) |
Alternatives
coreyhaines31/marketingskills
When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program
coreyhaines31/marketingskills
When the user wants to reduce churn, build cancellation flows, set up save offers, recover failed payments, or implement retention strategies. Also use when the user mentions 'churn,' 'cancel flow,' 'offboarding,' 'save offer,' 'dunning,' 'failed payment recovery,' 'win-back,' 'retention,' 'exit survey,' 'pause subscription,' 'involuntary churn,' 'people keep canceling,' 'churn rate is too high,' 'how do I keep users,' or 'customers are leaving.' Use this whenever someone is losing subscribers o
prowler-cloud/prowler
PostgreSQL indexing best practices for Prowler: index design, partial indexes, partitioned table indexing, EXPLAIN ANALYZE validation, concurrent operations, monitoring, and maintenance. Trigger: When creating or modifying PostgreSQL indexes, analyzing query performance with EXPLAIN, debugging slow queries, reviewing index usage statistics, reindexing, dropping indexes, or working with partitioned table indexes. Also trigger when discussing index strategies, partial indexes, or index maintenance
JasonColapietro/suede-creator-skills
Suede-owned experimentation discipline for hypotheses, sample sizing, test duration, significance, and repeatable experiment programs. Use when comparing variants, deciding whether a result is reliable, or building an experiment backlog and cadence. NOT FOR: analytics instrumentation (use suede-analytics), post-click conversion diagnosis (use suede-site-alchemy), or writing the variant copy itself (use suede-copy).