Result icon
FreeThe outcome of an operation as a mark that draws itself once: a check, a cross, an exclamation, or a turning arc only while pending.
pnpm dlx shadcn@latest add https://caliperui.site/r/result-icon.json- Build
- Run tests
- Upload assets
- Promote
When to use #
Use it for
- Steps of a job, rows of a checklist, the result of a test.
- Replacing a spinner with the outcome in the same place.
Skip it when
- Status that never changes; use a badge.
- Long-running progress; use progress.
Installation #
terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/result-icon.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 { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { ResultIcon, type ResultIconStatus } from "@/components/ui/result-icon" type Upload = { id: string; name: string; size: string }; const steps = ["Build", "Run tests", "Upload assets", "Promote"]; export function ResultIconDemo() { const [state, setState] = useState<ResultIconStatus[]>(["success", "success", "pending", "idle"]); const advance = () => setState((s) => { const i = s.indexOf("pending"); if (i < 0) return ["pending", "idle", "idle", "idle"]; return s.map((v, j) => (j === i ? (i === 2 && s[3] === "idle" ? "warning" : "success") : j === i + 1 ? "pending" : v)); }); return ( <div className="grid w-full max-w-sm gap-3"> <ol className="grid gap-2"> {steps.map((step, i) => ( <li key={step} className="flex items-center gap-3 text-sm"> <ResultIcon status={state[i]} /> <span className={state[i] === "idle" ? "text-muted-foreground" : ""}>{step}</span> {state[i] === "warning" && <Badge variant="warning">2 assets over 1 MB</Badge>} </li> ))} </ol> <div className="flex gap-2"> <Button size="sm" onClick={advance}>Next step</Button> <Button size="sm" variant="ghost" onClick={() => setState(["idle", "pending", "error", "idle"])}>Show a failure</Button> </div> </div> ); }
API reference #
ResultIcon
component
PropTypeDefaultDescription
status *ResultIconStatus—
labelstring—Spoken name; defaults to a word for the status.
sizenumber20
classNamestring—
Accessibility #
It exposes img semantics, with aria-label kept in step with state.
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.