caliper uidocs

Shake

Free

Nudges a field or control sideways once to say “not accepted”, with an outline instead under reduced motion.

Installs components/ui/shake.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/shake.json

When to use #

Use it for

  • A wrong code, password or answer.
  • A refused drop or action.

Skip it when

  • Explaining the problem; always write the reason too.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/shake.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Field } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Shake } from "@/components/ui/shake"

export function ShakeDemo() {
  const [code, setCode] = useState("");
  const [misses, setMisses] = useState(0);
  const [error, setError] = useState<string>();
  return (
    <form
      className="grid w-full max-w-xs gap-3"
      onSubmit={(event) => {
        event.preventDefault();
        if (code.trim() === "SAVE20") {
          setError(undefined);
          return;
        }
        setMisses((n) => n + 1);
        setError(code.trim() ? `“${code.trim()}” is not a valid code. Try SAVE20.` : "Enter a code.");
      }}
    >
      <Shake signal={misses}>
        <Field label="Discount code" error={error} description={!error && code.trim() === "SAVE20" ? "Applied: 20% off" : undefined}>
          {(props) => <Input {...props} value={code} onChange={(event) => setCode(event.target.value)} autoComplete="off" />}
        </Field>
      </Shake>
      <Button type="submit" size="sm" className="w-fit">Apply</Button>
    </form>
  );
}

API reference #

Shake

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
signal *number—Bump it to shake again, e.g. the number of failed attempts.

Accessibility #

It is built on native elements, so roles and focus come from the browser.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.