Diagrams
DiagramMinimap
Where you are in something bigger than the window.
Usage
When to reach for it
Ships from@misoto22/design/diagrams
import { DiagramMinimap } from '@misoto22/design/diagrams'Notes
Where you are in something bigger than the window.
Two things at once, and both are needed: a miniature of the whole artwork, and a rectangle showing which part of it the frame is currently over. The miniature alone answers "what is there"; the rectangle answers "and where am I", which is the question a reader who has just panned twice actually has.
THE RECTANGLE IS DERIVED, never stored. Its position comes out of the canvas's own view — the same three numbers the canvas is already transforming by — divided by the map's scale, and clamped to the plate: a viewport that has been panned past the edge of the artwork is a viewport half over blank paper, and drawing the rectangle out there claims the map extends somewhere it does not. Keeping a second copy of "where the viewport is" is how a minimap comes to disagree with the thing it is a map of, and a map that disagrees is worse than none.
NOTHING IS DRAWN UNTIL THE ARTWORK HAS A SIZE. content is usually a measurement, and a measurement's first value is zero — so the scale would be 1, the miniature would be the artwork's top-left corner at full size, and the rectangle would sit over it meaning nothing. An empty plate for one frame is the honest version of "not yet".
CLICKING RECENTRES rather than jumping. onSeek reports a point in CONTENT coordinates, which is what a canvas's centerOn takes. The minimap does not move anything itself: it has no authority over the view, it only says where the reader pointed. A drag keeps seeking, and only a drag that STARTED on the map does — a button held down somewhere else and dragged across is not this component's gesture to answer.
Anatomy
| Element | Description |
|---|---|
| Map framerequired | A fixed-width plate — 200px unless width says otherwise — whose height follows the artwork’s own aspect ratio, named as a group so it is not an unlabelled box beside the figure. |
| Miniaturerequired | The children under a CSS scale of width over content.width, anchored top-left, hidden from assistive technology and transparent to the pointer. Drawn only once content has a size — at width 0 the scale would be 1 and the miniature would be the artwork’s top-left corner at full size. It answers "what is there" at a glance and nothing more. |
| Viewport rectanglerequired | An accent-washed rectangle computed from the canvas’s view and the map’s scale, clipped to the plate so a viewport panned past the artwork stops claiming a map that reaches further, and floored at a few pixels so a deep zoom still leaves something on screen to see. It is derived on every render and never stored. |
| Seek layer | The transparent layer over the map that turns a press, or a drag that STARTED on it, into a point in CONTENT coordinates and hands it to onSeek. A button pressed elsewhere and dragged across it moves nothing. Nor does the map itself — it has no authority over the view. |
Best practices
Do
- Feed it the canvas’s own onViewChange. The rectangle is arithmetic on those three numbers, so a map wired that way cannot disagree with the frame it maps — and keeping a second copy of where the viewport is, is exactly how one comes to.
- Measure content at the artwork’s natural CSS size. Everything here is scaled by width over content.width, so a wrong content width scales the miniature and the rectangle by the same wrong factor: the map still looks plausible and points at the wrong part of the picture. A width of 0 draws an empty plate instead, which is what a measurement that has not landed yet should look like.
- Let the canvas say how big the frame is. Every view a DiagramCanvas emits carries the frame it was measured against, so passing onViewChange through is enough — the frame prop is for a frame this component cannot be told about, and a hand-declared one is the single number that makes the rectangle lie.
- Send onSeek straight to the canvas handle’s centerOn. The point arrives in content coordinates, which is the space centerOn already takes, so recentring needs no conversion of your own.
Don’t
- It is pointer-only and hidden from assistive technology: no tab stop, no keys, and the miniature deliberately publishes nothing because the figure it mirrors already does. The canvas’s arrow keys stay the keyboard’s route to a far corner, so the minimap must never be the only way to reach one.
- The miniature is the same markup shrunk, not a simplified drawing — every label goes down with it, so a wide figure at 200px is a shape rather than a reading copy. It answers where you are; what a node says is the figure’s job.
Examples
default
A map is only worth drawing when the frame is smaller than the artwork, so frame carries the canvas’s own box in CSS pixels and content the figure at its natural size. The rectangle is derived from the canvas’s view on every change rather than stored, which is why the two can never come to disagree. Clicking or dragging on the map reports a point in the artwork’s coordinates and centerOn does the moving — the minimap has no authority over the view, it only says where the reader pointed.
5 elements and 4 relationships.
- Browser (external)
- CloudFront (cloud)
- API (backend)
- Postgres (database)
- SQS (messagebus)
- Browser → CloudFront
- CloudFront → API
- API → Postgres
- API → SQS
Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.
what the rectangle is
Three copies of one map, differing in nothing but view. The rectangle is arithmetic on those three numbers — the content point at the frame’s top-left is the negated offset over the zoom, scaled to the map — so there is no second copy of where the viewport is and nothing to keep in step. Zooming in shrinks the rectangle rather than moving it, which is the reading a reader wants: it says how much of the whole they are currently looking at. None of these takes onSeek, so they are readouts rather than controls and the pointer keeps its ordinary cursor.
when a minimap earns its place
The two cases side by side. On the left the frame is a window on something four times its area, so the rectangle picks out a part and the map answers where you are; on the right the frame already holds the whole artwork, the rectangle covers the map edge to edge, and every pixel of it says the one thing the reader can already see. Render a minimap only in the first case — in the second it is a second drawing of the diagram that costs a render and tells nobody anything.
Accessibility
- The miniature is aria-hidden. The figure it mirrors already publishes its own summary, and a second copy would read the whole diagram out twice.