From cb3d2c40e52606631320c83d82a1baa8df2e7ad3 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Thu, 18 Jun 2026 18:50:19 -0400 Subject: [PATCH] fix(a11y): descriptive aria-label on reaction buttons (P3-4) Reaction.tsx now computes aria-label='{shortcode} reaction, N people' using getShortcodeFor so screen readers announce emoji name and count instead of an ambiguous button. Custom (mxc://) emoji falls back to 'custom emoji reaction'. Co-Authored-By: Claude Sonnet 4.6 --- LOTUS_BUGS.md | 2 +- src/app/components/message/Reaction.tsx | 64 ++++++++++++++----------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/LOTUS_BUGS.md b/LOTUS_BUGS.md index 7e50cda46..4df384021 100644 --- a/LOTUS_BUGS.md +++ b/LOTUS_BUGS.md @@ -184,7 +184,7 @@ This document tracks identified bugs, edge cases, and architectural discrepancie | Performance | Numerous event handlers (e.g., handleUserClick, handleReplyClick) lack `useCallback`, leading to unnecessary re-renders of message components. | `cinny/src/app/features/room/RoomTimeline.tsx` | OPEN | | Performance | The `submit` function and file handling callbacks (e.g., handleSendUpload) are re-created on every render, causing re-renders of the editor and toolbar components. | `cinny/src/app/features/room/RoomInput.tsx` | OPEN | | Accessibility | `button` for edit history lacks `aria-label` | `cinny/src/app/components/message/content/FallbackContent.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="View edit history"` | -| Accessibility | `button` for reaction lacks `aria-label` | `cinny/src/app/components/message/Reaction.tsx` | OPEN — emoji content is already screen-reader-accessible via alt text; parent caller would need to set aria-label per reaction | +| Accessibility | `button` for reaction lacks `aria-label` | `cinny/src/app/components/message/Reaction.tsx` | **FIXED ⚠️ UNTESTED** — `Reaction` component now computes `aria-label="{shortcode} reaction, N people"` internally using `getShortcodeFor`; custom (mxc://) emoji falls back to "custom emoji reaction". | | Accessibility | `button` for ThreadIndicator lacks `aria-label` | `cinny/src/app/components/message/Reply.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="View thread"` | | Accessibility | `button` for ReplyLayout lacks `aria-label` | `cinny/src/app/components/message/Reply.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="Jump to original message"` | diff --git a/src/app/components/message/Reaction.tsx b/src/app/components/message/Reaction.tsx index 8562c7b9a..b9e69788f 100644 --- a/src/app/components/message/Reaction.tsx +++ b/src/app/components/message/Reaction.tsx @@ -15,34 +15,42 @@ export const Reaction = as< reaction: string; useAuthentication?: boolean; } ->(({ className, mx, count, reaction, useAuthentication, ...props }, ref) => ( - - - {reaction.startsWith('mxc://') ? ( - {reaction} - ) : ( - - {reaction} - - )} - - - {count} - - -)); +>(({ className, mx, count, reaction, useAuthentication, ...props }, ref) => { + const shortcode = reaction.startsWith('mxc://') + ? 'custom emoji' + : (getShortcodeFor(getHexcodeForEmoji(reaction)) ?? reaction); + const label = `${shortcode} reaction, ${count} ${count === 1 ? 'person' : 'people'}`; + + return ( + + + {reaction.startsWith('mxc://') ? ( + {reaction} + ) : ( + + {reaction} + + )} + + + {count} + + + ); +}); type ReactionTooltipMsgProps = { room: Room;