Skip to content

Commit a8840bb

Browse files
committed
Adds accessibility labels and states
This adds ARIA labels, states and properties to the Claude Code Session and related flows. The goal is to make sure that screen reader users have a decent experience when interacting with Claude Code inside a session. More work is needed around focus management and element order, but this will hopefully will be added in subsequent commits.
1 parent 8de6700 commit a8840bb

17 files changed

+181
-51
lines changed

bun.lockb

184 KB
Binary file not shown.

src/components/Agents.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,8 @@ export const Agents: React.FC = () => {
346346
</div>
347347
<DropdownMenu>
348348
<DropdownMenuTrigger asChild>
349-
<Button variant="ghost" size="icon" className="h-8 w-8">
350-
<ChevronDown className="w-4 h-4" />
349+
<Button variant="ghost" size="icon" className="h-8 w-8" aria-label="Agent options menu">
350+
<ChevronDown className="w-4 h-4" aria-hidden="true" />
351351
</Button>
352352
</DropdownMenuTrigger>
353353
<DropdownMenuContent align="end">

src/components/CCAgents.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
469469
variant="outline"
470470
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
471471
disabled={currentPage === 1}
472+
aria-label={`Go to previous page (page ${currentPage - 1} of ${totalPages})`}
472473
>
473474
Previous
474475
</Button>
@@ -480,6 +481,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
480481
variant="outline"
481482
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
482483
disabled={currentPage === totalPages}
484+
aria-label={`Go to next page (page ${currentPage + 1} of ${totalPages})`}
483485
>
484486
Next
485487
</Button>

src/components/ClaudeCodeSession.tsx

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,32 +1297,42 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
12971297
animate={{ opacity: 1, y: 0 }}
12981298
exit={{ opacity: 0, y: 20 }}
12991299
className="fixed bottom-24 left-1/2 -translate-x-1/2 z-30 w-full max-w-3xl px-4"
1300+
role="region"
1301+
aria-label="Prompt queue"
1302+
aria-live="polite"
13001303
>
13011304
<div className="bg-background/95 backdrop-blur-md border rounded-lg shadow-lg p-3 space-y-2">
13021305
<div className="flex items-center justify-between">
1303-
<div className="text-xs font-medium text-muted-foreground mb-1">
1306+
<div id="queue-header" className="text-xs font-medium text-muted-foreground mb-1">
13041307
Queued Prompts ({queuedPrompts.length})
13051308
</div>
13061309
<TooltipSimple content={queuedPromptsCollapsed ? "Expand queue" : "Collapse queue"} side="top">
13071310
<motion.div
13081311
whileTap={{ scale: 0.97 }}
13091312
transition={{ duration: 0.15 }}
13101313
>
1311-
<Button variant="ghost" size="icon" onClick={() => setQueuedPromptsCollapsed(prev => !prev)}>
1312-
{queuedPromptsCollapsed ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
1314+
<Button
1315+
variant="ghost"
1316+
size="icon"
1317+
onClick={() => setQueuedPromptsCollapsed(prev => !prev)}
1318+
aria-label={queuedPromptsCollapsed ? "Expand queued prompts" : "Collapse queued prompts"}
1319+
>
1320+
{queuedPromptsCollapsed ? <ChevronUp className="h-3 w-3" aria-hidden="true" /> : <ChevronDown className="h-3 w-3" aria-hidden="true" />}
13131321
</Button>
13141322
</motion.div>
13151323
</TooltipSimple>
13161324
</div>
1317-
{!queuedPromptsCollapsed && queuedPrompts.map((queuedPrompt, index) => (
1318-
<motion.div
1319-
key={queuedPrompt.id}
1320-
initial={{ opacity: 0, y: 4 }}
1321-
animate={{ opacity: 1, y: 0 }}
1322-
exit={{ opacity: 0, y: -4 }}
1323-
transition={{ duration: 0.15, delay: index * 0.02 }}
1324-
className="flex items-start gap-2 bg-muted/50 rounded-md p-2"
1325-
>
1325+
{!queuedPromptsCollapsed && (
1326+
<ul role="list" aria-labelledby="queue-header" className="space-y-2">
1327+
{queuedPrompts.map((queuedPrompt, index) => (
1328+
<motion.li
1329+
key={queuedPrompt.id}
1330+
initial={{ opacity: 0, y: 4 }}
1331+
animate={{ opacity: 1, y: 0 }}
1332+
exit={{ opacity: 0, y: -4 }}
1333+
transition={{ duration: 0.15, delay: index * 0.02 }}
1334+
className="flex items-start gap-2 bg-muted/50 rounded-md p-2"
1335+
>
13261336
<div className="flex-1 min-w-0">
13271337
<div className="flex items-center gap-2 mb-1">
13281338
<span className="text-xs font-medium text-muted-foreground">#{index + 1}</span>
@@ -1341,12 +1351,15 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
13411351
size="icon"
13421352
className="h-6 w-6 flex-shrink-0"
13431353
onClick={() => setQueuedPrompts(prev => prev.filter(p => p.id !== queuedPrompt.id))}
1354+
aria-label={`Remove queued prompt: ${queuedPrompt.prompt.slice(0, 50)}${queuedPrompt.prompt.length > 50 ? '...' : ''}`}
13441355
>
13451356
<X className="h-3 w-3" />
13461357
</Button>
13471358
</motion.div>
1348-
</motion.div>
1349-
))}
1359+
</motion.li>
1360+
))}
1361+
</ul>
1362+
)}
13501363
</div>
13511364
</motion.div>
13521365
)}
@@ -1395,7 +1408,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
13951408
}}
13961409
className="px-3 py-2 hover:bg-accent rounded-none"
13971410
>
1398-
<ChevronUp className="h-4 w-4" />
1411+
<ChevronUp className="h-4 w-4"
1412+
aria-label="Scroll to top" />
13991413
</Button>
14001414
</motion.div>
14011415
</TooltipSimple>
@@ -1423,7 +1437,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
14231437
}}
14241438
className="px-3 py-2 hover:bg-accent rounded-none"
14251439
>
1426-
<ChevronDown className="h-4 w-4" />
1440+
<ChevronDown className="h-4 w-4"
1441+
aria-label="Scroll to bottom" />
14271442
</Button>
14281443
</motion.div>
14291444
</TooltipSimple>
@@ -1455,6 +1470,9 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
14551470
size="icon"
14561471
onClick={() => setShowTimeline(!showTimeline)}
14571472
className="h-9 w-9 text-muted-foreground hover:text-foreground"
1473+
aria-label="Session timeline"
1474+
aria-haspopup="dialog"
1475+
aria-expanded={showTimeline}
14581476
>
14591477
<GitBranch className={cn("h-3.5 w-3.5", showTimeline && "text-primary")} />
14601478
</Button>
@@ -1473,6 +1491,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
14731491
variant="ghost"
14741492
size="icon"
14751493
className="h-9 w-9 text-muted-foreground hover:text-foreground"
1494+
aria-label="Copy Conversation"
1495+
aria-haspopup="menu"
14761496
>
14771497
<Copy className="h-3.5 w-3.5" />
14781498
</Button>
@@ -1515,6 +1535,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
15151535
size="icon"
15161536
onClick={() => setShowSettings(!showSettings)}
15171537
className="h-8 w-8 text-muted-foreground hover:text-foreground"
1538+
aria-label="Checkpoint Settings"
1539+
aria-haspopup="dialog"
15181540
>
15191541
<Wrench className={cn("h-3.5 w-3.5", showSettings && "text-primary")} />
15201542
</Button>

