ADR-0044: Compact navigation adapts to label width
- Status: Accepted
- Date: 2026-10-15 (amended 2026-10-16 — single bounded row; amended 2026-10-17 — selected tab kept in view)
Context
Five equal-width compact navigation items clipped destination names at large
system text sizes. A first fix wrapped the overflowing items into extra rows
based on measured label width, but on narrow screens or large text this doubled
or tripled the bar height (4 + 1 rows), stranded Settings alone on a second
row, and ate list content on every screen.
Once the single scrolling row shipped, the scroll position was still never
synchronised with the selected route: a restored or deep-linked Settings /
Reports could sit outside the visible viewport (its label and Material
indicator offscreen) at narrow widths or large text, because nothing brought the
selected item into view.
Decision
Measure the full localized labels using the navigation label typography and
current density/font scale. Compact navigation keeps one row of five
equal-width items whose height is bounded by Material's single NavigationBar
row (~80 dp plus system insets). When the measured items no longer fit the
available width, the row is laid out at its intrinsic width and scrolls
horizontally behind the shared edge fade (scrollFades); otherwise it fills the
viewport so Home stays the centre tab. Keep destination order, full labels,
the user's font scale, Material selected indicators and semantics, and the
≥48 dp touch targets. Reserve only the measured Material item inset (8 dp per
side); never abbreviate labels, truncate them, hide items behind a menu, or wrap
to extra rows. Apply system insets once around the complete bar. The
medium/expanded navigation rail and tab save/restore policy are unchanged.
The row also keeps the selected destination fully visible. Whenever the
selected route, viewport width, or measured item bounds change, an effect brings
the selected item into view using a short, minimal animateScrollTo (the
smallest movement that reveals the whole item, clamped to the scroll range). The
effect is keyed to those inputs only — never to the live scroll value — so a
manual scroll is preserved and the bar never repeatedly fights the user. The
scroll respects the system animator scale.
Consequences
The bar consumes a single bounded row at every supported width and text size, so it never strands a destination alone or steals a second row from list content. On the narrowest widths or the largest text the off-screen destination requires a horizontal scroll; the edge fade makes that discoverable. Home remains the centre tab whenever the five items fit. A restored or deep-linked selection is always shown complete, so its label and selected indicator can never be hidden.
Verification
Instrumented compact-navigation tests measure each label's laid-out width against its full single-line intrinsic width and its bounds against its item, assert every label shares one vertical band with a bounded bar height, and scroll the row to select all five destinations at 320/411 dp with 100%, 150%, and 200% text in light/dark themes. Selected-tab visibility is asserted for an externally selected (restored/deep-linked) route at 320 dp / 200% text, in RTL, and after a viewport resize, plus a live deep-link launch. Navigation state regressions exercise saved tab state, Home navigation, and Back. Home viewport budget tests assert the summary and two complete recent rows stay above the fold at 320 dp / 100% text when no capture-status notice is shown (see ADR-0053's Amendment for the notice case).