# Info Popover — unified metric-help affordance **Date:** 2026-07-22 **Status:** Approved (design) ## Problem Two different "info affordance" patterns explain metrics across the app, and both are broken: 1. **`MetricTooltip` (ⓘ)** — used on the two detail pages (`SchoolDetailView`, `SecondarySchoolDetailView`). Its bubble is `position: absolute` with a fixed `220px` width and no viewport-collision detection. Near a screen edge on mobile the bubble renders **partly or wholly off-viewport with no way to scroll to it** — effectively unusable. A `left: -12px` mobile hack only shifts the problem, it doesn't solve it. 2. **Compare-section `?` help** — every compare section funnels through `RowLabel` in `components/compare/sectionShared.tsx`, which uses the **native `title=` attribute**. On desktop the native tooltip has a long, unconfigurable hover delay; on touch it barely surfaces at all. We want a single component that positions itself correctly in any viewport and shows a custom (non-native) tooltip on desktop. ## Decisions - **Positioning: `@floating-ui/react`** (industry standard, ~10KB gzipped, React 19 compatible). Chosen over a hand-rolled portal + JS positioning because the current hand-rolled approach is exactly what failed, and Floating UI already solves flip/shift/portal/interactions/ARIA. - **Mobile presentation: repositioning popover** (not a bottom-sheet). Same small bubble as desktop; Floating UI's `shift`/`flip` keep it fully on-screen. One presentation to build and maintain, consistent across platforms. - **Glyph: standardise on the circled `?`** everywhere (replaces ⓘ on the detail pages). Matches the common "help" convention. - **Bundle:** adding `@floating-ui/react` as a runtime dependency is accepted. ## Architecture One shared engine, two thin adapters — **no call-site churn**. ### `InfoPopover` (new — `components/InfoPopover.tsx`) Owns all behaviour via `@floating-ui/react`. - **Trigger:** a real `