Redesign: Fix "Skip to main content" link (#40626)

This commit is contained in:
diondiondion
2026-09-21 12:56:38 +00:00
committed by GitHub
parent b6b52b92c7
commit 152de5e8e8
6 changed files with 59 additions and 5 deletions

View File

@@ -6,6 +6,8 @@ import { Link } from 'react-router-dom';
import { IconLogo } from '@/mastodon/components/logo';
import { customAppIcon, domain, title } from '@/mastodon/initial_state';
import { getNavigationSkipLinkId } from '../../ui/components/skip_links';
import classes from './header.module.scss';
export const NavigationHeader: React.FC<{
@@ -24,7 +26,11 @@ export const LogoLockup: React.FC<{
className?: string;
}> = ({ siteName, className }) => {
return (
<Link to='/' className={classNames(classes.siteNameLink, className)}>
<Link
to='/'
className={classNames(classes.siteNameLink, className)}
id={getNavigationSkipLinkId()}
>
{customAppIcon && (
<img src={customAppIcon} alt='' className={classes.appIcon} />
)}

View File

@@ -11,6 +11,16 @@
gap: var(--multi-column-gap);
padding-inline-end: var(--multi-column-gap);
overflow-x: auto;
scroll-padding-inline-start: var(--multi-column-navigation-width);
&:focus-visible {
// Scrollable elements are made focusable by some browsers, this adds
// a visible outline in this case. Using `border` instead of `outline`
// causes a light layout jump shift on focus, but an outline would be
// rendered below the navbar and columns and didn't look right.
border: var(--outline-focus-default);
border-radius: var(--radius-2);
}
}
.navigationWrapper {

View File

@@ -2,15 +2,16 @@ import { useCallback, useId } from 'react';
import { useIntl } from 'react-intl';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import { useAppSelector } from 'mastodon/store';
import classes from './skip_links.module.scss';
export const getNavigationSkipLinkId = () => 'skip-link-target-nav';
export const getColumnSkipLinkId = (index: number) =>
`skip-link-target-content-${index}`;
export const getColumnSkipLinkId = (index: number | null) =>
`skip-link-target-content-${index ?? ''}`;
export const SkipLinks: React.FC<{
const LegacySkipLinks: React.FC<{
multiColumn: boolean;
onFocusGettingStartedColumn: () => void;
}> = ({ multiColumn, onFocusGettingStartedColumn }) => {
@@ -54,6 +55,23 @@ export const SkipLinks: React.FC<{
);
};
const RedesignSkipLinks: React.FC = () => {
const intl = useIntl();
return (
<div className={classes.list}>
<div className={classes.listItem}>
<SkipLink target={getColumnSkipLinkId(1)} hotkey='2'>
{intl.formatMessage({
id: 'skip_links.skip_to_content',
defaultMessage: 'Skip to main content',
})}
</SkipLink>
</div>
</div>
);
};
const SkipLink: React.FC<{
children: string;
target: string;
@@ -82,3 +100,7 @@ const SkipLink: React.FC<{
</>
);
};
export const SkipLinks = isRedesignEnabled()
? RedesignSkipLinks
: LegacySkipLinks;

View File

@@ -1,6 +1,6 @@
.list {
position: fixed;
z-index: 100;
z-index: 200;
margin: 10px;
padding: 10px 16px;
border-radius: 10px;

View File

@@ -649,6 +649,7 @@ class UI extends PureComponent {
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}>
{!minimalShell && (
<SkipLinks
// TODO: Remove these props & related methods when isRedesignEnabled() flag is removed
multiColumn={layout === 'multi-column'}
onFocusGettingStartedColumn={this.handleHotkeyGoToStart}
/>

View File

@@ -1,3 +1,5 @@
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import {
getColumnSkipLinkId,
getNavigationSkipLinkId,
@@ -45,11 +47,24 @@ function focusColumnTitle(index: number, multiColumn: boolean) {
}
}
function focusRedesignColumnTitle(index: number) {
const idToFocus =
index === 1 ? getNavigationSkipLinkId() : getColumnSkipLinkId(index - 1);
document.querySelector<HTMLElement>(`#${idToFocus}`)?.focus();
}
/**
* Move focus to the column of the passed index (1-based).
* Focus is placed on the topmost visible item, or the column title.
*/
export function focusColumn(index = 1) {
if (isRedesignEnabled()) {
// In the redesign, always focus the column title
focusRedesignColumnTitle(index);
return;
}
// Skip the leftmost drawer in multi-column mode
const isMultiColumnLayout = !!document.querySelector(
'body.layout-multiple-columns',