caliper uidocs

Result icon

Free

The outcome of an operation as a mark that draws itself once: a check, a cross, an exclamation, or a turning arc only while pending.

Installs components/ui/result-icon.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/result-icon.json
  1. Build
  2. Run tests
  3. Upload assets
  4. 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.