Before / after slider

Compare before and after states of the same visual with a draggable divider.

Preview

Live preview

A draggable divider that wipes between two overlaid images to compare them.

Preview
Before
After
Authoritative vocabulary

Definition and close-term distinctions

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

Canonical termbefore-after-slider

Before / after slider

前后对比滑杆
Authoritative definition

A draggable divider that wipes between two overlaid images to compare them.

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

Compare two overlaid states of the same visual through a draggable divider.

Frequency

Use for low-frequency, user-driven comparison with a default position that makes the difference immediately clear.

Trigger

Trigger from divider drag, track click, or keyboard arrow keys.

Enter / exit

Show both states immediately and let dragging change only the clipping boundary, with no automatic entrance.

Interruptibility

Stop at the current position on interruption and continue from that ratio on new input.

Gesture and keyboard

Use pointer capture, a 44px handle, arrow-key steps, Home/End, and labels describing both sides.

Reduced-motion strategy

Keep the operable divider while removing position tweening; keyboard and pointer update the static clip immediately.

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--before-after-slider {
  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--before-after-slider *, .motion-demo.motion-demo--before-after-slider *::before, .motion-demo.motion-demo--before-after-slider *::after {
  box-sizing: border-box;
}

.motion-demo.motion-demo--before-after-slider .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--before-after-slider .motion-line {
  display: block;
  width: 100%;
  height: 0.5rem;
  background: #e4e4e7;
  border-radius: 999px;
}

.motion-demo.motion-demo--before-after-slider .motion-line:last-child { width: 62%; }

.motion-demo.motion-demo--before-after-slider .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--before-after-slider .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }

.motion-demo.motion-demo--before-after-slider .motion-action-row {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.85rem;
}

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

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

.motion-demo.motion-demo--before-after-slider button.motion-surface {
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.motion-demo.motion-demo--before-after-slider .motion-replay-button,
.motion-demo.motion-demo--before-after-slider .motion-undo-button,
.motion-demo.motion-demo--before-after-slider .motion-dismiss-button,
.motion-demo.motion-demo--before-after-slider .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--before-after-slider .motion-replay-button:focus-visible,
.motion-demo.motion-demo--before-after-slider .motion-undo-button:focus-visible,
.motion-demo.motion-demo--before-after-slider .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--before-after-slider .motion-pause-button:focus-visible,
.motion-demo.motion-demo--before-after-slider [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--before-after-slider [data-swipe-target]:focus-visible {
  outline: 3px solid rgb(37 99 235 / 32%);
  outline-offset: 3px;
}

.motion-demo.motion-demo--before-after-slider .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--before-after-slider .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--before-after-slider .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--before-after-slider .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--before-after-slider .motion-state--to { background: #18181b; color: white; }

.motion-demo.motion-demo--before-after-slider .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--before-after-slider .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--before-after-slider .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--before-after-slider svg { width: min(100%, 20rem); height: auto; }

.motion-demo.motion-demo--before-after-slider .motion-comparison { position: relative; width: min(100%, 22rem); aspect-ratio: 16 / 9; overflow: hidden; border-radius: 8px; background: #52525b; }
.motion-demo.motion-demo--before-after-slider .motion-before, .motion-demo.motion-demo--before-after-slider .motion-after { position: absolute; inset: 0; display: grid; place-items: center; color: white; }
.motion-demo.motion-demo--before-after-slider .motion-after { overflow: hidden; background: #2563eb; clip-path: inset(0 50% 0 0); }
.motion-demo.motion-demo--before-after-slider .motion-divider { position: absolute; inset: 0 auto 0 0; width: 2px; pointer-events: none; background: white; box-shadow: 0 0 0 1px rgb(0 0 0 / 18%); transform: translate3d(0, 0, 0); }
.motion-demo.motion-demo--before-after-slider .motion-divider::after { content: "↔"; position: absolute; top: 50%; left: 50%; width: 2.25rem; height: 2.25rem; display: grid; place-items: center; color: #18181b; background: white; border-radius: 50%; transform: translate(-50%, -50%); box-shadow: 0 2px 8px rgb(0 0 0 / 18%); }
.motion-demo.motion-demo--before-after-slider .motion-comparison-control { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%; margin: 0; cursor: ew-resize; opacity: 0; }

@keyframes motion-before-after-slider-reduced-fade {
  from { opacity: 0.84; }
  to { opacity: 1; }
}

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

.force-reduced-motion .motion-demo.motion-demo--before-after-slider *,
.force-reduced-motion .motion-demo.motion-demo--before-after-slider *::before,
.force-reduced-motion .motion-demo.motion-demo--before-after-slider *::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--before-after-slider .motion-button,
.force-reduced-motion .motion-demo.motion-demo--before-after-slider [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--before-after-slider [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--before-after-slider [data-spring-target] {
  transform: none !important;
}

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

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

  .motion-demo.motion-demo--before-after-slider *, .motion-demo.motion-demo--before-after-slider *::before, .motion-demo.motion-demo--before-after-slider *::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--before-after-slider .motion-button,
  .motion-demo.motion-demo--before-after-slider [data-reorder-item],
  .motion-demo.motion-demo--before-after-slider [data-swipe-target],
  .motion-demo.motion-demo--before-after-slider [data-spring-target] {
    transform: none !important;
  }

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

Pre-delivery review

  • Keep both images aligned in size, composition, and alt-text strategy.
  • Keep the handle visible at minimum and maximum positions.
Accessibility

Keep information accessible

Reduced motion

Keep the operable divider while removing position tweening; keyboard and pointer update the static clip immediately.

Parameters

Parameter reference

ParameterRange / optionsDefaultPurpose
position10%–90%50%Adjusts divider position.
duration80ms–1200ms180msControls the time required for one motion cycle.