From 91a3fcf2378104aa37e60b1a8634515be07f9342 Mon Sep 17 00:00:00 2001 From: Louis King Date: Thu, 2 Jul 2026 21:32:25 +0100 Subject: [PATCH] feat(web): make custom clickable elements keyboard accessible The channel cards, member node badges, and member URL text are clickable divs/spans, so keyboard users could not reach or activate them. Add role/tabindex, Enter/Space key handling, and a focus-visible outline (invisible to mouse users). daisyUI btn/link/ input components already ship focus-visible outlines, so only these custom elements needed it. Co-Authored-By: Claude Fable 5 --- src/meshcore_hub/web/static/js/spa/pages/channels.js | 4 +++- src/meshcore_hub/web/static/js/spa/pages/members.js | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/meshcore_hub/web/static/js/spa/pages/channels.js b/src/meshcore_hub/web/static/js/spa/pages/channels.js index e9b93ea..f51f6f0 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/channels.js +++ b/src/meshcore_hub/web/static/js/spa/pages/channels.js @@ -37,7 +37,9 @@ function renderChannelCard(channel, { oidcEnabled, isAdmin, onDelete, onEdit, on ? html`
` : nothing; - return html`
onNavigate(channelIdx)}> + return html`
onNavigate(channelIdx)} + @keydown=${(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onNavigate(channelIdx); } }}>

diff --git a/src/meshcore_hub/web/static/js/spa/pages/members.js b/src/meshcore_hub/web/static/js/spa/pages/members.js index 999790a..9ca8878 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/members.js +++ b/src/meshcore_hub/web/static/js/spa/pages/members.js @@ -25,7 +25,9 @@ function renderProfileTile(profile, router) { e.stopPropagation(); router.navigate('/nodes/' + node.public_key); }; - return html`${label}`; + return html` { if (e.key === 'Enter' || e.key === ' ') handleClick(e); }}>${label}`; })}

` : nothing; @@ -33,8 +35,11 @@ function renderProfileTile(profile, router) { ? html`

${profile.description}

` : nothing; + const openUrl = (e) => { e.preventDefault(); e.stopPropagation(); window.open(profile.url, '_blank', 'noopener,noreferrer'); }; const urlLink = profile.url - ? html` { e.preventDefault(); e.stopPropagation(); window.open(profile.url, '_blank', 'noopener,noreferrer'); }}>${profile.url}` + ? html` { if (e.key === 'Enter') openUrl(e); }}>${profile.url}` : nothing; return html`