Teaches interface-feel craft: timing, motion, perceived performance, and micro-interactions. Triggers when someone asks to make a UI feel fast, polished, or responsive, or wants timing/easing/animation values for web or app interfaces.
./install.sh interface-feel-craftname: "interface_feel_craft" description: "Teaches interface-feel craft: timing, motion, perceived performance, and micro-interactions. Triggers when someone asks to make a UI feel fast, polished, or responsive, or wants timing/easing/animation values for web or app interfaces."
Interfaces are judged by how they feel, not by how fast they are. A 200ms response can feel instant with the right feedback, and a 100ms response can feel broken with none. This skill teaches the timing principles, motion rules, perceived-performance techniques, and micro-interaction structure that make interfaces feel good.
Every user action must get acknowledgment inside ~100ms. This is the hard floor of human perception: responses within 100ms feel instantaneous. Below that, do not animate; just respond. The layers:
If an action cannot complete inside 100ms, ship the feedback layer anyway. A spinner shown late feels slower than one shown instantly.
Use a duration scale. Bigger change, longer animation. Reference Material Design 3 tokens: short1 50ms, short2 100ms, medium1 200ms, medium2 300ms, long1 400ms, long2 500ms.
Easing is what makes motion feel physical instead of mechanical. Choose by where the element is going:
cubic-bezier(0, 0, 0, 1).cubic-bezier(0.3, 0, 1, 1).cubic-bezier(0.2, 0, 0, 1) also works for most transitions.ease; it is a compromise curve that feels neither intentional nor natural.For 60fps motion, animate transform (translate, scale, rotate) and opacity only. Animating width, height, top, left, margin, or padding forces layout and paint every frame and will stutter under real page weight. A panel reveal should be transform: scaleY() with transform-origin: top, never a height transition.
Dan Saffer's structure for any small interaction:
Worked example, a button that submits a form:
css
.btn {
transition: transform 150ms cubic-bezier(0.2, 0, 0, 1),
background-color 150ms cubic-bezier(0.2, 0, 0, 1);
}
.btn:active { transform: scale(0.97); } /* press: instant confirmation */
.btn.loading { pointer-events: none; } /* rule: no double submit */
.btn .spinner { display: none; }
.btn.loading .spinner { display: inline-block; } /* feedback: working state */
.btn.loading .label { visibility: hidden; } /* keep button dimensions: no layout shift */
On click: label hides, spinner shows, dimensions stay fixed (rule), button becomes non-interactive (mode). On success: 300ms color transition to the success state plus an icon swap. On failure: an error state with a message, and the button returns to idle with the label back.
When several elements move together, stagger them 50-200ms apart, leading element first. Sequential beats simultaneous: a modal that fades its backdrop in while the panel slides up 80ms later feels layered and intentional; both at once feels flat. Keep the whole sequence under 1000ms.
prefers-reduced-motion is an accessibility need, not a preference. Parallax, zoom, and large spatial movement can cause nausea and dizziness. The strategy: replace motion with an instant cut. Keep functional state changes (color, opacity) but drop translate/scale movement.
css
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
When applying this skill to a design or component, produce:
references/sources.md.See references/sources.md for citations and the research base.
publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e
No ratings yet — be the first to rate it.