fix(ui): Tabs is an ARIA tab list with roving focus and a wired panel

The rail was a row of aria-pressed toggles: every tab a Tab stop, no arrows,
nothing tying a tab to its content. tablist/tab/aria-selected, one Tab stop,
arrows on either axis, Home/End, and useTabPanel for aria-controls/labelledby.
This commit is contained in:
2026-09-27 14:22:37 +02:00
parent a975ab627a
commit 922d589b09
14 changed files with 311 additions and 139 deletions
+4 -1
View File
@@ -24,6 +24,7 @@ import { useWizard } from "./wizardContext";
import { START_ON_HOME } from "./navigationInit";
import { usePrefs } from "./prefsContext";
import Tabs from "./ui/Tabs";
import { useTabPanel } from "./ui/tabPanel";
import { useMe } from "../lib/useMe";
import { Pressable } from "./ui/Pressable";
import { Slider } from "./ui/NativeControls";
@@ -68,6 +69,7 @@ export default function SettingsPanel() {
// "Connect YouTube" onboarding lives in WizardProvider (the Account tab's reconnect button).
const { openWizard } = useWizard();
const [tabRaw, setTab] = useAccountPersistedState(LS.settingsTab, "appearance");
const panel = useTabPanel();
// Clamp at render so a stale/removed tab id falls back to Appearance.
const tab: TabId = TABS.some((tabItem) => tabItem.id === tabRaw)
? (tabRaw as TabId)
@@ -80,6 +82,7 @@ export default function SettingsPanel() {
{/* Tab rail: a scrolling row above the content on a phone, the side rail from `sm` up (see
Tabs). Active is a clear accent pill. */}
<Tabs
idBase={panel.idBase}
orientation="vertical"
className="flex gap-1 overflow-x-auto border-b border-border/60 p-2 sm:w-36 sm:shrink-0 sm:flex-col sm:border-b-0 sm:border-r"
active={tab}
@@ -95,7 +98,7 @@ export default function SettingsPanel() {
{/* Render only the active tab so the panel sizes to its actual content. (Stacking
every tab in one grid cell made the whole panel as tall as the tallest tab —
Account — leaving the short tabs with dead space and a needless scrollbar.) */}
<div className="flex-1 min-w-0 p-4">
<div {...panel.panelProps(tab)} className="flex-1 min-w-0 p-4">
{tab === "appearance" && <Appearance prefs={prefs} />}
{tab === "notifications" && <Notifications prefs={prefs} />}
{tab === "account" && <Account me={me} onOpenWizard={openWizard} />}