From e2ac6c1d7e4a727a8d146a467c48fa9c3fce9c0c Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Mon, 10 Nov 2025 14:42:42 -0600 Subject: [PATCH] Keep selected item in view --- app/assets/stylesheets/nav.css | 14 ++++++++------ .../controllers/navigable_list_controller.js | 8 ++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/nav.css b/app/assets/stylesheets/nav.css index a8c51a7ad..4fabdfd76 100644 --- a/app/assets/stylesheets/nav.css +++ b/app/assets/stylesheets/nav.css @@ -58,15 +58,16 @@ --panel-padding: var(--block-space) var(--block-space) 0 var(--block-space); --panel-size: 45ch; --popup-display: grid; + --nav-section-gap: 2px; - align-items: stretch; + block-size: auto; box-shadow: 0 0 0 1px oklch(var(--lch-blue-medium) / 5%), 0 0.2em 0.2em oklch(var(--lch-blue-medium) / 5%), 0 0.4em 0.4em oklch(var(--lch-blue-medium) / 5%), 0 0.8em 0.8em oklch(var(--lch-blue-medium) / 5%); display: grid; grid-template-rows: auto 1fr auto; - gap: 2px; + gap: var(--nav-section-gap); overflow: hidden; scrollbar-gutter: stable both-edges; } @@ -74,10 +75,11 @@ .nav__scroll-container { display: flex; flex-direction: column; - gap: 2px; - margin-inline: -1ch; + gap: var(--nav-section-gap); + margin-inline: calc(-1 * var(--block-space)); /* space for scrollbar */ overflow: auto; - padding-inline: 1ch; /* room for scrollbar */ + padding-block-end: var(--nav-section-gap); + padding-inline: var(--block-space); } .nav__close { @@ -156,7 +158,7 @@ border-block-start: 1px solid var(--color-ink-lighter); font-size: var(--text-small); line-height: 1.6; - margin-block-start: var(--block-space-half); + margin-block-start: calc(-1 * var(--nav-section-gap)); padding: 1.5ch; text-align: center; z-index: 1; diff --git a/app/javascript/controllers/navigable_list_controller.js b/app/javascript/controllers/navigable_list_controller.js index a5aa1a94f..46a5d2782 100644 --- a/app/javascript/controllers/navigable_list_controller.js +++ b/app/javascript/controllers/navigable_list_controller.js @@ -80,6 +80,14 @@ export default class extends Controller { selectedItem.setAttribute(this.selectionAttributeValue, "true") this.currentItem = selectedItem await nextFrame() + // Ensure the selected item is visible inside any scrollable container. + // Use 'nearest' so we avoid jumping the viewport unnecessarily. + try { + this.currentItem.scrollIntoView({ block: "nearest", inline: "nearest" }) + } catch (e) { + // scrollIntoView may not be supported in older environments; ignore. + } + if (this.focusOnSelectionValue) { this.currentItem.focus() } if (this.hasInputTarget && id) { this.inputTarget.setAttribute("aria-activedescendant", id)