Skip to content

Commit 3f996ba

Browse files
ComBbaclaude
andcommitted
a11y: add skip-nav, ARIA landmarks, keyboard support, remove alert()
1. layout.tsx: add skip-to-main-content link for keyboard/screen reader users 2. zero-prompt-landing.tsx: add <label> for YouTube URL input; replace alert() Start button with disabled "Admin Only" state + tooltip 3. kanban-board.tsx: add role="region" + aria-label on board container 4. kanban-column.tsx: add role="group" + aria-label with column name and item count for screen readers 5. action-feed.tsx: add role="button", tabIndex, aria-pressed, and keyboard handlers (Enter/Space) to all filter badges 6. demo/page.tsx: add aria-hidden="true" to decorative cursor animation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3495679 commit 3f996ba

File tree

6 files changed

+26
-8
lines changed

6 files changed

+26
-8
lines changed

web/src/app/demo/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ export default function DemoPage() {
262262
</AnimatePresence>
263263

264264
<motion.div
265+
aria-hidden="true"
265266
className="pointer-events-none fixed left-0 top-0 z-[120]"
266267
initial={false}
267268
animate={{

web/src/app/layout.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ export default function RootLayout({
3939
<body
4040
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background text-foreground`}
4141
>
42-
<ErrorBoundary>{children}</ErrorBoundary>
42+
<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:rounded-md focus:bg-primary focus:px-4 focus:py-2 focus:text-primary-foreground">
43+
Skip to main content
44+
</a>
45+
<main id="main-content">
46+
<ErrorBoundary>{children}</ErrorBoundary>
47+
</main>
4348
</body>
4449
</html>
4550
);

web/src/components/zero-prompt-landing.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,19 +152,23 @@ export function ZeroPromptLanding({ youtubeUrl, onYoutubeUrlChange, startAction,
152152
<Card className="border-border/50 overflow-hidden">
153153
<CardContent className="pt-6 space-y-4">
154154
<div className="flex gap-2">
155+
<label htmlFor="youtube-url-input" className="sr-only">YouTube video URL</label>
155156
<Input
157+
id="youtube-url-input"
156158
ref={inputRef}
157159
value={youtubeUrl}
158160
onChange={(e) => onYoutubeUrlChange(e.target.value)}
159161
placeholder="https://www.youtube.com/watch?v=..."
160162
className="font-mono text-sm"
163+
aria-label="YouTube video URL"
161164
/>
162165
<Button
163-
className="shrink-0 gap-2 bg-gradient-to-r from-red-600 to-red-500 hover:from-red-500 hover:to-red-400"
164-
onClick={() => alert("This feature is restricted to admin users and authorized IPs only.")}
166+
disabled
167+
className="shrink-0 gap-2 opacity-50 cursor-not-allowed"
168+
title="Admin access required — use Zero-Prompt Start for the full pipeline"
165169
>
166170
<Lock className="w-4 h-4" />
167-
Start
171+
Admin Only
168172
</Button>
169173
</div>
170174

web/src/components/zero-prompt/action-feed.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,27 @@ export function ActionFeed({ actions }: ActionFeedProps) {
7373
/>
7474
</div>
7575
<div className="flex gap-1 overflow-x-auto pb-1 sm:pb-0 hide-scrollbar">
76-
<Badge
76+
<Badge
77+
role="button"
78+
tabIndex={0}
79+
aria-pressed={typeFilter === null}
7780
variant={typeFilter === null ? "default" : "outline"}
7881
className="cursor-pointer text-[10px] whitespace-nowrap"
7982
onClick={() => setTypeFilter(null)}
83+
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setTypeFilter(null); } }}
8084
>
8185
All
8286
</Badge>
8387
{uniqueTypes.map(type => (
84-
<Badge
88+
<Badge
8589
key={type}
90+
role="button"
91+
tabIndex={0}
92+
aria-pressed={typeFilter === type}
8693
variant={typeFilter === type ? "default" : "outline"}
8794
className="cursor-pointer text-[10px] whitespace-nowrap"
8895
onClick={() => setTypeFilter(type === typeFilter ? null : type)}
96+
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setTypeFilter(type === typeFilter ? null : type); } }}
8997
>
9098
{type}
9199
</Badge>

web/src/components/zero-prompt/kanban-board.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function KanbanBoard({ cards, deployedCards = [], sessionId, onQueueBuild
6969

7070
return (
7171
<>
72-
<div className="grid grid-cols-1 md:grid-cols-5 gap-4 mb-6 overflow-x-auto pb-4">
72+
<div role="region" aria-label="Idea pipeline Kanban board" className="grid grid-cols-1 md:grid-cols-5 gap-4 mb-6 overflow-x-auto pb-4">
7373
{COLUMNS.map((col) => (
7474
<KanbanColumn
7575
key={col.id}

web/src/components/zero-prompt/kanban-column.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function KanbanColumn({ title, statuses, cards, maxItems, onDeleteRejecte
2727
const isWindowed = typeof maxItems === "number" && columnCards.length > maxItems;
2828

2929
return (
30-
<div className="flex flex-col min-w-[280px] bg-muted/30 rounded-xl p-3 border border-border/50">
30+
<div role="group" aria-label={`${title} column, ${columnCards.length} items`} className="flex flex-col min-w-[280px] bg-muted/30 rounded-xl p-3 border border-border/50">
3131
<div className="mb-3 space-y-2 px-1">
3232
<div className="flex items-center justify-between gap-2">
3333
<h3 className="font-semibold text-sm">{title}</h3>

0 commit comments

Comments
 (0)