src/components/CustomTitlebar.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
9191
}}
9292
className="group relative w-3 h-3 rounded-full bg-red-500 hover:bg-red-600 transition-all duration-200 flex items-center justify-center tauri-no-drag"
9393
title="Close"
94+
aria-label="Close window"
9495
>
9596
{isHovered && (
9697
<X size={8} className="text-red-900 opacity-60 group-hover:opacity-100" />
@@ -105,6 +106,7 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
105106
}}
106107
className="group relative w-3 h-3 rounded-full bg-yellow-500 hover:bg-yellow-600 transition-all duration-200 flex items-center justify-center tauri-no-drag"
107108
title="Minimize"
109+
aria-label="Minimize window"
108110
>
109111
{isHovered && (
110112
<Minus size={8} className="text-yellow-900 opacity-60 group-hover:opacity-100" />
@@ -119,6 +121,7 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
119121
}}
120122
className="group relative w-3 h-3 rounded-full bg-green-500 hover:bg-green-600 transition-all duration-200 flex items-center justify-center tauri-no-drag"
121123
title="Maximize"
124+
aria-label="Maximize window"
122125
>
123126
{isHovered && (
124127
<Square size={6} className="text-green-900 opacity-60 group-hover:opacity-100" />
@@ -146,6 +149,7 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
146149
whileTap={{ scale: 0.97 }}
147150
transition={{ duration: 0.15 }}
148151
className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors tauri-no-drag"
152+
aria-label="Agents manager"
149153
>
150154
<Bot size={16} />
151155
</motion.button>
@@ -159,6 +163,7 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
159163
whileTap={{ scale: 0.97 }}
160164
transition={{ duration: 0.15 }}
161165
className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors tauri-no-drag"
166+
aria-label="Usage dashboard"
162167
>
163168
<BarChart3 size={16} />
164169
</motion.button>
@@ -178,6 +183,7 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
178183
whileTap={{ scale: 0.97 }}
179184
transition={{ duration: 0.15 }}
180185
className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors tauri-no-drag"
186+
aria-label="Settings"
181187
>
182188
<Settings size={16} />
183189
</motion.button>
@@ -192,13 +198,19 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
192198
whileTap={{ scale: 0.97 }}
193199
transition={{ duration: 0.15 }}
194200
className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-1"
201+
aria-label="More options"
202+
aria-expanded={isDropdownOpen}
195203
>
196204
<MoreVertical size={16} />
197205
</motion.button>
198206
</TooltipSimple>
199207

200208
{isDropdownOpen && (
201-
<div className="absolute right-0 mt-2 w-48 bg-popover border border-border rounded-lg shadow-lg z-[250]">
209+
<div
210+
className="absolute right-0 mt-2 w-48 bg-popover border border-border rounded-lg shadow-lg z-[250]"
211+
role="menu"
212+
aria-label="Additional options menu"
213+
>
202214
<div className="py-1">
203215
{onClaudeClick && (
204216
<button
@@ -207,6 +219,8 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
207219
setIsDropdownOpen(false);
208220
}}
209221
className="w-full px-4 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-3"
222+
role="menuitem"
223+
aria-label="CLAUDE.md file editor"
210224
>
211225
<FileText size={14} />
212226
<span>CLAUDE.md</span>
@@ -220,6 +234,8 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
220234
setIsDropdownOpen(false);
221235
}}
222236
className="w-full px-4 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-3"
237+
role="menuitem"
238+
aria-label="MCP servers manager"
223239
>
224240
<Network size={14} />
225241
<span>MCP Servers</span>
@@ -233,6 +249,8 @@ export const CustomTitlebar: React.FC<CustomTitlebarProps> = ({
233249
setIsDropdownOpen(false);
234250
}}
235251
className="w-full px-4 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-3"
252+
role="menuitem"
253+
aria-label="Show about information"
236254
>
237255
<Info size={14} />
238256
<span>About</span>

src/components/FilePicker.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,14 @@ export const FilePicker: React.FC<FilePickerProps> = ({
449449
isSelected && "bg-accent"
450450
)}
451451
title={entry.is_directory ? "Click to select • Double-click to enter" : "Click to select"}
452+
aria-label={entry.is_directory
453+
? `${entry.name} directory. Click to select or double-click to enter.`
454+
: `${entry.name} file${entry.size > 0 ? `, ${formatFileSize(entry.size)}` : ''}. Click to select.`}
452455
>
453456
<Icon className={cn(
454457
"h-4 w-4 flex-shrink-0",
455458
entry.is_directory ? "text-blue-500" : "text-muted-foreground"
456-
)} />
459+
)} aria-hidden="true" />
457460

458461
<span className="flex-1 truncate">
459462
{entry.name}

0 commit comments

Comments
 (0)