Work mark
One SVG whose sweep, dash, check, and cross are numbers, so any work status springs to any other.
Work mark is a small mark for where a piece of work stands. It draws six statuses as shapes: a
dashed ring for triage, an empty ring for ready, a half pie in progress, a three-quarter pie in
review, a full disc with a check when done, and a full disc with a cross when cancelled. The pie
sweep, the ring dash, the check, and the cross are numbers on one spring, so any status retargets
any other, including mid-flight, and it is not six swapped icons. A circle marks a status, and
shape="square" draws the same marks as a task box. Unlike
Health face and
Priority bars, it has no name of its own and is
hidden from assistive tech, so it always goes beside text that says the status.
Triage
Click Advance, or jump to a status. The geometry morphs and the ink rides the same spring. One mark is immediate, with no fake delay.
Usage
import { WORK_STATUS_LABEL, WorkMark } from '@/components/ui/work-mark';
<div className="flex items-center gap-2">
<WorkMark status="in_progress" />
{WORK_STATUS_LABEL.in_progress}
</div>The statuses are triage, ready, in_progress, in_review, done, and cancelled.
WORK_STATUS_LABEL holds the display text for each and WORK_INK holds the color each is
drawn in, as a CSS variable, which is also the one to reuse for a board lane well.
Six statuses, six shapes
| Status | Shape | Ink |
|---|---|---|
triage | Dashed ring | --sys-fg-subtle |
ready | Empty ring | --primary |
in_progress | Half pie | --sys-cat-purple |
in_review | Three-quarter pie | --sys-cat-blue |
done | Full disc with a check | --sys-cat-green |
cancelled | Full disc with a cross | --sys-fg-subtle |
ready follows the look's brand color and the others stay fixed, so ready repaints when the
look changes. The cross draws in the way the check does, so moving from done to cancelled swaps
one for the other on the same disc.
The six geometries. Mount jumps, so a list does not play in on first paint.
Circle or square
The circle is the default and marks a status. shape="square" draws the marks in a rounded
square, for a task box where the mark reads as something to tick. It keeps the same sweep, dash,
check, and cross: the dash runs around the square's edge in the same sixteen beats as the ring,
and the pie fills the square, clipped to its corners.
<WorkMark status={task.status} shape="square" />Three sizes
sm is 14px, md is 16px and the default, and lg is 20px. The mark also takes className,
so a size-* class sets any other size.
Rows stagger with delay
A single mark springs immediately, so delay defaults to 0. When several marks change together,
pass delay in milliseconds so the wave reads, stepping by WORK_MARK_STAGGER_MS (28ms, in the
20 to 40ms band).
import { WORK_MARK_STAGGER_MS, WorkMark } from '@/components/ui/work-mark';
{tasks.map((task, index) => (
<WorkMark
key={task.id}
status={task.status}
delay={index * WORK_MARK_STAGGER_MS}
/>
))}- Draft copyTriage
- API contractReady
- Motion passIn progress
- A11y reviewIn review
- Ship notesDone
- Old bannerCancelled
Motion
One story: status to status on a shared spring (stiffness 420 / damping 40 /
mass 0.9), the same one Health face and
Priority bars use. It is faster than a
--duration-base fade, with no bounce and no opacity fade.
| Rule | What happens |
|---|---|
| Single mark | Springs now, because delay defaults to 0. |
| Row or matrix | Pass delay={index * WORK_MARK_STAGGER_MS}. |
| Color | Rides the same spring and the same delay, so it does not lag the shape. |
| Mount | initial={false}, and the springs start on the current numbers. |
| Reduced motion | jump to the end state, and the delay drops to 0. |
| Interrupt | A new status retargets the same springs. |
See Motion for the overlay rules. Work mark is a spring exception, and Tabs is the stretch-line exception.
Props
Work mark takes only these props, and it forwards its ref to the <svg>.
| Prop | Type | Default | What it does |
|---|---|---|---|
status | "triage" | "ready" | "in_progress" | "in_review" | "done" | "cancelled" | Required. Sets the shape and the ink. | |
shape | "circle" | "square" | "circle" | The outline the marks are drawn in. The square is for a task box. |
size | "sm" | "md" | "lg" | "md" | 14, 16, or 20px square. |
delay | number | 0 | Milliseconds to wait before springing to a new status. Negative values count as 0. |
label | string | Names the mark, which makes it role="img" with a <title>. Without one it is hidden. | |
className | string | Added to the <svg>. |
Accessibility
- By default the
<svg>isaria-hidden, since the status is usually written beside it, likeWORK_STATUS_LABEL[status]. Where it stands alone, passlabel, and it becomes an image with that name and a<title>. - Shape carries the state as well as color, so all six statuses differ without it. Triage and
ready are both thin rings, and the dashes against a solid line are small at
sm. Done and cancelled are both full discs, told apart by the check and the cross. In forced colors the ink becomesCanvasTextand the check and the cross are drawn inCanvas, so the shapes and the marks on them still show. The square works the same way. - The mark is not focusable and has no press behavior. Put it inside a button or a link for that, with the status as that control's text.
- It is not an interactive target, so it has no 44px minimum. Size the control around it.
- Under reduced motion the shape and the ink jump to the new status, and
delayis ignored.
When to use something else
- Badge for a status written as a word, in a role color, when a mark is not needed.
- Category colors for a status the user defines and colors themselves, which keeps its hue in every look.
- Check mark for a plain check that draws itself, as
in a Stepper or a checkbox. Work mark draws its own check
only for
done, and its square is a status mark, not a control to tick. - Progress or Segmented bar for an amount that changes continuously. The half and three-quarter pies here are two named statuses, not a percentage.
- Health face for how a project is going and Priority bars for how urgent a task is, which are the other two marks drawn the same way.
- Avatar for a person, such as the assignee beside the status.
Install
shadcn add @gesso/work-mark