diff --git a/neode-ui/src/composables/useControllerNav.ts b/neode-ui/src/composables/useControllerNav.ts index c0091c85..ec541d1d 100644 --- a/neode-ui/src/composables/useControllerNav.ts +++ b/neode-ui/src/composables/useControllerNav.ts @@ -149,13 +149,23 @@ function findNearestInDirection( }) scored.sort((a, b) => { - if (b.overlap !== a.overlap) return b.overlap - a.overlap - if (a.dist !== b.dist) return a.dist - b.dist - // Tiebreaker: prefer leftmost for up/down - if (direction === 'up' || direction === 'down') { - return a.el.getBoundingClientRect().left - b.el.getBoundingClientRect().left + const isVertical = direction === 'up' || direction === 'down' + // For vertical nav: prefer closest element first, use overlap as tiebreaker. + // This prevents a distant full-width element from winning over a closer narrow one. + if (isVertical) { + // Both have overlap — prefer closer distance + if (a.overlap > 0 && b.overlap > 0) { + if (a.dist !== b.dist) return a.dist - b.dist + if (b.overlap !== a.overlap) return b.overlap - a.overlap + return a.el.getBoundingClientRect().left - b.el.getBoundingClientRect().left + } + // One has overlap, the other doesn't — prefer the one with overlap + if (a.overlap !== b.overlap) return b.overlap - a.overlap + return a.dist - b.dist } - return 0 + // Horizontal: overlap first (same row), then distance + if (b.overlap !== a.overlap) return b.overlap - a.overlap + return a.dist - b.dist }) return scored[0]?.el ?? null