Hold to confirm

Show cancellable progress during hold-to-confirm interactions to reduce mistakes.

Preview

Live preview

A progress effect that fills up while the user holds a button.

Preview
Authoritative vocabulary

Definition and close-term distinctions

English definitions are preserved verbatim from Animation Vocabulary; translations and distinctions help you choose precisely.

Canonical termhold-to-confirm

Hold to confirm

长按确认
Authoritative definition

A progress effect that fills up while the user holds a button.

Chinese translation

用户持续按住按钮时逐步填满的进度效果。

Browse all 91 terms
Design contract

Decide before this motion ships

Purpose, frequency, trigger, timing, interruptibility, and gesture rules shape the implementation.

Purpose

Confirm a high-risk action through cancellable sustained progress, reducing accidental activation.

Frequency

Reserve for infrequent consequential actions and keep ordinary controls immediate.

Trigger

Begin on sustained press and submit exactly once when the threshold is reached.

Enter / exit

Fill progress continuously from zero, clearing and returning to idle on early release, exit, or cancel.

Interruptibility

Every cancel event stops timing immediately; a new press restarts from zero with a clear state.

Gesture and keyboard

Support pointer capture, touch cancellation, and keyboard holding, plus an accessible alternative confirmation flow.

Reduced-motion strategy

Use static progress or countdown text without filling motion, and allow a standard two-step confirmation.

Output

Copy into your interface

Current parameters update visual-state CSS, semantic HTML, the required vanilla JS runtime, and an interaction prompt.

Updates with parameters
motion.css
.motion-demo.motion-demo--hold-to-confirm {
  position: relative;
  display: grid;
  place-items: center;
  width: min(100%, 34rem);
  min-height: 15rem;
  padding: 2rem;
  overflow: hidden;
  color: #18181b;
  background: #fafafa;
  border: 1px solid #e4e4e7;
  border-radius: 12px;
  isolation: isolate;
}

.motion-demo.motion-demo--hold-to-confirm *, .motion-demo.motion-demo--hold-to-confirm *::before, .motion-demo.motion-demo--hold-to-confirm *::after {
  box-sizing: border-box;
}

.motion-demo.motion-demo--hold-to-confirm .motion-surface {
  position: relative;
  display: grid;
  gap: 0.65rem;
  width: min(100%, 18rem);
  padding: 1.25rem;
  background: #ffffff;
  border: 1px solid #e4e4e7;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgb(0 0 0 / 6%), 0 10px 28px rgb(0 0 0 / 7%);
}

.motion-demo.motion-demo--hold-to-confirm .motion-line {
  display: block;
  width: 100%;
  height: 0.5rem;
  background: #e4e4e7;
  border-radius: 999px;
}

.motion-demo.motion-demo--hold-to-confirm .motion-line:last-child { width: 62%; }

.motion-demo.motion-demo--hold-to-confirm .motion-button {
  position: relative;
  min-height: 2.75rem;
  padding: 0.75rem 1.1rem;
  color: white;
  font: inherit;
  font-weight: 700;
  background: #18181b;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
}

.motion-demo.motion-demo--hold-to-confirm .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }

.motion-demo.motion-demo--hold-to-confirm .motion-action-row {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.85rem;
}

.motion-demo.motion-demo--hold-to-confirm .motion-replay-scene {
  width: min(100%, 22rem);
  display: grid;
  place-items: center;
}

.motion-demo.motion-demo--hold-to-confirm .motion-pausable-scene {
  width: min(100%, 22rem);
  display: grid;
  place-items: center;
}

