Components · Feedback & status

Activity

The indeterminate wait. It knows only that work is in flight, and says that rather than inventing a fraction.

Stable · v1.0 shadcn/ui WCAG 2.2 AA

Overview

An activity indicator is honest about not knowing. That is its whole value, and it is why it is not a worse Progress: the failure this prevents is a bar that advances on a timer, which is a measurement nobody took.

Most of what governs a wait lives here rather than with the determinate bar — when to show one at all, what changes as the wait grows, when to stop describing the wait and start describing the gap, and how to stop. All of it used to sit on a page named “Progress & activity”, which is why the split was worth doing.

Which of the three to reach for is decided on Progress.

The rule that overrides the rest

An activity indicator may never cover the alert region. A system busy with one patient still has to be able to alarm about another — see what it must never do.

Three shapes, chosen by scope

Linear, circular, inline

LINEAR — A REGION

Reading prior studies started 14:27:02

CIRCULAR — A VIEW

Assembling the handover brief
4 systems · started 06:41:08

INLINE — A CONTROL

Checking the identifier
ShapeScope Rule
LinearOne region — a panel, a list, a section Sits at the edge of the region it describes, so its extent says what is waiting.
CircularA whole view, or a large section of one Centred in the region, with the label centred under it. Never centred over the whole screen on a patient-scoped view — that would cover the alert region.
InlineOne control or one row Sits with the thing it is about. Feedback for a pressed button belongs on that button, per Interaction states.

Two sizes, not seven. Small for inline use, default everywhere else. A size scale invites choosing by taste; scope already decides the shape, and the shape decides the size.

When to show one, and what changes as the wait grows

A wait is not one state. It is a sequence of them, and the boundaries matter because they are where a clinician's belief changes — first that the system is slow, then that it is broken, then that they should proceed without it. The last of those is the dangerous one, and the interface decides how soon it arrives.

ElapsedShow Why here
Under ~0.4 sNothing An indicator that appears and vanishes is a flicker. It reads as a fault, and on a list of rows it reads as many faults.
~0.4 – 2 sThe indicator alone Enough to say the press registered. A label here is noise nobody finishes reading.
Beyond ~2 sIndicator and a label naming the work The reader has started wondering. "Reading 4 source systems" answers the question they are actually asking, which is what is it doing.
Beyond ~5 sSecondary text: why it is slow, and elapsed time The question changes to is this stuck. An absolute start time and a running elapsed count answer it without inventing an estimate.
Beyond ~10 sA stated bound, and a way out Either a real estimate or a timeout with a number on it, plus a control to stop and a route to the record without this result.
At the boundA stated failure, never a continuing spinner An indicator with no timeout is an unreported error. What replaces it says which source did not answer, per Empty state.

These are starting points, not measurements — nobody has tested them here, and they are Reasoned claims like most of this system. What is not negotiable is the shape: every wait ends, and the reader is told more as it lengthens rather than less.

When it is slow and the duration is genuinely unknown

The usual advice — past ten seconds, use a progress indicator — is not an answer. A progress indicator needs a computable fraction, and if one existed you would already be showing it. Indeterminate and long is the common case, and switching component does not make the number appear.

The rule

Past the bound, stop describing the wait and start describing the state: which sources have answered, which have not, what can be shown now, and what stopping costs.

A long wait, decomposed
Assembling the handover brief started 06:41:08 · 00:34 elapsed
  • Observations · Medication · Results answered 3 of 4
  • Theatre record no response since 06:41:08 waiting

A brief without the theatre record will say so on every page it is printed on.

Stopping

What the component enforces, and what it only asks for

Short table, because an indeterminate indicator is mostly a refusal to claim things.

RuleHeld by How
It cannot claim to know how longThe component There is no value on the type at all.
Asserted: Activity: no value, and none can be passed
It names the workThe component label is required, so an unlabelled spinner is not expressible.
Asserted: Activity: an unlabelled indicator cannot be rendered
It is announced as busy, not as a false zeroThe component No aria-valuenow; the spinner is aria-hidden and the region is aria-busy.
Asserted: Activity: circular and inline carry no progressbar role
Movement stops under reduced motionThe component The animation is suppressed rather than slowed, because movement is the alarm channel.
Asserted: Activity: movement stops rather than slows under reduced motion
An indeterminate bar cannot be read as a fractionThe component Hatched across the whole track. It was a third of the track — “a third done” — and under reduced motion the full track, the picture of “finished”.
Asserted: Activity: an indeterminate bar does not look like a fraction
There is a bound, and it resolves to a stated failureYou The component has no timer. A region that spins for ever is a request that died, rendered as one still working.
It does not cover the alert regionYou A layout decision, and the rule that overrides every other one on this page.
Why this is written down

A rule stated on a component page reads as a rule the component keeps. 4 of these 6 are; 2 are obligations this page was quietly handing to the reader. The two rules that make an indeterminate wait safe — that it ends, and that it never covers an alarm — are both yours.

Do's and don'ts

Do

Assembling the handover brief
Pathology is slow this morning · started 06:41:08 · 00:12 elapsed

Past five seconds the question stops being what and becomes is this stuck. An elapsed count answers it without inventing an estimate.

Don't

Assembling the handover brief
This may take a moment…

A guess presented as information. Thirty seconds later it is still saying "a moment", and the reader has stopped believing anything on the screen.

Do

Prior studies

Searching 3 archivestimes out 00:20

Linear, at the edge of the one region that is waiting. Everything else on the screen stays readable and stays alarmable.

Don't
Loading…

One slow archive has taken the whole view. The patient header, the results already on screen and any alarm arriving are all behind this.

Do

Checking the identifier

Inline, beside the field it is about. The rest of the form stays usable while it resolves.

Don't

A 1.5 rem spinner centred over the form for 300 ms.

Below half a second an indicator is a flicker that reads as a fault — and repeated down a list of rows, it reads as many faults.

Do

Waiting on the theatre record — no response since 06:41:08.
Show the brief without it · Stop waiting

Past the bound the wait stops being the subject. Somebody can phone theatre, or proceed with the gap stated.

Don't

Still loading… (at 00:34, unchanged since 00:02)

Three sources answered and this says nothing about any of them. There is no way to proceed and no way to stop, so the clinician leaves — losing the three.

Accessibility

Outcomes of use

Clinical safety notes

Risk controls this page specifies

Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1). Which of these the component keeps and which are yours is stated in the guarantees table; a control the component does not keep is one your risk file must show you keep.

Implementation

$ npx shadcn@latest add https://md.notjustany.tech/r/activity.json
<Activity
  label="Searching prior studies"
  shape="circular"   // linear | circular | inline
  startedAt="14:27:02"
/>

// There is no value on this type at all, so an indicator that does not
// know how long cannot accidentally claim that it does. The bound, and
// what happens when it is reached, are the caller's.
NotJustAnyMed.Tech Design System · Progress & activity · v1.0 · draft for review
Reference applications named in this system are fictional; all patient data shown is fabricated.