PURPOSE
Universal bottom navigation bar for the React metagame shell. Renders in two distinct modes: a default main-menu bar with Shop/Play/Ships tabs used across Hub/Shop/Ships/Mission/Profile/Collection, and an overlay bar with a wide BACK button plus optional right-aligned tabs used inside fullscreen overlays such as the planet track and challenges.
OWNS
BottomNav— default-export functional component rendering the three-tab main-menu bar.OverlayBottomBar— named-export functional component rendering the overlay variant.OverlayTab— exported TypeScript interface describing a single tab passed to the overlay variant (label, optionalicon,onClick, optionalactive).OverlayBarProps— internal props interface for the overlay variant (onBack, optionaltabs).- Inline SVG icon paths for Shop (cart), Play (triangle), and Ships (ship silhouette).
- Active-tab detection logic derived from the current router pathname.
READS FROM
react-router-dom—useNavigateanduseLocationfor routing and active-route detection.@starship-survivors/services/anchor-registry—useAnchorto register the Ships tab as thenav-collectionanchor.location.pathname— drives the three active-state checks:isPlayfor/,isShopfor paths starting with/shop,isShipsfor paths starting with/games/starship-survivors/ships.- CSS custom properties from the medical-UI scope:
--med-panel-raised,--med-border,--med-accent,--med-text-muted. - Shop CSS overrides under the BOTTOM NAV section of
screens/shop/shop.css.
PUSHES TO
- React Router via
navigate('/shop'),navigate('/'),navigate('/games/starship-survivors/ships')on tab click. - The anchor registry, which receives the Ships tab’s DOM ref under the key
nav-collection. - Consumer-supplied callbacks:
onBackand per-tabonClickhandlers in the overlay variant.
DOES NOT
- Manage any local component state.
- Persist anything to stores, Supabase, or localStorage.
- Render badges, counts, notification dots, or dynamic icons.
- Handle keyboard navigation, focus management, or aria roles beyond the semantic
<nav>element. - Define its own styling — all visual rules live in shared CSS (medical-UI tokens plus shop overrides).
- Render itself outside a
.sig-shopCSS scope; styling silently fails if that contract is violated. - Animate transitions between active tabs.
- Hide or conditionally remove tabs in the main-menu variant — the three tabs are always rendered.
Signals
- Click on a main-menu tab triggers
navigate(...)and updates router state; the active class flips on next render vialocation.pathnamecomparison. - Click on the overlay BACK button invokes the consumer’s
onBackcallback. - Click on an overlay tab invokes that tab’s
onClickcallback;activeis consumer-controlled and reflected via theactiveCSS class. - The Ships tab’s DOM node is registered with the anchor registry on mount under
nav-collection, allowing other systems to position tutorials, tooltips, or effects relative to it.
Entry points
- Default export
BottomNavis consumed byV32Shelland mounted across the main metagame screens. - Named export
OverlayBottomBaris imported by fullscreen overlay screens (planet track, challenges) that supply their ownonBackand optional tab array. - The
OverlayTabinterface is imported by overlay-mounting screens to type their tab definitions.
Pattern notes
- Two modes share the same
.bottom-barroot class so height, background, and 1px top hairline are identical between variants; the overlay variant adds.bottom-bar-overlayfor layout differences. - Active-state detection is pathname-based with
startsWithfor nested routes (Shop, Ships) and exact match for the root Play route. - The Shop and Play tabs are interactive
<div>elements rather than buttons, while the overlay BACK and tab elements are real<button>elements — a deliberate split tied to the existing.bottom-bar-btnversus.btn-ctastyling. - The BACK button reuses the shop’s
.btn-ctastyling inside a.back-btn-fire-wrapto inherit the LAUNCH-style fire-stroke wrapper rhythm at a smaller size. - The overlay layout uses a
flex: 1spacer div to push optional tabs to the right of the BACK button. - Styling is governed by ADR 20260513-001 (medical-UI shared chrome); this component is a pure consumer of those tokens and does not introduce its own colors.
- Icons are inline SVG with
fill="currentColor"so they inherit the active/inactive text color from the surrounding tab class. - The component has no props for the main-menu variant — all routing state comes from React Router and all active-state derives from
location.pathname.