motion system

Motion Taste System

Good motion should explain state change without making the interface feel busy.

waiting for sustained input

slow confirmationfast release

the starting point

The starting point is a control that needs confirmation. If the action happens instantly, it feels risky. If it opens a modal, it feels heavier than the task.

setting up the structure

The button carries two states at once: the normal surface and the confirming surface. The confirming layer is revealed inside the control, so progress stays attached to the user's finger.

That keeps the component small. No dialog, no extra screen, just a visible hold state where the action already lives.

polishing it up

The hold can be slow because it represents intent. The release should be quick because cancellation should feel effortless.

A small press scale gives immediate feedback, so the slower confirmation still feels responsive.

video controls as proof surface

The player chrome should appear only when it helps someone inspect the demo. The video stays the proof; the controls sit as a thin translucent layer over moving media.

Glass works here because it improves legibility over changing frames. If it becomes a permanent visual effect, it starts competing with the work.

## Easing Decision Flowchart

Is the element entering or exiting the viewport?
├── Yes -> ease-out
└── No
    ├── Is it moving or morphing on screen?
    │   ├── Yes -> ease-in-out
    │   └── No
    │       ├── Is it a hover change?
    │       │   ├── Yes -> ease
    │       │   └── No
    │       │
    │       └── Is it constant motion?
    │           ├── Yes -> linear
    │           └── No  -> ease-out

Choosing motion from a rule keeps the interface quiet instead of leaving every easing to taste.

rules I keep

press 160ms instant tactile feedback

hold 1200ms sustained intent, linear progress

release 120-180ms fast cancellation so it never feels stuck

video chrome hover/focus only mobile stays visible because hover does not exist

final code

.hold-action {
  transition: transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
}

.hold-action[data-state="holding"] {
  transform: scale(0.985);
}

.hold-action__overlay {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 180ms cubic-bezier(0.22, 1, 0.36, 1);
}

.hold-action[data-state="holding"] .hold-action__overlay {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1200ms linear;
}
3.1.79c29updated 0d 0h 0m 0s before