From 8d95a4a6a5fe1f6ce0626c58a799285c9d5d88cb Mon Sep 17 00:00:00 2001 From: xRangerDE Date: Mon, 11 May 2026 09:07:35 +0200 Subject: [PATCH] a11y: role=menu / menuitem / separator on the two right-click context menus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both context menus (queue row + VOD card) were built as plain
trees with no ARIA roles — screen readers couldn't tell they were menus, and the individual rows weren't exposed as menu items. Users on assistive tech got a generic "group of nested divs" with no menu semantics, despite the menus being visually and functionally menus. Added the WAI-ARIA menu pattern roles: - role="menu" on the container - role="menuitem" on each clickable row - aria-disabled="true" on disabled menu items - role="separator" on the horizontal divider lines Both renderer-queue.ts (queue right-click context menu) and renderer-streamers.ts (VOD card right-click context menu) get the same treatment so the two share both visual style (.context-menu — 4.6.120) and accessibility semantics. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/renderer-queue.ts | 4 ++++ src/renderer-streamers.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/renderer-queue.ts b/src/renderer-queue.ts index 7bc23b7..c167763 100644 --- a/src/renderer-queue.ts +++ b/src/renderer-queue.ts @@ -186,11 +186,14 @@ function showQueueContextMenu(x: number, y: number, item: QueueItem): void { const menu = document.createElement('div'); menu.className = 'context-menu'; + menu.setAttribute('role', 'menu'); const makeItem = (label: string, onClick: () => void, disabled = false): HTMLElement => { const el = document.createElement('div'); el.textContent = label; el.className = 'context-menu-item' + (disabled ? ' disabled' : ''); + el.setAttribute('role', 'menuitem'); + if (disabled) el.setAttribute('aria-disabled', 'true'); if (!disabled) { el.addEventListener('click', () => { try { onClick(); } finally { closeQueueContextMenu(); } @@ -202,6 +205,7 @@ function showQueueContextMenu(x: number, y: number, item: QueueItem): void { const makeSeparator = (): HTMLElement => { const sep = document.createElement('div'); sep.className = 'context-menu-separator'; + sep.setAttribute('role', 'separator'); return sep; }; diff --git a/src/renderer-streamers.ts b/src/renderer-streamers.ts index 1bcaa18..dcb0e99 100644 --- a/src/renderer-streamers.ts +++ b/src/renderer-streamers.ts @@ -925,6 +925,7 @@ function showVodContextMenu(x: number, y: number, ctx: VodCardContext): void { const menu = document.createElement('div'); menu.className = 'context-menu'; + menu.setAttribute('role', 'menu'); const downloadedIds = new Set( Array.isArray(config.downloaded_vod_ids) @@ -937,6 +938,7 @@ function showVodContextMenu(x: number, y: number, ctx: VodCardContext): void { const el = document.createElement('div'); el.textContent = label; el.className = 'context-menu-item'; + el.setAttribute('role', 'menuitem'); el.addEventListener('click', () => { try { onClick(); } finally { closeVodContextMenu(); } });