Skip to content
Folio

Diagrams

DiagramToolbar

A bar of actions belonging to the surface underneath them.

Usage

When to reach for it

FloatingIconButton is one pinned action. This is the container for several, so they read as one object rather than as a scatter.

Ships from@misoto22/design/diagrams

TSX
import { DiagramToolbar } from '@misoto22/design/diagrams'

Notes

A bar of actions that belong to the surface underneath them.

The package already has FloatingIconButton for ONE pinned action. This is the container for several — which is a different problem, because several pinned buttons need to read as one object rather than as a scatter: one plate, one border, hairlines between the groups, and a single role="toolbar" so a screen reader announces a toolbar instead of six unrelated buttons.

role="toolbar" also changes the keyboard contract, and callers should know what they are opting into: arrow keys are expected to move between the controls and Tab is expected to leave the bar. This component does not implement roving focus for you — a bar of three buttons where Tab visits all three is honest and fine — so pass placement="inline" and skip the role by using a plain <div> if that is not what you want.

Anatomy

DiagramToolbar anatomy
ElementDescription
Barrequiredrole="toolbar" with a horizontal orientation, on a panel plate with a hairline and the system’s corner. One plate and one border is what makes six pinned buttons read as one object instead of as six.
Namerequiredlabel, which becomes the bar’s accessible name. It is a required prop with no default, because a toolbar that announces its role and nothing else is a group a screen reader cannot tell from the next one.
GrouprequiredDiagramToolbarGroup: one run of related controls, divided by a hairline on its leading edge rather than by space. The first group carries no rule, so the bar does not open with a divider.
ControlsrequiredThe children, at whatever size the caller gives them. The bar sets the gap and paints no state of its own — a button in here is the same button it is anywhere else.
Pinplacement="floating" takes the bar out of the flow and pins it to the top edge of the nearest POSITIONED ancestor, at the sticky rank — under every anchored panel and every dialog — with align choosing the corner. placement="inline" leaves it in the flow and pins nothing.

Best practices

Do

  • Give label the bar’s job rather than its shape — "Diagram actions", not "Toolbar" — because a page with a figure and a table otherwise announces two toolbars and distinguishes neither.
  • Divide with DiagramToolbarGroup rather than with a gap. The rule is drawn on the group, and a gap wide enough to read as a boundary is also wide enough to stop the bar reading as one object, which is the reason the controls were collected at all.
  • Keep it to a handful of controls. There is no roving focus here, so Tab visits every one of them: ten actions in the bar is ten stops between the reader and the rest of the page.

Don’t

  • A floating bar sits over the surface, not beside it. Whatever is in that corner of the figure is underneath it — pad the surface, or use placement="inline" and let the bar take its own row.
  • role="toolbar" is a promise about the keyboard as well as a name: a reader is told this is a toolbar and expects the arrow keys to move inside it. For two buttons that merely sit next to each other, a plain div makes no promise this component then has to keep.
  • A floating bar is a SIBLING of the canvas inside a positioned wrapper, never a child of it. Everything handed to DiagramCanvas renders inside the transformed stage, so a bar passed as a child zooms and pans away with the diagram it was put there to control — and with no positioned ancestor at all it pins to whichever box further up the page happens to be one.

Examples

default

Four controls in two groups, separated by a hairline rather than by space — a gap wide enough to read as a boundary is also wide enough to stop the bar reading as one object, which is the only reason the controls were collected into a bar. The wrapper carries role="toolbar", which is why label is required: without it a screen reader announces a toolbar with no name. Roving focus is not implemented, so Tab visits all four buttons; where that is wrong, use a plain div and skip the role.

floating over a canvas

placement="floating" pins the bar over the surface it acts on, and align picks which top corner. It has to be a SIBLING of the canvas inside a positioned wrapper, not a child of it: everything passed to DiagramCanvas as children goes inside the transformed stage, so a toolbar handed to it would zoom and pan along with the diagram it is meant to control. The same wrapper is what the export menu takes a ref to, so the picture and the button that saves it are one object.

Request path3 elements and 2 relationships.CLOUDCloudFrontCDNSERVICEAPIFastAPIDATAPostgres

3 elements and 2 relationships.

  • CloudFront (cloud) — CDN
  • API (backend) — FastAPI
  • Postgres (database)
  • CloudFrontAPI: HTTPS
  • APIPostgres: SQL

Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.

a different set of controls

The same bar carrying controls that change WHAT the figure says rather than how it is being looked at — a guided reading, one chapter at a time. The current chapter is marked with aria-pressed as well as with the secondary variant, because the variant is a difference in paint and a screen reader cannot see paint. The second group holds a readout rather than a control: a group is an ordinary div, and the hairline is what says the count belongs to the bar instead of trailing after it.

Parts

Composed at the call site rather than configured through props, so a layout this component did not anticipate is still expressible.

DiagramToolbarGroup

One run of related controls inside the bar.

Separated by a hairline rather than by space, because a gap large enough to read as a group boundary is also large enough to stop the bar reading as one object — which is the whole reason the controls were collected into a bar.

Takes no props of its own.

Accessibility

  • role="toolbar" announces a toolbar rather than six unrelated buttons. It does not implement roving focus — Tab visits every control, which is honest for a bar of three.