Gesso
Data display

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

StatusShapeInk
triageDashed ring--sys-fg-subtle
readyEmpty ring--primary
in_progressHalf pie--sys-cat-purple
in_reviewThree-quarter pie--sys-cat-blue
doneFull disc with a check--sys-cat-green
cancelledFull 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.

Triage
Ready
In progress
In review
Done
Cancelled

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.

circleTriageReadyIn progressIn reviewDoneCancelled
squareTriageReadyIn progressIn reviewDoneCancelled
<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.

sm
md
lg

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.

RuleWhat happens
Single markSprings now, because delay defaults to 0.
Row or matrixPass delay={index * WORK_MARK_STAGGER_MS}.
ColorRides the same spring and the same delay, so it does not lag the shape.
Mountinitial={false}, and the springs start on the current numbers.
Reduced motionjump to the end state, and the delay drops to 0.
InterruptA 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>.

PropTypeDefaultWhat 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.
delaynumber0Milliseconds to wait before springing to a new status. Negative values count as 0.
labelstringNames the mark, which makes it role="img" with a <title>. Without one it is hidden.
classNamestringAdded to the <svg>.

Accessibility

  • By default the <svg> is aria-hidden, since the status is usually written beside it, like WORK_STATUS_LABEL[status]. Where it stands alone, pass label, 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 becomes CanvasText and the check and the cross are drawn in Canvas, 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 delay is 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

On this page