From 328ba1959542ed8c0c86b42f4c301147c4ab5f52 Mon Sep 17 00:00:00 2001 From: iris Date: Tue, 18 Aug 2026 22:35:58 +0200 Subject: [PATCH] swarm-ui: fix nav underline gap argus caught in round 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix put both the touch-target min-height and the active-state border-bottom on the same : centering the text within a 2.75em box pushed the border ~0.7em away from it, a real visible regression argus caught by actually rendering the CSS rather than reasoning about the box model abstractly. Split the two concerns onto two elements: the (.shell-nav-link) owns the full-height tappable box, an inner (.shell-nav-link-text) wrapping just the label owns the underline, so the indicator stays directly under the text regardless of the tappable box's height. Verified: tsc clean, build succeeds, screenshotted close-up (600x100) and at 320px — underline sits flush under the text in both, no gap. --- .../packages/swarm-ui/src/shell/Shell.css | 25 ++++++++++++------- .../packages/swarm-ui/src/shell/Shell.tsx | 8 ++++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/frontend/packages/swarm-ui/src/shell/Shell.css b/frontend/packages/swarm-ui/src/shell/Shell.css index 48d0a31f..b3bfffc4 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.css +++ b/frontend/packages/swarm-ui/src/shell/Shell.css @@ -28,25 +28,32 @@ flex-wrap: wrap; gap: 1em; } -/* `display: flex` + `min-height` rather than more vertical padding: a - link's own height is text-line-height, and padding alone would have - pushed the border-bottom indicator away from the text to hit 2.75em - — flex centres the text within the full-height box instead, so the - indicator still sits directly under it. Same 2.75em (~44px, WCAG - 2.5.5) floor as the rest of the shared kit — this is the primary - nav, the single most-clicked/tapped element in the whole shell, so - it gets the floor same as everything else, not a smaller one. */ +/* Touch target and active-state underline are deliberately on two + different elements. An earlier version put both `min-height: 2.75em` + and `border-bottom` on the same box: centering the text within a + 2.75em box pushes a bottom-edge border ~0.7em away from the text, + which argus caught by actually rendering it — a real visual + regression, not just a touch-target win. So: `.shell-nav-link` (the + ``) owns the full-height tappable box (same 2.75em / ~44px WCAG + 2.5.5 floor as the rest of the shared kit — this is the primary nav, + the single most-clicked/tapped element in the whole shell), and + `.shell-nav-link-text` (an inner span hugging just the text) owns + the underline, so the indicator stays directly under the label + regardless of how tall the tappable box around it is. */ .shell-nav-link { display: flex; align-items: center; min-height: 2.75em; color: var(--muted); text-decoration: none; - border-bottom: 2px solid transparent; } .shell-nav-link:hover { color: var(--fg); } +.shell-nav-link-text { + padding-bottom: 0.25em; + border-bottom: 2px solid transparent; +} .shell-nav-link-active { color: var(--fg); border-bottom-color: var(--purple); diff --git a/frontend/packages/swarm-ui/src/shell/Shell.tsx b/frontend/packages/swarm-ui/src/shell/Shell.tsx index fe5fc69d..70538e0b 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.tsx +++ b/frontend/packages/swarm-ui/src/shell/Shell.tsx @@ -34,8 +34,12 @@ const DEFAULT_BRAND = 'hyperhive swarm'; function NavLink({ href, label }: { href: string; label: string }) { const [active] = useRoute(href); return ( - - {label} + + {/* Touch target (min-height) lives on the so the whole row is + tappable; the active-state underline lives on this inner span + so it hugs the text instead of sitting at the bottom of the + full-height box, ~0.7em away from it. */} + {label} ); }