AnimateShake
The one effect in the set that says no. A password that was wrong, a form that would not send, a row that could not be dropped where it was let go: every other animation here is content arriving, and this one is an answer.
import { AnimateShake } from 'neba';
<AnimateShake key={attempts} play={failed}>
<TextField label="Passphrase" error={message} />
</AnimateShake>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| staggershared | number | 0 | How long after one child the next one starts, in milliseconds. 0 animates the box itself; anything above it moves the effect onto each child in turn |
| durationStepshared | number | 0 | How much longer each successive child takes, in milliseconds. Negative speeds them up down the list, and never past zero |
| reverseshared | boolean | false | Runs the children from the last to the first. Only the order reverses |
| durationshared | number | 420 | How long one run takes, in milliseconds |
| delayshared | number | 0 | How long before it starts, in milliseconds |
| easing | string | — | The easing curve, as CSS writes it. Defaults to the house curve |
| repeatshared | number | 'infinite' | 1 | How many times it runs |
| alternateshared | boolean | false | Runs every other pass backwards, so a repeat returns instead of jumping |
| triggershared | 'mount' | 'visible' | 'hover' | 'manual' | 'mount' | What starts it. visible is on scrolling into view, hover is under the pointer (focus counts), manual is whatever play says |
| play | boolean | — | Runs it when trigger is manual. Each false → true starts it over |
| onceshared | boolean | true | With trigger="visible", whether it runs only the first time. Off, it runs again on every return |
| thresholdshared | number | 0.2 | With trigger="visible", how much of the element has to be on screen, from 0 to 1 |
| pausedshared | boolean | false | Holds the animation where it is |
| distance | number | string | 6 | How far it travels at the widest point. Numbers are pixels |
| render | useRender.RenderProp | — | Renders something other than a div (render={<label />}). Base UI's own escape hatch |
| children | ReactNode | — | What is shaken |
Every other <div> attribute passes through to the root. The settings shared by every Animate* are defined in prop conventions.
Unlike every other effect here it defaults to trigger="manual": a shake that runs on mount is decoration, and decoration that moves is what a reader learns to ignore. Bind play to the thing that failed. A fresh key on each rejection is what rewinds it, so the second wrong answer moves as much as the first.
There is no mode. It starts and ends where the element sits, so a run that is interrupted leaves nothing off its mark, and never give it a repeat.
Examples
distance
How far it travels at the widest point: a CSS length, or a number of pixels. 6 by default: this is a head shaken, not a thing thrown.
Why this one is an exception
The house rule is that a control is never transformed. It applies to a control's resting states (hover, press, on, off), which colour expresses more clearly than movement does. A shake is not a state: it is a one-off reply to something the reader just did, it is over in four hundred milliseconds, and no colour reports a failure as unmistakably.
Accessibility
- A reduced-motion preference switches the animation off, so the shake is never the only thing carrying the message. Say it in words too: the
erroron the field is what a screen reader reads. - Move the focus to the control that failed as well. A reader who is not looking at it has been told nothing by a movement.