Components · Structure

Toolbar

Two ways of putting actions next to each other, with opposite meanings. A button group says these are alternatives. A toolbar says these apply to what you are looking at. Confusing the two is how a clinician ends up applying an action to the wrong patient.

Stable · v1.0 shadcn/ui WCAG 2.2 AA Merged: Toolbar · Button group

Overview

Both components exist to make a relationship visible without words. Adjacency is the whole message, so it has to be truthful: things touching each other are read as belonging to the same decision, and things in a bar above a table are read as acting on that table.

The failure mode is the same for both — an action whose target is ambiguous. In a clinical worklist that ambiguity is not cosmetic. "Acknowledge" in a toolbar that appears to float between two panels is an acknowledgement of something, and the clinician has to guess what.

Adjacency means something

Buttons only touch when they are alternatives in one decision. Unrelated actions are separated by at least a group gap.

The target is visible

A toolbar sits inside the boundary of the thing it acts on, and names it when the thing is a selection rather than the whole view.

Nothing hides on hover

Actions are present or absent, never revealed by pointing. See Interaction states.

Which one

Button groupToolbar
Says These are alternatives — pick one, or one mode is active. These act on the region I sit in.
Spacing Segments share borders; zero gap. Grouped with gaps and separators; never a continuous bar of segments.
Selection May be stateful (aria-pressed / radio semantics). Never stateful as a whole; individual toggles may be.
Max items Five. Beyond that use Select. No fixed limit, but overflow goes to a Menu, not a scrollbar.
Keyboard Arrow keys move within the group; one tab stop. Arrow keys move within the toolbar; one tab stop.

Anatomy

Toolbar above a scoped region

Waveform region. The toolbar is inside the same bordered panel, so its scope is unambiguous.

PartRule
Scope label The toolbar names what it acts on when that is not obvious from position — the recording, the selection, the patient.
Groups Related controls sit together; unrelated groups are separated by a separator or by at least 16 px. Never both.
Separator Decorative, aria-hidden. Semantics come from nested role="group", not from the line.
Primary action At most one, pushed to the trailing edge. Everything else is ghost or secondary — see Button.
Overflow A trailing menu button. Destructive and irreversible actions never live in overflow.

Button group

Segments share edges because the choice is exclusive. If two adjacent buttons can both be pressed, or if pressing one does not deselect the other, it is not a button group — it is two buttons that have been glued together by mistake.

Exclusive selection, one tab stop

The pressed segment is filled and carries aria-pressed. Fill alone would fail greyscale and colour-vision checks.

Split buttons, and when not to

A split button pairs a default action with a menu of alternatives behind an attached control. It is a space-saving device with a specific hazard: the two halves are adjacent, differ by a few millimetres, and do different things — which is the adjacent-control error that Touch & gestures names as the dominant touch failure in gloved use.

The rule

A split button is permitted only when every action behind the menu is reversible and non-clinical, and its default half is never destructive. Export formats, print options, sort orders. Not "Discharge", not "Sign", not anything that changes what a device does or what a record says.

Scope and selection

The rule that carries the risk

A toolbar action that operates on a selection states the count and the subject in the button itself — Acknowledge 3 selected, not Acknowledge. A count that changes under the clinician's hand while the pointer is travelling toward the button is the classic wrong-target error, and the label is the only thing that catches it.

Selection toolbar

Acknowledge is deliberately absent. Acknowledging an alarm is a per-patient act with per-patient consequences — see Acknowledge & escalate.

States

StateRendering
Unavailable Disabled with a reason adjacent, or removed entirely. Never a disabled control with no explanation — see Button.
Pending The pressed control shows activity in place and keeps its label and width. The rest of the toolbar stays live.
Pressed (group) Fill, weight and aria-pressed. Three signals, one of which is non-visual.
Overflowed Items move into the menu from the trailing end in a fixed order, so the same action is always in the same place at a given width.
Read-only context The toolbar renders with commands removed, not disabled. A row of greyed buttons reads as a fault.

A toolbar that changes shape

Mainstream systems offer several ways for a toolbar to rearrange itself: a tool picked from the overflow menu takes the place of the last tool in its group; a tool with a nested menu is replaced by whichever variant is chosen from it; tools can be dragged into a new order; the whole bar can be floated and docked wherever the reader likes. Each is offered as configurability. Together they mean the button under your finger is not the button that was there last time.

The rule

Using a toolbar never rearranges it. Position is how a control is found once it is known — not read, not searched for, reached. A toolbar that reorders as a side effect of being used destroys the only property that made it fast.

