Skip to content
Component installs need the registry setup
VegaStack Design

Status Icon

A small workflow status icon in Linear's set — backlog, todo, in progress, in review, done, canceled, duplicate, and triage.

Status
stable
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-icon

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/status-icon

Run 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.

BacklogTodoIn progressIn reviewDoneCanceledDuplicateTriage
<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

PropTypeDefaultDescription
animatedbooleanfalseprogress 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.
labelstring—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

AttributeValues
data-icon-tone""
data-sizemirrors a prop or state value
data-slot"status-icon"
data-statusmirrors a prop or state value

Accessibility

  • By default the icon renders role="img" with an aria-label derived from status (e.g. "In progress"), so assistive tech announces the state. Override the wording with the label prop.
  • 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.
  • progress is static unless animated; the spin stops under prefers-reduced-motion: reduce through the global reset in base.css, which every component inherits rather than restating.
ConcernBehavior
Rolerole="img" unless label="" (decorative, aria-hidden)
Accessible namearia-label from label, or a default derived from status
Reduced motionanimated progress frozen by the global base.css reset
ContractStates tested
Behaviourdefault, empty, error, success
Accessibilitylabeled
Visualdefault, error, success, empty

Do / Don't

Do
Pair the icon with a text label, or give it a meaningful aria-label so the status is announced — distinct icon shapes back up the color.
Don't
Rely on color alone to convey status, or leave the icon unlabeled when no adjacent text describes the state.

On this page