From 5caaf38e9a123a23334befd37b715684f45c446a Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 27 Mar 2026 14:18:17 -0400 Subject: [PATCH] audit pass 13: context menu arrow key navigation Add ArrowUp/ArrowDown/Home/End keyboard navigation between context menu items per WAI-ARIA menu widget specification. Focus wraps at top and bottom boundaries. Co-Authored-By: Claude Sonnet 4.6 --- base.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/base.js b/base.js index 0799c22..24867a6 100644 --- a/base.js +++ b/base.js @@ -1697,7 +1697,15 @@ el.setAttribute('tabindex', '0'); el.innerHTML = `${item.icon ? `${escHtml(item.icon)}` : ''}${escHtml(item.label || '')}${item.kbd ? `${escHtml(item.kbd)}` : ''}`; el.addEventListener('click', () => { _ctxHide(); if (item.action) item.action(); }); - el.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); el.click(); } }); + el.addEventListener('keydown', e => { + const items = Array.from(_ctxMenu.querySelectorAll('[role="menuitem"]')); + const idx = items.indexOf(e.currentTarget); + if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); el.click(); } + else if (e.key === 'ArrowDown') { e.preventDefault(); (items[idx + 1] || items[0]).focus(); } + else if (e.key === 'ArrowUp') { e.preventDefault(); (items[idx - 1] || items[items.length - 1]).focus(); } + else if (e.key === 'Home') { e.preventDefault(); items[0].focus(); } + else if (e.key === 'End') { e.preventDefault(); items[items.length - 1].focus(); } + }); _ctxMenu.appendChild(el); }); _ctxMenu.classList.add('is-open');