Shake / Wiggle
Use one brief horizontal shake for errors or inputs that need attention.
Live preview
A quick side-to-side jitter signaling an error or rejected input.
Definition and close-term distinctions
English definitions are preserved verbatim from Animation Vocabulary; translations and distinctions help you choose precisely.
shake-wiggleShake / Wiggle
摇晃 / 抖动A quick side-to-side jitter signaling an error or rejected input.
快速左右抖动,用来提示错误或输入被拒绝。
Decide before this motion ships
Purpose, frequency, trigger, timing, interruptibility, and gesture rules shape the implementation.
Use one brief horizontal motion to signal an error, rejection, or input needing attention.
Use infrequently for explicit errors and avoid repetition during continuous validation.
Trigger on submission failure or server rejection while showing readable error text.
Run 2–3 decaying oscillations when the error appears, with no reverse animation when cleared.
Stop shaking on new input and retrigger only after another submission.
No gesture is required; move focus to an error summary or first invalid field with a clear keyboard path.
Remove travel and use error border, icon, and text, with at most one brief color pulse.
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--shake-wiggle {
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--shake-wiggle *, .motion-demo.motion-demo--shake-wiggle *::before, .motion-demo.motion-demo--shake-wiggle *::after {
box-sizing: border-box;
}
.motion-demo.motion-demo--shake-wiggle .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--shake-wiggle .motion-line {
display: block;
width: 100%;
height: 0.5rem;
background: #e4e4e7;
border-radius: 999px;
}
.motion-demo.motion-demo--shake-wiggle .motion-line:last-child { width: 62%; }
.motion-demo.motion-demo--shake-wiggle .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--shake-wiggle .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }
.motion-demo.motion-demo--shake-wiggle .motion-action-row {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 0.85rem;
}
.motion-demo.motion-demo--shake-wiggle .motion-replay-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--shake-wiggle .motion-pausable-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--shake-wiggle button.motion-surface {
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
.motion-demo.motion-demo--shake-wiggle .motion-replay-button,
.motion-demo.motion-demo--shake-wiggle .motion-undo-button,
.motion-demo.motion-demo--shake-wiggle .motion-dismiss-button,
.motion-demo.motion-demo--shake-wiggle .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--shake-wiggle .motion-replay-button:focus-visible,
.motion-demo.motion-demo--shake-wiggle .motion-undo-button:focus-visible,
.motion-demo.motion-demo--shake-wiggle .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--shake-wiggle .motion-pause-button:focus-visible,
.motion-demo.motion-demo--shake-wiggle [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--shake-wiggle [data-swipe-target]:focus-visible {
outline: 3px solid rgb(37 99 235 / 32%);
outline-offset: 3px;
}
.motion-demo.motion-demo--shake-wiggle .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--shake-wiggle .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--shake-wiggle .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--shake-wiggle .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--shake-wiggle .motion-state--to { background: #18181b; color: white; }
.motion-demo.motion-demo--shake-wiggle .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--shake-wiggle .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--shake-wiggle .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--shake-wiggle svg { width: min(100%, 20rem); height: auto; }
.motion-demo.motion-demo--shake-wiggle .motion-surface { animation: motion-shake-wiggle 240ms cubic-bezier(0.77, 0, 0.175, 1) both; border-color: #b34d37; }
@keyframes motion-shake-wiggle { 0%, 100% { transform: none; } 14% { transform: translateX(10px); } 29% { transform: translateX(-9px); } 43% { transform: translateX(7px); } 57% { transform: translateX(-6px); } 71% { transform: translateX(4px); } 86% { transform: translateX(-3px); } }
@keyframes motion-shake-wiggle-reduced-fade {
from { opacity: 0.84; }
to { opacity: 1; }
}
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle {
animation: motion-shake-wiggle-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle *,
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle *::before,
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle *::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--shake-wiggle .motion-button,
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle [data-spring-target] {
transform: none !important;
}
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--shake-wiggle .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
@media (prefers-reduced-motion: reduce) {
.motion-demo.motion-demo--shake-wiggle {
animation: motion-shake-wiggle-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.motion-demo.motion-demo--shake-wiggle *, .motion-demo.motion-demo--shake-wiggle *::before, .motion-demo.motion-demo--shake-wiggle *::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--shake-wiggle .motion-button,
.motion-demo.motion-demo--shake-wiggle [data-reorder-item],
.motion-demo.motion-demo--shake-wiggle [data-swipe-target],
.motion-demo.motion-demo--shake-wiggle [data-spring-target] {
transform: none !important;
}
.motion-demo.motion-demo--shake-wiggle .motion-path { stroke-dashoffset: 0 !important; }
.motion-demo.motion-demo--shake-wiggle .motion-word--from { opacity: 0 !important; visibility: hidden; }
.motion-demo.motion-demo--shake-wiggle .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}Pre-delivery review
- Never rely on shaking in place of error copy.
- Keep amplitude small and avoid moving a page or large container.
Keep information accessible
Remove travel and use error border, icon, and text, with at most one brief color pulse.
Parameter reference
| Parameter | Range / options | Default | Purpose |
|---|---|---|---|
duration | 80ms–1200ms | 240ms | Controls the time required for one motion cycle. |
distance | 0px–30px | 10px | Controls how far the element travels. |
cycles | 1–6 | 3 | Adjusts cycles. |