From 6e5aa00436053a3cb51d6eeeb1f9ed0d771d5087 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 3 Mar 2026 12:02:04 +0100 Subject: [PATCH] Anchor post navigation via hotkeys to top of viewport (#38036) --- .../mastodon/features/ui/util/focusUtils.ts | 32 ++++++++----------- app/javascript/styles/mastodon/basics.scss | 9 ++++++ .../styles/mastodon/components.scss | 6 ---- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/app/javascript/mastodon/features/ui/util/focusUtils.ts b/app/javascript/mastodon/features/ui/util/focusUtils.ts index 1043d09ed7..8237eb286b 100644 --- a/app/javascript/mastodon/features/ui/util/focusUtils.ts +++ b/app/javascript/mastodon/features/ui/util/focusUtils.ts @@ -47,7 +47,7 @@ function focusColumnTitle(index: number, multiColumn: boolean) { /** * Move focus to the column of the passed index (1-based). - * Focus is placed on the topmost visible item, or the column title + * Focus is placed on the topmost visible item, or the column title. */ export function focusColumn(index = 1) { // Skip the leftmost drawer in multi-column mode @@ -94,8 +94,16 @@ export function focusColumn(index = 1) { window.innerWidth || document.documentElement.clientWidth; const { item, rect } = itemToFocus; + const scrollParent = isMultiColumnLayout + ? container + : document.documentElement; + const columnHeaderHeight = + parseInt( + getComputedStyle(scrollParent).getPropertyValue('--column-header-height'), + ) || 0; + if ( - container.scrollTop > item.offsetTop || + scrollParent.scrollTop > item.offsetTop - columnHeaderHeight || rect.right > viewportWidth || rect.left < 0 ) { @@ -141,11 +149,7 @@ export function focusFirstItem() { /** * Focus the item next to the one with the provided index */ -export function focusItemSibling( - index: number, - direction: 1 | -1, - scrollThreshold = 62, -) { +export function focusItemSibling(index: number, direction: 1 | -1) { const focusedElement = document.activeElement; const itemList = focusedElement?.closest('.item-list'); @@ -173,17 +177,9 @@ export function focusItemSibling( } if (targetElement) { - const elementRect = targetElement.getBoundingClientRect(); - - const isFullyVisible = - elementRect.top >= scrollThreshold && - elementRect.bottom <= window.innerHeight; - - if (!isFullyVisible) { - targetElement.scrollIntoView({ - block: direction === 1 ? 'start' : 'center', - }); - } + targetElement.scrollIntoView({ + block: 'start', + }); targetElement.focus(); } diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index 8cc06a50b3..610730df5a 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -19,6 +19,15 @@ html { &.rtl { --text-x-direction: -1; } + + // Compensate for column header height when scrolling elements into view + --column-header-height: 62px; + + scroll-padding-top: var(--column-header-height); + + &:has(.layout-multi-column) { + --column-header-height: 0; + } } html.has-modal { diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index aee074e453..1b6b47397d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2984,8 +2984,6 @@ a.account__display-name { } &__main { - --column-header-height: 62px; - box-sizing: border-box; width: 100%; flex: 0 1 auto; @@ -9143,10 +9141,6 @@ noscript { .conversation { position: relative; - // When scrolling these elements into view, take into account - // the column header height - scroll-margin-top: var(--column-header-height, 0); - &.unread { &::before { content: '';