From 2d1fb1e711ae35fed713373abc20f5cc11ae483c Mon Sep 17 00:00:00 2001 From: benjaminleonard Date: Mon, 18 May 2026 15:49:08 +0100 Subject: [PATCH] Tab hover animation --- app/components/RouteTabs.tsx | 35 +++++++++++----------- app/ui/lib/Tabs.tsx | 7 +++-- app/ui/styles/components/Tabs.css | 48 +++++++++++++++++++++++++++---- 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/app/components/RouteTabs.tsx b/app/components/RouteTabs.tsx index 630b59103..739027104 100644 --- a/app/components/RouteTabs.tsx +++ b/app/components/RouteTabs.tsx @@ -16,26 +16,26 @@ const selectTab = (e: React.KeyboardEvent) => { // Don't intercept modified arrow keys (Cmd for browser back/forward, etc.) if (hasModifier(e)) return - const target = e.target as HTMLDivElement - if (e.key === KEYS.left) { - e.stopPropagation() - e.preventDefault() + const target = e.target as HTMLElement + const isArrow = e.key === KEYS.left || e.key === KEYS.right + if (!isArrow) return - const sibling = (target.previousSibling ?? - target.parentElement!.lastChild!) as HTMLDivElement - - sibling.focus() - sibling.click() - } else if (e.key === KEYS.right) { - e.stopPropagation() - e.preventDefault() + // Walk only role=tab siblings so non-tab children (e.g. the hover pill) are skipped + const tabs = Array.from( + target.parentElement!.querySelectorAll('[role="tab"]') + ) + const i = tabs.indexOf(target) + if (i === -1) return - const sibling = (target.nextSibling ?? - target.parentElement!.firstChild!) as HTMLDivElement + e.stopPropagation() + e.preventDefault() - sibling.focus() - sibling.click() - } + const next = + e.key === KEYS.left + ? tabs[(i - 1 + tabs.length) % tabs.length] + : tabs[(i + 1) % tabs.length] + next.focus() + next.click() } export interface RouteTabsProps { @@ -66,6 +66,7 @@ export function RouteTabs({ className={cn(sideTabs ? 'ox-side-tabs-list' : 'ox-tabs-list', tabListClassName)} onKeyDown={selectTab} > + {!sideTabs &&