Components · Structure

Side navigation

The panel of destinations, whose counts say what is waiting and whose current item is marked by more than colour.

Stable · v1.0 shadcn/ui WCAG 2.2 AA

Overview

A side navigation is a list of places, and the two things it must never get wrong are which one you are on and what is waiting in the ones you are not. Both survive greyscale, because colour is not available to every reader and is the first thing a bad display loses.

How these four sit together — the anatomy of a screen, how deep a hierarchy may go, and what has to happen on a route change — is on Navigation.

Shapes of side navigation

Three are available. Two common ones are not, and the reasons are the same reasons twice.

ShapeUse Constraint
Persistent The default. Labels always visible, panel always present. 184–360 px, fixed. It does not resize with the window, because a panel whose width tracks the viewport moves its items under a returning reader.
Collapsible A control in the masthead collapses the panel to reclaim width for content. Collapsed means gone, not narrowed to icons. Page content reflows; the state is remembered per session and stated in the control.
Overlay Below the breakpoint where a panel and the content both fit. A real overlay with focus containment and Escape — see Routing. It never covers the alert region.
Two shapes this system does not have

An icon-only rail. The common pattern is a 56 px bar of icons whose labels appear on hover or focus. It fails twice here: this system has no icon set, so there is nothing to carry the meaning; and a label available only on hover does not exist on a touch cart, which is half the surfaces this system targets — the rule Tooltip exists to enforce. An unlabelled rail is a navigation the reader must already know.

Navigation that expands on hover. A panel that widens when the pointer enters it changes the page layout without anyone deciding to. The usual mitigations — a delay before expanding, keeping other controls away from the edge — treat the symptom: the design causes accidental expansion and then asks the rest of the screen to make room for it. Expansion is a press, on a labelled control, or it does not happen.

Rules

What the component enforces, and what it only asks for

The rendering rules are held; every fact being rendered is yours.

RuleHeld by How
The current item is marked by more than colourThe component aria-current="page" plus a fill, a weight and an inset rule.
Asserted: SideNav: current matching is segment-wise, not substring
A count names what it countsThe component The count renders through Badge, so a bare number is not expressible.
Asserted: SideNav: a count names what it counts
Labels are never replaced by iconsThe component There is no icon slot. A 56 px rail whose labels appear on hover cannot be built from this.
Asserted: SideNav: an item is a label and a href, with no icon field
The current item is the current itemYou currentHref is compared to what you pass. A route change that does not update it leaves the panel confidently marking the wrong place.
The counts are correct and currentYou They are numbers you supply, with no clock and no subscription. A section with two unreviewed results and a stale zero reads as empty.
It does not overlay the alert regionYou A layout decision — see Grid & layout.
Why this is written down

A rule stated on a component page reads as a rule the component keeps. 3 of these 6 are; 3 are obligations this page was quietly handing to the reader. A navigation panel that marks the wrong item, or shows a stale count, is wrong in exactly the way a reader cannot detect.

Do's and don'ts

Do

Current item marked by fill, weight and an inset rule. Counts show what is waiting inside.

Don't

Current item by text colour alone, no counts. Gone in greyscale, and a section with two unreviewed results looks empty.

Do

Labels present, counts on the item that holds them, current marked by more than colour. Readable by a clinician who has never used this product.

Don't

A 56 px rail whose labels appear on hover. There is no icon set to carry the meaning and no hover on a cart, so this is a navigation the reader must already know.

Do

Expansion is a press on a labelled control that reports its own state. Nothing changes width because a pointer passed over it.

Don't

The rail widens on hover, after 500 ms, and other controls are kept clear of its edge.

The delay and the spacing are treating the symptom. The layout still changes without anyone deciding, and on a cart the pointer gets there by being knocked.

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/side-nav.json
<SideNav
  label="Sections"
  items={[
    { label: "ECG", href: "/ecg" },
    { label: "Troponin", href: "/troponin",
      count: { value: 2, of: { one: "unreviewed result", other: "unreviewed results" } } },
  ]}
  currentHref={pathname}
/>
NotJustAnyMed.Tech Design System · Navigation · v1.0 · draft for review
Covers Masthead, Side navigation and Breadcrumb. AcuteLine is a fictional reference application; all patient data shown is fabricated.