Skip to content

Commit 156013b

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 246e0c8 commit 156013b

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
@@ -354,8 +354,8 @@ export const Agents: React.FC = () => {
354354
</div>
355355
<DropdownMenu>
356356
<DropdownMenuTrigger asChild>
357-
<Button variant="ghost" size="icon" className="h-8 w-8">
358-
<ChevronDown className="w-4 h-4" />
357+
<Button variant="ghost" size="icon" className="h-8 w-8" aria-label="Agent options menu">
358+
<ChevronDown className="w-4 h-4" aria-hidden="true" />
359359
</Button>
360360
</DropdownMenuTrigger>
361361
<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
@@ -1333,32 +1333,42 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
13331333
animate={{ opacity: 1, y: 0 }}
13341334
exit={{ opacity: 0, y: 20 }}
13351335
className="fixed bottom-24 left-1/2 -translate-x-1/2 z-30 w-full max-w-3xl px-4"
1336+
role="region"
1337+
aria-label="Prompt queue"
1338+
aria-live="polite"
13361339
>
13371340
<div className="bg-background/95 backdrop-blur-md border rounded-lg shadow-lg p-3 space-y-2">
13381341
<div className="flex items-center justify-between">
1339-
<div className="text-xs font-medium text-muted-foreground mb-1">
1342+
<div id="queue-header" className="text-xs font-medium text-muted-foreground mb-1">
13401343
Queued Prompts ({queuedPrompts.length})
13411344
</div>
13421345
<TooltipSimple content={queuedPromptsCollapsed ? "Expand queue" : "Collapse queue"} side="top">
13431346
<motion.div
13441347
whileTap={{ scale: 0.97 }}
13451348
transition={{ duration: 0.15 }}
13461349
>
1347-
<Button variant="ghost" size="icon" onClick={() => setQueuedPromptsCollapsed(prev => !prev)}>
1348-
{queuedPromptsCollapsed ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
1350+
<Button
1351+
variant="ghost"
1352+
size="icon"
1353+
onClick={() => setQueuedPromptsCollapsed(prev => !prev)}
1354+
aria-label={queuedPromptsCollapsed ? "Expand queued prompts" : "Collapse queued prompts"}
1355+
>
1356+
{queuedPromptsCollapsed ? <ChevronUp className="h-3 w-3" aria-hidden="true" /> : <ChevronDown className="h-3 w-3" aria-hidden="true" />}
13491357
</Button>
13501358
</motion.div>
13511359
</TooltipSimple>
13521360
</div>
1353-
{!queuedPromptsCollapsed && queuedPrompts.map((queuedPrompt, index) => (
1354-
<motion.div
1355-
key={queuedPrompt.id}
1356-
initial={{ opacity: 0, y: 4 }}
1357-
animate={{ opacity: 1, y: 0 }}
1358-
exit={{ opacity: 0, y: -4 }}
1359-
transition={{ duration: 0.15, delay: index * 0.02 }}
1360-
className="flex items-start gap-2 bg-muted/50 rounded-md p-2"
1361-
>
1361+
{!queuedPromptsCollapsed && (
1362+
<ul role="list" aria-labelledby="queue-header" className="space-y-2">
1363+
{queuedPrompts.map((queuedPrompt, index) => (
1364+
<motion.li
1365+
key={queuedPrompt.id}
1366+
initial={{ opacity: 0, y: 4 }}
1367+
animate={{ opacity: 1, y: 0 }}
1368+
exit={{ opacity: 0, y: -4 }}
1369+
transition={{ duration: 0.15, delay: index * 0.02 }}
1370+
className="flex items-start gap-2 bg-muted/50 rounded-md p-2"
1371+
>
13621372
<div className="flex-1 min-w-0">
13631373
<div className="flex items-center gap-2 mb-1">
13641374
<span className="text-xs font-medium text-muted-foreground">#{index + 1}</span>
@@ -1377,12 +1387,15 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
13771387
size="icon"
13781388
className="h-6 w-6 flex-shrink-0"
13791389
onClick={() => setQueuedPrompts(prev => prev.filter(p => p.id !== queuedPrompt.id))}
1390+
aria-label={`Remove queued prompt: ${queuedPrompt.prompt.slice(0, 50)}${queuedPrompt.prompt.length > 50 ? '...' : ''}`}
13801391
>
13811392
<X className="h-3 w-3" />
13821393
</Button>
13831394
</motion.div>
1384-
</motion.div>
1385-
))}
1395+
</motion.li>
1396+
))}
1397+
</ul>
1398+
)}
13861399
</div>
13871400
</motion.div>
13881401
)}
@@ -1431,7 +1444,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
14311444
}}
14321445
className="px-3 py-2 hover:bg-accent rounded-none"
14331446
>
1434-
<ChevronUp className="h-4 w-4" />
1447+
<ChevronUp className="h-4 w-4"
1448+
aria-label="Scroll to top" />
14351449
</Button>
14361450
</motion.div>
14371451
</TooltipSimple>
@@ -1464,7 +1478,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
14641478
}}
14651479
className="px-3 py-2 hover:bg-accent rounded-none"
14661480
>
1467-
<ChevronDown className="h-4 w-4" />
1481+
<ChevronDown className="h-4 w-4"
1482+
aria-label="Scroll to bottom" />
14681483
</Button>
14691484
</motion.div>
14701485
</TooltipSimple>
@@ -1496,6 +1511,9 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
14961511
size="icon"
14971512
onClick={() => setShowTimeline(!showTimeline)}
14981513
className="h-9 w-9 text-muted-foreground hover:text-foreground"
1514+
aria-label="Session timeline"
1515+
aria-haspopup="dialog"
1516+
aria-expanded={showTimeline}
14991517
>
15001518
<GitBranch className={cn("h-3.5 w-3.5", showTimeline && "text-primary")} />
15011519
</Button>
@@ -1514,6 +1532,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
15141532
variant="ghost"
15151533
size="icon"
15161534
className="h-9 w-9 text-muted-foreground hover:text-foreground"
1535+
aria-label="Copy Conversation"
1536+
aria-haspopup="menu"
15171537
>
15181538
<Copy className="h-3.5 w-3.5" />
15191539
</Button>
@@ -1556,6 +1576,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
15561576
size="icon"
15571577
onClick={() => setShowSettings(!showSettings)}
15581578
className="h-8 w-8 text-muted-foreground hover:text-foreground"
1579+
aria-label="Checkpoint Settings"
1580+
aria-haspopup="dialog"
15591581
>
15601582
<Wrench className={cn("h-3.5 w-3.5", showSettings && "text-primary")} />
15611583
</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)