caliper uidocs

Usage meter

Free

A segmented bar showing how an allowance is used, with a warning near the limit.

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

API requests · day 9 of 31

742 / 1,000 k

At this pace you reach about 2.56M by 31 Oct: roughly $78 extra.

Free

Within limit

When to use #

Use it for

  • Storage, seats and quota shown as parts of a whole.
  • You want a visible warning as the limit nears.

Skip it when

  • There is no limit; use a metric.
  • Only one value matters; use a progress bar.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/usage-meter.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 { UsageMeter } from "@/components/ui/usage-meter"

export function StorageMeter() {
  const [used, setUsed] = useState(742);
  const [day, setDay] = useState(9);
  const projected = Math.round((used / day) * 31);
  const over = projected - 1000;
  const consequence = used > 1000
    ? `Over the limit by ${used - 1000}k. Extra requests are $0.05 per 1k: $${((used - 1000) * 0.05).toFixed(2)} so far.`
    : over > 0
      ? `At this pace you reach about ${(projected / 1000).toFixed(2)}M by 31 Oct: roughly $${Math.round(over * 0.05)} extra.`
      : `Projected ${projected}k by 31 Oct. Comfortably inside the limit.`;
  return (
    <div className="grid w-full max-w-md gap-3">
      <UsageMeter
        label={`API requests · day ${day} of 31`}
        unit="k"
        limit={1000}
        projected={projected}
        consequence={consequence}
        segments={[
          { id: "api", label: "REST", value: Math.round(used * 0.7) },
          { id: "webhooks", label: "Webhooks", value: used - Math.round(used * 0.7) },
        ]}
      />
      <div className="flex flex-wrap gap-2">
        <Button size="sm" variant="secondary" onClick={() => setUsed((value) => value + 120)}>+120k requests</Button>
        <Button size="sm" variant="secondary" onClick={() => setDay((value) => Math.min(31, value + 1))}>Advance a day</Button>
        <Button size="sm" variant="ghost" onClick={() => { setUsed(742); setDay(9); }}>Reset</Button>
      </div>
    </div>
  );
}

API reference #

UsageMeter

component
PropTypeDefaultDescription
label *string—
segments *readonly UsageSegment[]—
limit *number—
unitstring""
freeLabelstring"Free"
overLabelstring"Over limit"
warnAtnumber0.9
projectednumber—Projected total at the end of the period, drawn dashed beyond what is used.
consequencestring—What the numbers mean, e.g. "About $78 extra at this pace."
legendbooleantrueThe segment buttons and status word under the bar. Turn off for a single plain count.
classNamestring—

Accessibility #

It exposes group and img semantics, with aria-label, aria-pressed and aria-live kept in step with state. Changes that matter are announced through a live region.

KeyBehaviour
→Moves to the next item or value.
↓Moves to the next item or value.
←Moves to the previous item or value.
↑Moves to the previous item or value.
HomeJumps to the first item or the minimum.
EndJumps to the last item or the maximum.

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.