Morph
Create continuous transformation between before and after layouts of a shared element.
Live preview
One shape smoothly turns into another shape, e.g. Dynamic Island.
Definition and close-term distinctions
English definitions are preserved verbatim from Animation Vocabulary; translations and distinctions help you choose precisely.
morphMorph
形变One shape smoothly turns into another shape, e.g. Dynamic Island.
一个形状平滑地变成另一个形状,例如灵动岛。
continuity-transitionContinuity transition
连续性过渡A change that keeps the user oriented by visually connecting before and after. For example, making the same rectangle bigger and smaller.
通过视觉连接变化前后的状态来帮助用户保持方位感,例如让同一个矩形放大或缩小。
Continuity transition is a principle for preserving before-and-after relationships; Morph is one technique that can create continuity.
shared-element-transitionShared element transition
共享元素过渡An element travels and transforms from one position into another, like a thumbnail expanding into a card.
同一个元素从一个位置移动并变换到另一个位置,例如缩略图展开成卡片。
Shared element transition moves and transforms the same element across layouts; Morph emphasizes one shape becoming another.
layout-animationLayout animation
布局动画When an element's size or position changes, it animates to the new spot instead of snapping.
元素尺寸或位置变化时,以动画移动到新状态,而非瞬间跳变。
Layout animation smooths size or position changes; Morph changes an element's shape.
Decide before this motion ships
Purpose, frequency, trigger, timing, interruptibility, and gesture rules shape the implementation.
Turn one shape smoothly into another while preserving object identity.
Reserve for low-frequency brand marks, icons, and high-value state changes; keep common controls subtler.
Trigger when the object's state genuinely changes, with understandable correspondence between shapes.
Use one geometric object and interpolation path across states, normalizing path points when needed.
Retarget from the current interpolated shape instead of returning to an endpoint.
Map gesture-driven morph progress continuously to input, with keyboard toggles for discrete controls.
Use a static state replacement or short crossfade while preserving object position and size.
Copy into your interface
Current parameters update visual-state CSS, semantic HTML, the required vanilla JS runtime, and an interaction prompt.
.motion-demo.motion-demo--morph {
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--morph *, .motion-demo.motion-demo--morph *::before, .motion-demo.motion-demo--morph *::after {
box-sizing: border-box;
}
.motion-demo.motion-demo--morph .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--morph .motion-line {
display: block;
width: 100%;
height: 0.5rem;
background: #e4e4e7;
border-radius: 999px;
}
.motion-demo.motion-demo--morph .motion-line:last-child { width: 62%; }
.motion-demo.motion-demo--morph .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--morph .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }
.motion-demo.motion-demo--morph .motion-action-row {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 0.85rem;
}
.motion-demo.motion-demo--morph .motion-replay-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--morph .motion-pausable-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--morph button.motion-surface {
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
.motion-demo.motion-demo--morph .motion-replay-button,
.motion-demo.motion-demo--morph .motion-undo-button,
.motion-demo.motion-demo--morph .motion-dismiss-button,
.motion-demo.motion-demo--morph .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--morph .motion-replay-button:focus-visible,
.motion-demo.motion-demo--morph .motion-undo-button:focus-visible,
.motion-demo.motion-demo--morph .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--morph .motion-pause-button:focus-visible,
.motion-demo.motion-demo--morph [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--morph [data-swipe-target]:focus-visible {
outline: 3px solid rgb(37 99 235 / 32%);
outline-offset: 3px;
}
.motion-demo.motion-demo--morph .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--morph .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--morph .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--morph .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--morph .motion-state--to { background: #18181b; color: white; }
.motion-demo.motion-demo--morph .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--morph .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--morph .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--morph svg { width: min(100%, 20rem); height: auto; }
.motion-demo.motion-demo--morph .motion-surface { width: min(100%, 18rem); transform-origin: center; animation: motion-morph 260ms cubic-bezier(0.23, 1, 0.32, 1) both; }
@keyframes motion-morph { from { opacity: 0.65; transform: translateX(-17px) scale(0.86); } to { opacity: 1; transform: none; } }
@keyframes motion-morph-reduced-fade {
from { opacity: 0.84; }
to { opacity: 1; }
}
.force-reduced-motion .motion-demo.motion-demo--morph {
animation: motion-morph-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.force-reduced-motion .motion-demo.motion-demo--morph *,
.force-reduced-motion .motion-demo.motion-demo--morph *::before,
.force-reduced-motion .motion-demo.motion-demo--morph *::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--morph .motion-button,
.force-reduced-motion .motion-demo.motion-demo--morph [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--morph [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--morph [data-spring-target] {
transform: none !important;
}
.force-reduced-motion .motion-demo.motion-demo--morph .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--morph .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--morph .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
@media (prefers-reduced-motion: reduce) {
.motion-demo.motion-demo--morph {
animation: motion-morph-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.motion-demo.motion-demo--morph *, .motion-demo.motion-demo--morph *::before, .motion-demo.motion-demo--morph *::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--morph .motion-button,
.motion-demo.motion-demo--morph [data-reorder-item],
.motion-demo.motion-demo--morph [data-swipe-target],
.motion-demo.motion-demo--morph [data-spring-target] {
transform: none !important;
}
.motion-demo.motion-demo--morph .motion-path { stroke-dashoffset: 0 !important; }
.motion-demo.motion-demo--morph .motion-word--from { opacity: 0 !important; visibility: hidden; }
.motion-demo.motion-demo--morph .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}Pre-delivery review
- Distinguish morphing from shared-element and layout transitions.
- Check intermediate shapes for self-intersection or flicker.
Keep information accessible
Use a static state replacement or short crossfade while preserving object position and size.
Parameter reference
| Parameter | Range / options | Default | Purpose |
|---|---|---|---|
duration | 80ms–1200ms | 260ms | Controls the time required for one motion cycle. |
mode | Shape morph / Continuity transition / Shared element / Layout change | Shape morph | Selects how continuity is preserved between states. |
distance | 0px–120px | 48px | Controls how far the element travels. |
scale | 70%–105% | 86% | Controls the starting size relative to the final state. |
ease | Soft / Snap / Calm / Linear | Soft | Controls how velocity changes over time. |