.motion-demo.motion-demo--hold-to-confirm button.motion-surface {
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.motion-demo.motion-demo--hold-to-confirm .motion-replay-button,
.motion-demo.motion-demo--hold-to-confirm .motion-undo-button,
.motion-demo.motion-demo--hold-to-confirm .motion-dismiss-button,
.motion-demo.motion-demo--hold-to-confirm .motion-pause-button {
  min-height: 2.75rem;
  padding: 0.65rem 0.9rem;
  color: #18181b;
  font: 650 0.78rem/1 ui-sans-serif, system-ui, sans-serif;
  background: #ffffff;
  border: 1px solid #d4d4d8;
  border-radius: 7px;
  cursor: pointer;
}

.motion-demo.motion-demo--hold-to-confirm .motion-replay-button:focus-visible,
.motion-demo.motion-demo--hold-to-confirm .motion-undo-button:focus-visible,
.motion-demo.motion-demo--hold-to-confirm .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--hold-to-confirm .motion-pause-button:focus-visible,
.motion-demo.motion-demo--hold-to-confirm [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--hold-to-confirm [data-swipe-target]:focus-visible {
  outline: 3px solid rgb(37 99 235 / 32%);
  outline-offset: 3px;
}

.motion-demo.motion-demo--hold-to-confirm .motion-status {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.motion-demo.motion-demo--hold-to-confirm .motion-ripple-ink {
  position: absolute;
  width: 12px;
  height: 12px;
  pointer-events: none;
  background: rgb(255 255 255 / 72%);
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0.95);
  opacity: 0;
}

.motion-demo.motion-demo--hold-to-confirm .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--hold-to-confirm .motion-state { position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; background: #ffffff; border: 1px solid #e4e4e7; border-radius: 8px; }
.motion-demo.motion-demo--hold-to-confirm .motion-state--to { background: #18181b; color: white; }

.motion-demo.motion-demo--hold-to-confirm .motion-scroll-track > span { color: #71717a; font: 600 0.72rem/1.2 ui-monospace, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
.motion-demo.motion-demo--hold-to-confirm .motion-orbit-item { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; color: white; background: #2563eb; border-radius: 50%; }
.motion-demo.motion-demo--hold-to-confirm .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--hold-to-confirm svg { width: min(100%, 20rem); height: auto; }

.motion-demo.motion-demo--hold-to-confirm .motion-button { position: relative; overflow: hidden; transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), background-color 120ms ease-out; }
.motion-demo.motion-demo--hold-to-confirm .motion-button:active { transform: scale(0.98); }
.motion-demo.motion-demo--hold-to-confirm .motion-progress { position: absolute; inset: auto 0 0; height: 4px; pointer-events: none; background: #2563eb; clip-path: inset(0 100% 0 0); }
.motion-demo.motion-demo--hold-to-confirm .motion-button[data-state="holding"] { background: #1d4ed8; }
.motion-demo.motion-demo--hold-to-confirm .motion-button[data-state="complete"] { background: #166534; }

@keyframes motion-hold-to-confirm-reduced-fade {
  from { opacity: 0.84; }
  to { opacity: 1; }
}

.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm {
  animation: motion-hold-to-confirm-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}

.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm *,
.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm *::before,
.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm *::after {
  animation: none !important;
  animation-delay: 0ms !important;
  animation-iteration-count: 1 !important;
  transition-property: opacity, color, background-color, border-color !important;
  transition-duration: 160ms !important;
  transition-delay: 0ms !important;
  scroll-behavior: auto !important;
}

.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm .motion-button,
.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm [data-spring-target] {
  transform: none !important;
}

.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--hold-to-confirm .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }

@media (prefers-reduced-motion: reduce) {
  .motion-demo.motion-demo--hold-to-confirm {
    animation: motion-hold-to-confirm-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
  }

  .motion-demo.motion-demo--hold-to-confirm *, .motion-demo.motion-demo--hold-to-confirm *::before, .motion-demo.motion-demo--hold-to-confirm *::after {
    animation: none !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-property: opacity, color, background-color, border-color !important;
    transition-duration: 160ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  .motion-demo.motion-demo--hold-to-confirm .motion-button,
  .motion-demo.motion-demo--hold-to-confirm [data-reorder-item],
  .motion-demo.motion-demo--hold-to-confirm [data-swipe-target],
  .motion-demo.motion-demo--hold-to-confirm [data-spring-target] {
    transform: none !important;
  }

  .motion-demo.motion-demo--hold-to-confirm .motion-path { stroke-dashoffset: 0 !important; }
  .motion-demo.motion-demo--hold-to-confirm .motion-word--from { opacity: 0 !important; visibility: hidden; }
  .motion-demo.motion-demo--hold-to-confirm .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}
Review notes

Pre-delivery review

  • Test pointercancel, window blur, and touch-scroll cancellation.
  • Announce completion concisely through aria-live.
Accessibility

Keep information accessible

Reduced motion

Use static progress or countdown text without filling motion, and allow a standard two-step confirmation.

Parameters

Parameter reference

ParameterRange / optionsDefaultPurpose
duration600ms–2400ms1200msLeaves enough hold time to express deliberate intent; release recovery should stay brief.
scale70%–105%98%Controls the starting size relative to the final state.
easeSoft / Snap / Calm / LinearLinearControls how velocity changes over time.