fix(a11y): thread chip says it opens the thread, and whether it's unread/muted (#179)
CI / Build & Quality Checks (push) Successful in 2m15s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 14s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
CI / Build & Quality Checks (push) Successful in 2m15s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 14s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
The "N replies · <time>" chip under a thread root was named only by its visible text, so a screen reader never said it opens the thread, and the unread dot / muted bell were visual-only. Its accessible name is now the visible text first (WCAG 2.5.3) plus "view thread", "unread replies" and "muted" as applicable, e.g. "1 reply · 12:16 PM, view thread". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d5aa18e3ab
commit
206a3e933a
@@ -19,6 +19,18 @@ export function ThreadSummary({ rootEvent, room, onOpen }: ThreadSummaryProps) {
|
||||
|
||||
const { count, latestTs } = summary;
|
||||
const latestStr = latestTs !== undefined ? format(latestTs) : undefined;
|
||||
const visibleText = `${count === 1 ? '1 reply' : `${count} replies`}${latestStr ? ` · ${latestStr}` : ''}`;
|
||||
// #179: the chip was named only by its visible text, so a screen reader never
|
||||
// heard that it opens the thread, nor about the unread dot / muted bell. Keep
|
||||
// the visible text first (WCAG 2.5.3, label in name) and add the rest.
|
||||
const ariaLabel = [
|
||||
visibleText,
|
||||
'view thread',
|
||||
unread > 0 ? 'unread replies' : undefined,
|
||||
mode === ThreadNotificationMode.Mute ? 'muted' : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
|
||||
return (
|
||||
<Box style={{ marginTop: config.space.S200 }}>
|
||||
@@ -26,6 +38,7 @@ export function ThreadSummary({ rootEvent, room, onOpen }: ThreadSummaryProps) {
|
||||
variant="SurfaceVariant"
|
||||
radii="300"
|
||||
className={MobileTouchTarget}
|
||||
aria-label={ariaLabel}
|
||||
before={<Icon size="50" src={Icons.Thread} />}
|
||||
after={
|
||||
unread > 0 ? <Badge variant="Success" fill="Solid" radii="Pill" size="200" /> : undefined
|
||||
@@ -35,10 +48,7 @@ export function ThreadSummary({ rootEvent, room, onOpen }: ThreadSummaryProps) {
|
||||
if (threadId) onOpen(threadId);
|
||||
}}
|
||||
>
|
||||
<Text size="T200">
|
||||
{count === 1 ? '1 reply' : `${count} replies`}
|
||||
{latestStr ? ` · ${latestStr}` : ''}
|
||||
</Text>
|
||||
<Text size="T200">{visibleText}</Text>
|
||||
{mode === ThreadNotificationMode.Mute && <Icon size="50" src={Icons.BellMute} />}
|
||||
</Chip>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user