Offered elsewhereVerdict here
A tool chosen from the overflow menu replaces the last tool in its group No. It evicts a control the clinician did not ask to lose, and shifts everything after it. The tool they wanted is now in the bar and the one they rely on is in the menu, and nothing said so.
A nested tool is replaced by the variant chosen from its menu No. The same defect with a smaller radius. A control that becomes a different control keeps its position and changes its meaning, which is worse than moving.
Drag to reorder No. On a shared workstation the arrangement is inherited from whoever was here last, and there is no keyboard equivalent of a drag.
Float the toolbar anywhere; dock it by dragging within 16 px of an edge No. A floating bar covers the thing it operates on, a 16 px snap is a fine-motor task nobody performs in gloves, and the position is one more piece of state a colleague set.
Collapse to icons to save space No. There is no icon set here. A tool is a word, and a bar with no room for words has too many tools — see Which control is this.
Truncate a label that exceeds the tool width No. A truncated verb is not a verb. Per Perceivable affordance, a control the reader cannot read is a control they cannot use.

What remains permitted is deliberate and rare: the set of tools may differ by view, because a different view is a different task; and it may differ by role, because a control someone may not use is better absent than disabled. Both are decided before the clinician arrives, not by what they did a moment ago.

Do

Calipers · Measure · Compare · More

after using Annotate from More: Calipers · Measure · Compare · More

The bar is where it was. Annotate ran; nothing moved to make room for it.

Don't

Calipers · Measure · Compare · More

after using Annotate: Calipers · Measure · Annotate · More

Compare has gone into the menu to make room. The next reach for the third button lands on a tool that draws on the study.

What the component enforces, and what it only asks for

The keyboard contract is held — it was not, until an interaction test was written for it. Everything about what is in the bar is yours.

RuleHeld by How
One tab stop, arrows insideThe component A roving tabIndex, so twelve controls cost one tab press rather than twelve. This was a plain div announcing itself as a toolbar until the interaction gate was pointed at it.
Asserted: Toolbar: one tab stop, not one per control
Arrow direction follows the reading directionThe component Mirrored under dir="rtl", so the control to the right of the pointer in Arabic is the one the left arrow reaches.
Asserted: Toolbar: arrow keys move between controls
Re-entering returns to the last control usedThe component The tab stop follows focus, so leaving and coming back does not restart at the beginning.
Asserted: Toolbar: re-entering returns to the last control used
Separators and spacers are not focusableThe component Both are inert furniture; neither is a control.
Asserted: Toolbar: a separator is furniture, not a tab stop
The bar does not rearrange itselfYou The children are yours. Nothing here reorders, but nothing stops you rebuilding the bar with the tool just used moved to the front.
An action states what it acts onYou “Acknowledge 3 selected” against “Acknowledge” is a label you write.
No bulk acknowledgementYou Prohibited system-wide, and expressible here as an ordinary button.
Why this is written down

A rule stated on a component page reads as a rule the component keeps. 4 of these 7 are; 3 are obligations this page was quietly handing to the reader. Position is how a control is found once it is known. The component will not move anything; it also cannot stop you.

Do's and don'ts

Do

Modes touch each other; a command sits past a separator. The spacing tells you which is which before you read a word.

Don't

A command welded into a segmented control. It looks like a third view mode, and it is one segment away from the pointer that was heading for 3 × 4.

Do

Count in the label as well as in the bar. If the selection changes while you reach for it, the button says so.

Don't

An unscoped verb and a bulk acknowledgement. Neither says what it acts on, and one of them dismisses alarms the clinician has not read.

Do

One primary, at the trailing edge, visually separated from the exploratory controls it is not related to.

Don't

Three primaries. Emphasis that applies to everything applies to nothing, and the highest-consequence action no longer stands out.

Accessibility

Outcomes of use

What this contributes to, in the terms of Usability & context of use. These are attributes believed to contribute to an outcome; the outcome itself is settled by observing real use in a specified context, not by this page.

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/toolbar.json
<Toolbar label= "ECG review actions" >
  <ButtonGroup label= "Lead layout" >
    <Button variant= "outline"  aria-pressed={layout ===  "12x1" }>12 × 1</Button>
    <Button variant= "outline"  aria-pressed={layout ===  "6x2" }>6 × 2</Button>
  </ButtonGroup>
  <ToolbarSeparator />
  <Button variant= "outline" >Compare with prior</Button>
</Toolbar>

// One tab stop; arrow keys move between the controls and follow the
// reading direction. A ButtonGroup is related actions — if the buttons are
// mutually exclusive states, that is a RadioGroup.

What the component takes. This table used to list scope, controls, overflowFrom, ButtonGroup — props the component does not have, and never had. Found by the registry gate’s props-table rule.

PropTypeDefaultNotes
labelstring Required. Accessible name of the toolbar.
childrenReact.ReactNode— Required. The controls. One tab stop for all of them; arrow keys move between them.

Separator and spacer

Two pieces ship with this install command and neither was named on this page.

NotJustAnyMed.Tech Design System · Toolbar · v1.0 · draft for review
Reference applications named in this system are fictional; all patient data and reference ranges shown are fabricated and illustrative.