fix(person-mention): hover card mounts on focusin for keyboard users (WCAG 2.1.1)
Leonie FINDING-01 (Critical) + Elicit E3: only mouseenter triggered the hover card, so a keyboard user tabbing through transcribed text reached the anchor but never saw the rich-context preview. For the senior audience constraint that's a hard regression. Wire focusin/focusout alongside mouseenter/mouseleave on the delegated listener. Same handleMentionEnter/Leave run — getBoundingClientRect works identically on focused elements. focusin/focusout bubble naturally so no capture phase needed. Two new tests assert focusin mounts the card and focusout unmounts it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -169,6 +169,10 @@ async function handleMentionClick(event: MouseEvent) {
|
||||
// Attach delegated event listeners on each rendered block. Using {@html ...}
|
||||
// for the body means we cannot bind events declaratively to the injected
|
||||
// anchors, so we hook up listeners via a Svelte action when the wrapper mounts.
|
||||
//
|
||||
// Keyboard parity (Leonie FINDING-01, WCAG 2.1.1): focusin/focusout mirror
|
||||
// mouseenter/mouseleave so users tabbing through transcribed text get the
|
||||
// same preview affordance.
|
||||
function attachMentionHandlers(node: HTMLElement) {
|
||||
function onEnter(e: Event) {
|
||||
const t = e.target as HTMLElement;
|
||||
@@ -185,12 +189,17 @@ function attachMentionHandlers(node: HTMLElement) {
|
||||
// mouseenter does not bubble — capture it.
|
||||
node.addEventListener('mouseenter', onEnter, true);
|
||||
node.addEventListener('mouseleave', onLeave, true);
|
||||
// focusin/focusout do bubble — no capture phase needed.
|
||||
node.addEventListener('focusin', onEnter);
|
||||
node.addEventListener('focusout', onLeave);
|
||||
node.addEventListener('click', onClick);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('mouseenter', onEnter, true);
|
||||
node.removeEventListener('mouseleave', onLeave, true);
|
||||
node.removeEventListener('focusin', onEnter);
|
||||
node.removeEventListener('focusout', onLeave);
|
||||
node.removeEventListener('click', onClick);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user