Origin-aware animation

Expand or scale from the trigger point to preserve spatial continuity.

Preview

Live preview

An element animates out of its trigger, like a popover growing from the button that opened it instead of from its own center which is the default in CSS.

Preview
Origin-aware animation
Authoritative vocabulary

Definition and close-term distinctions

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

Canonical termorigin-aware-animation

Origin-aware animation

基于触发源的动画
Authoritative definition

An element animates out of its trigger, like a popover growing from the button that opened it instead of from its own center which is the default in CSS.

Chinese translation

元素从触发它的控件位置展开,例如弹出层从打开它的按钮处生长,而非沿用 CSS 默认的自身中心。

Browse all 91 terms
Design contract

Decide before this motion ships

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

Purpose

Grow overlays or expanded content from their trigger so the motion source remains traceable.

Frequency

Use for medium-frequency menus, popovers, and local expansion; full-screen transitions need a more stable spatial rule.

Trigger

Trigger when a specific control opens content, deriving origin from trigger and overlay placement.

Enter / exit

Scale and fade in from the trigger; exit toward the same point and then restore focus.

Interruptibility

Update origin smoothly if placement changes, and begin closing from current geometry.

Gesture and keyboard

No drag is required; the trigger supports keyboard opening, Escape closing, and correct focus management.

Reduced-motion strategy

Preserve the static trigger-overlay relationship and replace scaling travel with a short fade.

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

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

.motion-demo.motion-demo--origin-aware-animation .motion-line:last-child { width: 62%; }

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

.motion-demo.motion-demo--origin-aware-animation .motion-action-row {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.85rem;
}

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

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

.motion-demo.motion-demo--origin-aware-animation button.motion-surface {
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

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

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

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

.motion-demo.motion-demo--origin-aware-animation .motion-surface { transform-origin: top left; animation: motion-origin-aware-animation 220ms cubic-bezier(0.23, 1, 0.32, 1) both; }
@keyframes motion-origin-aware-animation { from { opacity: 0; transform: scale(0.88); } to { opacity: 1; transform: scale(1); } }

@keyframes motion-origin-aware-animation-reduced-fade {
  from { opacity: 0.84; }
  to { opacity: 1; }
}

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

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

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

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

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

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

Pre-delivery review

  • Test origin calculation at all four viewport edges.
  • Ensure the closing path still points to the current trigger after scroll or reflow.
Accessibility

Keep information accessible

Reduced motion

Preserve the static trigger-overlay relationship and replace scaling travel with a short fade.

Parameters

Parameter reference

ParameterRange / optionsDefaultPurpose
duration80ms–1200ms220msControls the time required for one motion cycle.
scale70%–105%88%Controls the starting size relative to the final state.
originTop left / Top right / Bottom left / Bottom right / CenterTop leftSelects motion origin.
easeSoft / Snap / Calm / LinearSoftControls how velocity changes over time.