From 5ad02283abf3253c914dcc81bee82cb268a6af1a Mon Sep 17 00:00:00 2001 From: Matias Palma Date: Tue, 21 Apr 2026 00:54:08 -0400 Subject: [PATCH] =?UTF-8?q?improvements:=20a11y=20=E2=80=94=20focus-visibl?= =?UTF-8?q?e=20ring,=20aria-label=20on=20icon=20buttons,=20tablist=20for?= =?UTF-8?q?=20ActivityBar=20and=20TabBar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The app's shell had three overlapping a11y gaps in keyboard and AT land: 1. Icon-only buttons (window controls, bottom-panel close, ActivityBar entries) had no accessible name — AT announced them as "button" with no further context. 2. There was no visible focus indicator. Every button used `transition-colors` on hover but nothing on keyboard focus, so keyboard users could not tell where they were. 3. ActivityBar and TabBar looked like tablists but lacked roles, so AT had no grouping and no selection state. Changes: - **ActivityBar**: the container is `role=tablist aria-orientation=vertical` with an `aria-label`; each entry (plugins, Extensions, Settings) is `role=tab` with `aria-label` and `aria-selected`. Decorative indicator pip and hover tooltips are `aria-hidden`. - **TabBar**: `role=tablist` container, each open file is a `role=tab` with `aria-selected`, `aria-label` (including an "unsaved" suffix for modified files), `tabIndex=0`, and Enter/Space activation. The per-tab close button now has `aria-label="Close {file}"`. - **TitleBar**: `aria-label` on the right-panel toggle (with `aria-pressed`), minimize, maximize/restore, and close buttons. Decorative separator is `aria-hidden`. - **BottomPanel**: `aria-label` on the close button. - Global focus style: all five touched buttons/tabs now carry `focus-visible:ring-2 focus-visible:ring-white/40` (inset on titlebar controls and tab-bar items to stay inside their rail). - New i18n keys: `activitybar.label`, `tabbar.label`, `tab.close_aria` (EN + ES). Closes #104 --- apps/desktop/src/api/builtin.l10n.ts | 8 +++++ apps/desktop/src/components/ActivityBar.tsx | 32 ++++++++++++++----- apps/desktop/src/components/BottomPanel.tsx | 3 +- apps/desktop/src/components/TabBar.tsx | 35 +++++++++++++++------ apps/desktop/src/components/TitleBar.tsx | 15 ++++++--- 5 files changed, 69 insertions(+), 24 deletions(-) diff --git a/apps/desktop/src/api/builtin.l10n.ts b/apps/desktop/src/api/builtin.l10n.ts index faa2fb90..f2b42618 100644 --- a/apps/desktop/src/api/builtin.l10n.ts +++ b/apps/desktop/src/api/builtin.l10n.ts @@ -265,6 +265,10 @@ export function registerBuiltinTranslations() { 'ai.disclaimer': 'Trixty AI can make mistakes. Check important info.', 'ai.chat_log_label': 'AI chat transcript', + 'activitybar.label': 'Primary sidebar navigation', + 'tabbar.label': 'Open editor tabs', + 'tab.close_aria': 'Close {file}', + 'terminal.error_connect': 'Error connecting to terminal: ', 'editor.empty_desc': 'Select a file from the explorer to begin', 'editor.view_not_found': 'View not found', @@ -648,6 +652,10 @@ export function registerBuiltinTranslations() { 'ai.disclaimer': 'Trixty AI puede cometer errores. Verifica info importante.', 'ai.chat_log_label': 'Transcripción del chat de IA', + 'activitybar.label': 'Navegación principal del panel lateral', + 'tabbar.label': 'Pestañas de editores abiertos', + 'tab.close_aria': 'Cerrar {file}', + 'terminal.error_connect': 'Error al conectar con la terminal: ', 'editor.empty_desc': 'Selecciona un archivo del explorador para comenzar', 'editor.view_not_found': 'Vista no encontrada', diff --git a/apps/desktop/src/components/ActivityBar.tsx b/apps/desktop/src/components/ActivityBar.tsx index 52c4cf92..85145202 100644 --- a/apps/desktop/src/components/ActivityBar.tsx +++ b/apps/desktop/src/components/ActivityBar.tsx @@ -43,7 +43,12 @@ const ActivityBar: React.FC = () => { }; return ( -
+
{plugins.map((item) => { @@ -51,16 +56,19 @@ const ActivityBar: React.FC = () => { return ( @@ -68,22 +76,30 @@ const ActivityBar: React.FC = () => { })}
diff --git a/apps/desktop/src/components/BottomPanel.tsx b/apps/desktop/src/components/BottomPanel.tsx index 5856a6d8..a444f5d9 100644 --- a/apps/desktop/src/components/BottomPanel.tsx +++ b/apps/desktop/src/components/BottomPanel.tsx @@ -23,7 +23,8 @@ const BottomPanel: React.FC = () => {
diff --git a/apps/desktop/src/components/TabBar.tsx b/apps/desktop/src/components/TabBar.tsx index 0506ffc4..7530f878 100644 --- a/apps/desktop/src/components/TabBar.tsx +++ b/apps/desktop/src/components/TabBar.tsx @@ -72,36 +72,51 @@ const TabBar: React.FC = () => { if (openFiles.length === 0) return null; return ( -
+
{openFiles.map((file) => { const isActive = currentFile?.path === file.path; return (
setCurrentFile(file)} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + setCurrentFile(file); + } + }} onContextMenu={(e) => handleContextMenu(e, file.path)} - className={`relative flex items-center gap-2 px-3 min-w-[100px] max-w-[180px] h-full cursor-pointer transition-all border-r border-[#1a1a1a] group ${ + className={`relative flex items-center gap-2 px-3 min-w-[100px] max-w-[180px] h-full cursor-pointer transition-all border-r border-[#1a1a1a] group focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-white/40 ${ isActive - ? "bg-[#141414] text-white" + ? "bg-[#141414] text-white" : "text-[#666] hover:text-[#999] hover:bg-[#111]" }`} > - {isActive &&
} - -
{getFileIcon(file)}
- + {isActive &&