View heading
Names the view, sets the document title, and is where focus lands when the route changes.
Overview
The view heading looks like a piece of typography and is mostly a piece of behaviour. It names the view, it sets the browser tab so a clinician with four tabs open can tell which patient is in which, and it is what receives focus when the route changes — the commonest defect in a client-rendered application is that nothing does.
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.
Moving between screens
This is where a navigation component stops being a list of links and starts being a piece of software, and it is where most of the real defects are.
- Focus moves to the heading of the new view. The commonest failure in a client-rendered application: the route changes, the DOM is replaced, and focus is left on a button that no longer exists or reset to the document body. A screen-reader user hears nothing at all and a keyboard user starts from the top of the page. This is invisible in testing with a mouse.
- The change is announced, once. Focus moving to the new heading is the announcement — a second, live-region announcement of the same words is noise. Before: a polite live region naming the view the reader has arrived at, because the visual cue — the panel highlight moving — carries no sound.
- The title changes too. It is what a screen reader reads on arrival, what a bookmark stores, and what a tab shows on a workstation running four applications.
- Back and forward work, always. Navigation state lives in the URL, so the browser's own controls behave and a screen is addressable — the same rule as Tabs. A clinician who presses Back and loses their place will stop pressing Back, which costs more later.
- The overlay panel is a real overlay. Focus moves into it on open, is
contained while it is there, Escape closes it, and focus returns to the control that opened
it. The control carries
aria-expanded. Every one of those is skipped routinely and each is a trap. - Each navigation region is a labelled landmark. More than one
<nav>on a page means each needs its own name, or a screen-reader user gets a list of identical landmarks and has to enter each to tell them apart. - A skip link precedes everything. The first focusable element on the page jumps past the navigation to the main content, per Accessibility.
- No context menu on navigation. A right-click menu on a nav item is undiscoverable, has no touch equivalent that is not a long press competing with scroll, and hides actions from the reader who most needs them. If an action matters it is on screen.
Navigating away is the one moment the interface can lose work or lose sight of a condition. Unsaved entry prompts rather than being discarded, per Textarea. An active alarm does not stop navigation — the clinician may be moving because of it — but the alarm travels, because the alarm layer sits above the view being replaced. And work still running is reported somewhere the reader will see it, rather than completing into a screen nobody is on — see Pending & unreviewed work.
What the component enforces, and what it only asks for
Unusually, the behaviour is held and the content is not — and here the content is the part with a wrong-patient consequence.
| Rule | Held by | How |
|---|---|---|
| Focus moves here on a route change | The component | Keyed on viewKey, to a heading with tabIndex={-1}. Without it a keyboard user is left on a control that no longer exists.Asserted: ViewHeading: a route change moves focus to the new heading |
| The arrival is announced | The component | Once, by focus: a screen reader reads the heading it lands on. A polite live region also repeated the same words fifty milliseconds later, so every arrival was spoken twice. Focus does not move on the first render, which on page load skipped the skip link. Asserted: AX tree: the view heading is a heading that takes focus, and is announced once |
| The document title is set | The component | From documentTitle, on every view change.Asserted: ViewHeading: the document title follows the view |
| The title names the subject | You | It is a string. A tab reading “ECG review” for four different patients is how a clinician with several tabs open acts in the wrong one. |
viewKey actually changes between views | You | Pass a stable key and focus never moves, which is the defect this component exists to prevent, wearing the component that prevents it. |
A rule stated on a component page reads as a rule the component keeps. 3 of these 5 are; 2 are obligations this page was quietly handing to the reader. The mechanism is reliable and silent when misused. Both of the rules that are yours fail without any visible symptom.
Do's and don'ts
On arriving at a new view:
focus moves to its heading · title updates ·
"Troponin trend" announced politely
A keyboard user starts where the content does; a screen-reader user is told they arrived. Neither is true by default in a client-rendered application.
On arriving at a new view:
the panel highlight moves.
Focus was left on a link that no longer exists, so it reset to the top of the document. Nothing was announced. Invisible in testing with a mouse.
ECG review
tab: ECG review — HARLAND, A.
The document title names the patient. A clinician with four tabs open can tell which is which from the tab strip.
ECG review
tab: AcuteLine
Four tabs, four patients, one title. The clinician acts in the wrong one, and the tab strip gave them no way to know.
→ route changes → focus lands on Troponin review
announced: “Troponin review”
A keyboard user is told they arrived, and where.
→ route changes → focus: (top of document)
The route changed and nothing moved. Focus was on a link that no longer exists, so the browser reset it to the top, and a screen-reader user has to hunt for what happened.
Accessibility
- Focus moves here on a route change, to a heading with
tabIndex={-1}. Without it a keyboard user is left on a control that no longer exists and the browser resets them to the top of the document. - The change is announced once, by focus moving to the heading, so a screen-reader user is told they arrived somewhere and is not told twice. No focus move on the first render.
- One
h1per view, and it says what the view is, not what the product is. - The document title names the subject, because the tab strip is where a clinician with several patients open disambiguates them.
Outcomes of use
- Effectiveness — the reader knows the view changed and what it changed to, whichever way they are reading.
- Efficiency — no hunt for the new content after a navigation.
- Satisfaction — not losing your place, which is what a route change costs when focus is not managed. Attributes believed to contribute to an outcome; the outcome is settled by observing real use, not by this page — see Usability & context of use.
Clinical safety notes
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.
- The document title names the subject of the view. (the component sets it; naming the subject is you) Mitigates: acting in the wrong tab, on the wrong patient.
- Focus is placed deliberately on every route change. (the component) Mitigates: a keyboard or screen-reader user acting on a screen they were never told they had reached.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/view-heading.json
<ViewHeading
viewKey={pathname} // changing it moves focus and re-announces
documentTitle={`ECG review — ${patient.display}`}
>ECG review</ViewHeading>
Related
- Navigation — the route change this is the end of.
- Masthead · Side navigation · Breadcrumb
- Interaction states — where focus is allowed to go.