Skip to content

Tab List: Add tab semantics and arrow key navigation - #532

Open
AlexOpasnost wants to merge 2 commits into
Ajaxy:masterfrom
AlexOpasnost:tablist-accessibility
Open

Tab List: Add tab semantics and arrow key navigation#532
AlexOpasnost wants to merge 2 commits into
Ajaxy:masterfrom
AlexOpasnost:tablist-accessibility

Conversation

@AlexOpasnost

Copy link
Copy Markdown

Problem

Chat folder tabs (All, Personal, Groups and so on) are plain <div>s with a click handler. They have no tab semantics, and nothing inside the tab list is focusable, so keyboard and screen reader users cannot reach or switch folders at all.

Fix

All of it is in ui/TabList.

The container gets role="tablist". Each tab gets role="tab" and aria-selected. Tabindex is roving, so the whole list is a single tab stop. Left and Right arrows move focus and activate the tab, wrapping at both ends, and they go through the existing onSwitchTab prop, so there is no second selection path. A :focus-visible outline was added because these elements were never focusable before.

The active indicator renders a mirrored copy of every tab. Those copies get no role, no tabindex and no aria-selected, so they stay out of the accessibility tree and out of the tab order.

Arrow navigation starts from the focused element rather than from the activeTab prop. The prop lags one render behind, so a held arrow key would otherwise stall on a single tab.

RTL needs no arrow flipping. TabList never sets dir, so DOM order matches visual order in every locale.

Mouse, touch and swipe are untouched.

Checks

npm run check:ts and npm run check:css pass.

Verified in dev:mocked with real keyboard input. Roles are correct, there is exactly one aria-selected and one tabIndex=0, the accessibility tree lists each folder once, arrows wrap both ways, Up and Down are not intercepted, focus survives re-render, no console errors.

Checked with NVDA on Windows. A folder tab is announced as its name, then tab, then selected, together with its position in the set. Each folder is announced once, so the mirrored copies stay out of the accessibility tree. Arrow navigation announces the newly selected folder.

Known limits

Forward Tab does not reach the tabs. The search field opens the search panel on focus and that collapses the folder bar. Shift+Tab reaches them. This is pre-existing and separate from this change.

SquareTabList (search tabs) and the vertical folders sidebar have the same gap. I left them alone to keep this scoped.

Closes #106

@netlify

netlify Bot commented Aug 2, 2026

Copy link
Copy Markdown

👷 Deploy request for telegram-tt-previews pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit de14089

@keyang556

Copy link
Copy Markdown

hope merged.

`FormattedDateModal` disables two of its tab lists with `pointer-events: none`,
which blocks the mouse but not the keyboard. Now that tabs are focusable, add an
`isDisabled` prop that drops them from the tab sequence and exposes
`aria-disabled`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

improve the accessibility of the tab component

2 participants