Badge & pagination
Both components exist because a set is bigger than the screen. A badge says how many there are; pagination says which ones you are looking at. Both are answers to the same clinical question — what am I not seeing? — and both are dangerous when they answer it approximately.
Overview
A badge is a count and nothing else. It never carries clinical state, never replaces a Status chip, and never becomes an alarm by turning red. A count of three tells you there are three things; it does not tell you any of them is critical, and it must not pretend to.
Pagination is the more consequential of the two, because it hides things. Its rule follows directly from Filter: a clinician must always be able to see the size of the whole set and know that something outside the current page needs attention.
Counts are exact
No "99+", no rounding, no approximation. A number a clinician cannot trust is worse than no number.
A count is not a state
Badges never take an IEC alarm hue. Quantity and priority are separate facts and get separate components.
The whole set is knowable
Pagination always shows the total, and always reports anything urgent on a page you are not on.
Badge
-
Laboratory 6 1 criticalCount and priority as two separate statements
-
Prior recordings 3A plain count, carrying no clinical claim
-
Imaging 0Zero is rendered, not hidden — "none ordered" is information
A badge never uses an --alarm-* colour to mean "some of these
are serious". A red badge is an alarm signal borrowed for a quantity, and it does two kinds of
damage: it dilutes the IEC 60601-1-8 palette everywhere else in the product, and it lets a
critical finding be represented by a number nobody has to open. Where priority exists, say it
in words with a status chip beside the count.
The single exception is documented on Colour and applies only to the alarm banner's own occurrence counter, where the surrounding component already carries the priority.
- Exact, never capped. "99+" hides an order of magnitude. If the number is too wide for the space, the space is wrong.
- Zero renders. A hidden zero is indistinguishable from an unloaded count, which is the ambiguity Key–value pair exists to prevent.
- Unknown is not zero. A count that failed to load renders as
—with a reason, never as0. - Never the only carrier of meaning. A badge always sits beside a label naming what is being counted.
- Never a dot alone for anything clinical. A dot says "something changed" and nothing else; it is permitted only for non-clinical notifications.
- Never on a disabled or unavailable control — a count on something you cannot open is a dead end.
Pagination
Prefer not to paginate at all. Virtual scrolling with a fixed header keeps the set continuous and the total visible, and it does not create the class of error where a patient exists on page three. Paginate when the set is genuinely large, the order is stable, and a page boundary carries no clinical meaning.
| Part | Rule |
|---|---|
| Range and total | Required. 1–25 of 63. Never "Page 1 of 3" alone — the
clinician needs the set size, not the page count. |
| Order statement | Required. Page boundaries are meaningless without a stated order, and a changing order moves items between pages. |
| Current page | aria-current="page" plus fill and weight. Never fill alone. |
| Off-page alarms | Anything unacknowledged at advisory priority or above on another page is reported above the set, with a route to it. |
| Page size | Fixed per view and remembered per session. It never changes as a side effect of sorting or filtering. |
Never paginate an alarm queue, an acknowledgement list, a handover view, or any set a clinician is expected to work to zero. In those views every item must be reachable by scrolling, because a page boundary is an invitation to stop. See Triage worklist and Acknowledge & escalate.
States
| State | Rendering |
|---|---|
| Single page | Controls hidden; the range line still renders — 1–19 of 19. |
| Loading a page | The previous page stays on screen with activity shown. Pagination never blanks the region, and the control keeps its width so the layout does not jump. |
| Total unknown | 1–25 of at least 25 with the reason. Never a fabricated total and never
a bare page number. |
| Set changed underneath | Position is re-anchored to the item the clinician was on, and the change is stated. Never silently the same page index over different content. |
| Count unloaded (badge) | — with an accessible reason, never 0. |
| Count updating (badge) | Updates on data arrival without animation, and never reorders what it sits on. |
Do's and don'ts
Quantity and priority stated separately. Six results; one of them is critical; both facts survive greyscale.
A red count. It borrows the IEC critical hue to mean "some of these matter", and dilutes it everywhere the product uses it properly.
Measured zero and unknown are different renderings. One is a fact; the other is a gap.
A hidden zero and a failed count rendered as zero. The clinician reads "nothing to see" twice, and is wrong once.
Range, total and order. The clinician knows exactly how much of the record is off-screen and why it is off-screen.
A page count instead of a set size. Three pages of what — thirty results or three hundred?
A page boundary is a display convenience, not a boundary on what the clinician is told.
Two urgent results sit on page 3 and nothing on page 1 says so. Most clinicians never reach page 2.
One page, controls hidden, range line still present. "You are seeing everything" is worth saying out loud.
A capped count. The difference between 100 and 900 is the difference between a manageable list and an unworkable one.
Accessibility
- A badge is never a bare number. Its accessible name includes what is counted — "Laboratory, 6 results" — because "6" announced alone is meaningless.
- Badges that change are announced politely (
aria-live="polite") or not at all. Never assertive: a count is not an interruption. - Never colour alone. Badge styling carries no meaning, so there is nothing for colour to fail to convey — which is the point.
- Pagination is a
<nav>with anaria-label, and each control is labelled by page — "Page 3", not "3". aria-current="page"on the active control, alongside fill and weight.- The range line is a live region so changing page announces the new range and total.
- Focus after a page change moves to the top of the set's region, not to the next pagination button, so a keyboard user is not left below the content they requested.
- Targets ≥ 24 px with ≥ 24 px between centres (WCAG 2.2 SC 2.5.8). Page numbers are the control most often built at 18 px.
- Disabled previous/next stay in the tab order as
aria-disabledwith a reason, rather than vanishing and shifting the row under a travelling pointer. - Reflow to 320 px — page numbers collapse to previous/next plus the range line, never to an icon-only pair (SC 1.4.10).
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.
- Effectiveness — the clinician knows the true size of the set and is told about anything urgent they cannot currently see.
- Efficiency — a count answers "is it worth opening?" without opening anything, which is most of what counts are for.
- Satisfaction — exact numbers. A capped or approximate count is the kind of small dishonesty that makes people stop believing the rest of the screen.
Clinical safety notes
Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1).
- Badges never take an alarm hue. Mitigates: dilution of the IEC 60601-1-8 palette, and priority conveyed by a number nobody opens.
- Counts are exact and uncapped. Mitigates: a set's true size being unknowable at a glance.
- Zero rendered; unknown rendered differently. Mitigates: a failed count read as an absence of results.
- Range and total always shown. Mitigates: a page read as the whole record.
- Order stated alongside the range. Mitigates: items moving between pages under an order the clinician cannot see.
- Off-page urgent items reported above the set. Mitigates: an unacknowledged result on a page never visited.
- Alarm queues, acknowledgement lists and handover views are never paginated. Mitigates: a page boundary read as the end of the work.
- Position re-anchored when the set changes. Mitigates: acting on a row that moved beneath the page index.
- Previous page retained while loading. Mitigates: a blank region read as an empty set.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/badge.json
$ npx shadcn@latest add https://md.notjustany.tech/r/pagination.json
// A badge takes a count and the thing being counted. There is no
// variant prop, because a badge has no clinical variants.
<Badge count={6} of="results" />
<Badge count={null} of="results" unavailableReason="Lab feed unreachable" />
// Enforced at the boundary: no alarm token may reach a badge.
if (props.className && /alarm-(critical|urgent|advisory)/.test(props.className)) {
throw new Error("[Badge] alarm colours are reserved. Use StatusChip for priority.")
}
// Pagination requires the set size and the order, and computes what is
// unacknowledged outside the current page.
<Pagination
page={1}
pageSize={25}
total={63}
orderedBy="Newest first"
offPage={{ urgent: 2, page: 3 }}
prohibitedFor={["alarmQueue", "handover"]} // dev-time guard
/>
| Prop | Type | Notes |
|---|---|---|
count | number | null |
Required. null renders —, never
0. |
of | string |
Required. What is counted; goes into the accessible name. |
unavailableReason | string |
Required when count is null. |
total | number | "atLeast" |
Required. Never fabricated; unknown totals render as "at least n". |
orderedBy | string |
Required. Rendered verbatim beside the range. |
offPage | OffPageSummary |
Unacknowledged items at advisory priority or above, elsewhere in the set. |
Related
- Status chip — where clinical priority belongs instead.
- Filter — the same "what am I not seeing?" rule, applied to criteria.
- List & tree — counts on branches, and the denominator rule.
- Data grid — virtual scrolling, the preferred alternative to pagination.
- Colour — why alarm hues are reserved.
- Typography — tabular numerals in counts and page numbers.