a11y: address PR feedback and polish docs

- HistoryTable: skip row key handler when focus is on Actions button (Enter/Space)
- StoryList: expose selected story (aria-pressed, 'Selected' in label)
- ProfileCard: skip card key handler when focus is on Export/Edit/Delete
- VoicesTab: keep table semantics; edit button in first cell instead of role=button on row
- PR-ACCESSIBILITY.md: 'Fine-tune' wording, 'focus on the text area' phrasing

Made-with: Cursor
This commit is contained in:
Richard Orme
2026-03-07 12:36:02 -08:00
parent 19a28bf6c5
commit 9955e1dcb7
5 changed files with 25 additions and 28 deletions
+1 -1
View File
@@ -274,7 +274,7 @@ export function HistoryTable() {
}}
onKeyDown={(e) => {
const target = e.target as HTMLElement;
if (target.closest('textarea')) return;
if (target.closest('textarea') || target.closest('button')) return;
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handlePlay(gen.id, gen.text, gen.profile_id);
+6 -1
View File
@@ -200,7 +200,12 @@ export function StoryList() {
'h-24 p-4 border rounded-2xl transition-colors group flex items-center cursor-pointer',
selectedStoryId === story.id && 'bg-muted border-primary',
)}
aria-label={`Story ${story.name}, ${story.item_count} ${story.item_count === 1 ? 'item' : 'items'}, ${formatDate(story.updated_at)}. Press Enter to select.`}
aria-label={
selectedStoryId === story.id
? `Story ${story.name}, ${story.item_count} ${story.item_count === 1 ? 'item' : 'items'}, ${formatDate(story.updated_at)}. Selected. Press Enter to select.`
: `Story ${story.name}, ${story.item_count} ${story.item_count === 1 ? 'item' : 'items'}, ${formatDate(story.updated_at)}. Press Enter to select.`
}
aria-pressed={selectedStoryId === story.id}
onClick={() => setSelectedStoryId(story.id)}
onKeyDown={(e) => {
if (e.target !== e.currentTarget) return;
@@ -62,6 +62,8 @@ export function ProfileCard({ profile }: ProfileCardProps) {
};
const handleKeyDown = (e: React.KeyboardEvent) => {
const target = e.target as HTMLElement;
if (target.closest('button')) return;
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleSelect();
+14 -24
View File
@@ -183,38 +183,28 @@ function VoiceRow({
const rowLabel = `${profile.name}, ${profile.language}, ${generationCount} generations, ${sampleCount} samples. Press Enter to edit.`;
const handleKeyDown = (e: React.KeyboardEvent) => {
const target = e.target as HTMLElement;
if (target.closest('button') || target.closest('[role="combobox"]') || target.closest('[role="listbox"]')) {
return;
}
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onEdit();
}
};
return (
<TableRow
className="cursor-pointer"
onClick={onEdit}
tabIndex={0}
role="button"
aria-label={rowLabel}
onKeyDown={handleKeyDown}
>
<TableRow className="cursor-pointer" onClick={onEdit}>
<TableCell>
<div className="flex items-center gap-2">
<button
type="button"
className="flex w-full min-w-0 items-center gap-2 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded"
aria-label={rowLabel}
onClick={(e) => {
e.stopPropagation();
onEdit();
}}
>
<div className="h-8 w-8 rounded-lg bg-muted flex items-center justify-center shrink-0">
<Mic className="h-4 w-4 text-muted-foreground" />
</div>
<div>
<div className="font-medium">{profile.name}</div>
<div className="min-w-0">
<div className="font-medium truncate">{profile.name}</div>
{profile.description && (
<div className="text-sm text-muted-foreground">{profile.description}</div>
<div className="text-sm text-muted-foreground truncate">{profile.description}</div>
)}
</div>
</div>
</button>
</TableCell>
<TableCell onClick={(e) => e.stopPropagation()}>{profile.language}</TableCell>
<TableCell onClick={(e) => e.stopPropagation()}>{generationCount}</TableCell>
+2 -2
View File
@@ -18,7 +18,7 @@ Improvements to support screen reader and keyboard users across the main app sur
### Generation UI (text box and voice choice)
- **Generate speech** (submit) and **Fine tune instructions** (sliders) Icon buttons now have `aria-label` (and state for fine-tune, e.g. "Fine tune instructions, on").
- **Generate speech** (submit) and **Fine-tune instructions** (sliders) Icon buttons now have `aria-label` (and state for fine-tune, e.g. "Fine-tune instructions, on").
### Voice selection (cards on Generate screen)
@@ -28,7 +28,7 @@ Improvements to support screen reader and keyboard users across the main app sur
### History list (generated samples)
- Each **sample row** is focusable with `role="button"` and an `aria-label` (e.g. "Sample from [profile], [duration], [date]. Press Enter to play."); **Enter/Space** plays or restarts.
- **Transcript textarea** has `aria-label` (e.g. "Transcript for sample from [profile], [duration]") so focus in the text area is announced in context.
- **Transcript textarea** has `aria-label` (e.g. "Transcript for sample from [profile], [duration]") so when you focus on the text area, the sample is announced in context.
### Voices tab (table)