Status Icon
A small workflow status icon in Linear's set — backlog, todo, in progress, in review, done, canceled, duplicate, and triage.
- Status
- Since
0.1.0- Accessibility pattern
- img role with derived label
Last updated
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Status Icon before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify status-iconInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/status-iconRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
Usage
import { StatusIcon } from "@/components/ui/status-icon";
<StatusIcon status="progress" />;Examples
States
Each status draws its own Linear-style glyph on a 16-unit grid in one of the system's standard
semantic color tokens: backlog (dashed ring, muted-foreground), todo (empty ring,
foreground), progress (ring with a half pie, warning-text), review (ring with a
three-quarter pie, info-text), done (filled disc with a check, success-text), canceled
(filled disc with a cross, muted-foreground), duplicate (ring with two diagonal strokes,
muted-foreground) and triage (filled disc with a left-right arrow, tag-orange-text).
blocked and cancelled remain as deprecated aliases: cancelled renders canceled, and
blocked draws a slashed ring in warning-text.
Still and animated progress
progress is static by default — lists and boards should not spin dozens of rows at once. Pass
animated to spin the glyph for a single live item.
Sizes
xs (14px), sm (16px), md (20px), lg (24px) — plain Tailwind size-* steps. Pick the one
that matches the text it sits beside.
Each status renders at every size:
With a text label
When adjacent text already states the status, pass label="" so the icon is marked decorative
(aria-hidden) and the screen reader announces the text once rather than twice. This is the
recommended accessible pattern.
<span className="flex items-center gap-2">
<StatusIcon status="progress" label="" />
In progress
</span>Status menu recipe
There is no picker component: compose DropdownMenu + StatusIcon + DropdownMenuShortcut. The
trigger is a ghost icon button showing the current status. While the menu is open, 1–7 pick
Backlog / Todo / In progress / In review / Done / Canceled / Duplicate, and 0 picks Triage. ⌥/Alt-click the
circle marks the item Done without opening the menu — pair it with an Undo toast.
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger
render={
<Button
variant="ghost"
size="icon-sm"
aria-label={`Status: ${label}. Alt-click to mark done`}
onClick={(e) => {
if (!e.altKey) return;
e.preventBaseUIHandler();
setStatus("done");
}}
/>
}
>
<StatusIcon status={status} size="sm" label="" />
</DropdownMenuTrigger>
<DropdownMenuContent onKeyDown={pickByShortcut}>
{STATUSES.map((s) => (
<DropdownMenuItem key={s.value} onClick={() => setStatus(s.value)}>
<StatusIcon status={s.value} size="sm" label="" />
{s.label}
<DropdownMenuShortcut>{s.key}</DropdownMenuShortcut>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>In Select and Combobox options
Use the icon as the leading icon of each option (with label="", since the option text names the
status), so filters and pickers read the same as rows.
For priority, see Priority Icon.
Playground
Pick a status and size to preview each state's icon and color, then copy the generated JSX.
<StatusIcon />API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
animated | boolean | false | progress only: spin the glyph. Leave it off in lists and boards, where many rows would
spin at once; turn it on for a single live item. |
label | string | — | Accessible label announced by assistive tech. Defaults to a human-readable
name derived from status (e.g. "In progress"). Pass an empty string to
make the icon decorative (aria-hidden) — only do this when adjacent text
already conveys the status. |
size | "lg" | "md" | "sm" | "xs" | 'md' | Size variant: xs (14px), sm (16px), md (20px), lg (24px). |
status | "backlog" | "blocked" | "canceled" | "cancelled" | "done" | "duplicate" | "progress" | "review" | "todo" | "triage" | 'todo' | Workflow status to display (Linear's set). Selects both the glyph and its semantic colour:
- backlog → dashed ring, text-muted-foreground
- todo → empty ring, text-foreground
- progress → ring with a half-filled pie, text-warning-text
- review → ring with a three-quarter pie, text-info-text
- done → filled disc with a check, text-success-text
- canceled → filled disc with a cross, text-muted-foreground
- duplicate → ring with two diagonal strokes, text-muted-foreground
- triage → filled disc with a left-right arrow, text-tag-orange-text
- blocked (deprecated) → slashed ring, text-warning-text
- cancelled (deprecated) → renders canceled |
Data attributes and CSS variables on StatusIcon
| Attribute | Values |
|---|---|
data-icon-tone | "" |
data-size | mirrors a prop or state value |
data-slot | "status-icon" |
data-status | mirrors a prop or state value |
Accessibility
- By default the icon renders
role="img"with anaria-labelderived fromstatus(e.g."In progress"), so assistive tech announces the state. Override the wording with thelabelprop. - When adjacent text already states the status — for example a label next to the icon — pass
label=""to make the icon decorative (aria-hidden) and avoid a redundant announcement. - Color is conveyed through a semantic token (
currentColor) only — never rely on color alone to communicate the status; the distinct icon shapes carry the meaning too. progressis static unlessanimated; the spin stops underprefers-reduced-motion: reducethrough the global reset inbase.css, which every component inherits rather than restating.
| Concern | Behavior |
|---|---|
| Role | role="img" unless label="" (decorative, aria-hidden) |
| Accessible name | aria-label from label, or a default derived from status |
| Reduced motion | animated progress frozen by the global base.css reset |
| Contract | States tested |
|---|---|
| Behaviour | default, empty, error, success |
| Accessibility | labeled |
| Visual | default, error, success, empty |