Approvals
FreeCards that ask the user to allow or deny an action, with a risk label.
pnpm dlx shadcn@latest add https://caliperui.site/r/approvals.jsonDelete 3 stale snapshot files
These files are no longer referenced by any test. Deleting them cannot be undone from here.
- tests/__snapshots__/table.old.snap
- tests/__snapshots__/tree.old.snap
- tests/__snapshots__/thread.old.snap
Approval needed: Delete 3 stale snapshot files
When to use #
Use it for
- An agent wants to run a tool that changes something.
- The decision should be explicit and reversible where possible.
Skip it when
- The action is harmless and routine; do not interrupt.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/approvals.json
Adds its 6 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. It is built from ApprovalCard, ToolApproval and RiskChip.
import { useRef, useState } from "react" import { ApprovalCard, type ApprovalStatus } from "@/components/ui/approvals" export function ApprovalsDemo() { const [, setStage] = useState(0); const [approval, setApproval] = useState<ApprovalStatus>("pending"); const timers = useRef<ReturnType<typeof setTimeout>[]>([]); const later = (ms: number, fn: () => void) => timers.current.push(setTimeout(fn, ms)); return ( <ApprovalCard title="Delete 3 stale snapshot files" risk="high" description="These files are no longer referenced by any test. Deleting them cannot be undone from here." details={<ul className="grid gap-1 font-mono text-xs"><li>tests/__snapshots__/table.old.snap</li><li>tests/__snapshots__/tree.old.snap</li><li>tests/__snapshots__/thread.old.snap</li></ul>} status={approval} approveLabel="Delete" resolvedBy="You" resolvedAt="just now" onApprove={() => { setApproval("approved"); later(500, () => setStage(4)); }} onDeny={() => { setApproval("denied"); later(500, () => setStage(4)); }} /> ); }
API reference #
ApprovalCard
component
ToolApproval
component
RiskChip
component
Accessibility #
It exposes status and alert semantics, with aria-hidden, aria-label and aria-expanded kept in step with state. Changes that matter are announced through a live region.
No custom key handling; Tab and the native activation keys do the work.
Motion #
Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.