Navigation counters

Async-loaded count badges on navigation/tab items (e.g. "Issues 42", "Pull requests 7") that load in parallel and render together so the nav never reflows item-by-item. Defines the loading contract and the no-layout-thrash rule. Modeled after GitHub Primer UnderlineNav counters.

Status: v0.1.0 — Draft. Promoted from the GitHub Primer parity scan (metadocsstack #092). Source: https://primer.style/components/nav-list (UnderlineNav counters).

R1 — What it is

A counter is a small numeric badge attached to a nav/tab item reflecting a live count for that destination. It reuses the visual of specs/components/badges.kmd (numeric variant) but adds an async load contract (R2–R3) specific to navigation.

R2 — Load in parallel, render together

  • All visible counters load concurrently (one batched request where the

    API allows), not sequentially per tab.

  • They render together in a single layout pass once resolved (or on a

    short ceiling timeout), so the nav reflows at most once — never N times as each count trickles in. This is the core anti-layout-thrash rule (avoids cumulative layout shift).

R3 — Placeholder while loading

  • During load, reserve the counter's space with a

    specs/components/skeleton.kmd placeholder (or a fixed min-width slot) so item widths do not jump when numbers arrive.

  • On error, the counter is simply omitted (the tab still works); failure

    of one counter never blocks the others or the nav.

R4 — Zero and large values

  • A count of zero renders no badge (absence, not "0") unless the

    surface explicitly wants to show zero.

  • Large counts are abbreviated per specs/components/badges.kmd (e.g.

    5k+), with the exact value in the accessible name + title.

R5 — Freshness

  • Counters are a hint, not a transaction: they may be cached/eventually

    consistent. Re-fetch on tab focus / surface revisit, not on a tight poll.

R6 — Accessibility & i18n

  • The count is part of the tab's accessible name ("Issues, 42 items"), not

    an orphan number; the badge itself is aria-hidden when the name already includes it.

  • The loading placeholder is not announced as content.
  • Number formatting + abbreviations localize per

    specs/i18n/contract.kmd (locale digit grouping; "k"/"mil" etc.).

Não-escopo

  • The badge visual itself (specs/components/badges.kmd).
  • The tabnav structure & selection (`specscomponents/tabs.kmd`,

    specs/components/navigation.kmd).

  • Real-time push updates of counts (separate; this is load-time + on-focus).