Skip to content

Commit 57a0330

Browse files
Phase 1 Complete: Critical accessibility fixes implemented
Co-authored-by: rezwana-karim <126201034+rezwana-karim@users.noreply.github.com>
1 parent d0d37b7 commit 57a0330

5 files changed

Lines changed: 209 additions & 49 deletions

File tree

src/app/globals.css

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,87 @@ body {
148148
* {
149149
border-color: var(--border);
150150
}
151+
152+
/* Enhanced Focus Indicators for WCAG 2.1 AA Compliance */
153+
*:focus {
154+
outline: none;
155+
}
156+
157+
*:focus-visible {
158+
outline: 2px solid var(--ring);
159+
outline-offset: 2px;
160+
border-radius: calc(var(--radius) - 2px);
161+
}
162+
163+
/* High contrast focus for better visibility */
164+
@media (prefers-contrast: high) {
165+
*:focus-visible {
166+
outline-width: 3px;
167+
outline-offset: 3px;
168+
}
169+
}
170+
171+
/* Skip links enhanced visibility */
172+
.sr-only:focus-visible,
173+
.sr-only:focus {
174+
position: absolute;
175+
width: auto;
176+
height: auto;
177+
padding: 0.5rem 1rem;
178+
margin: 0;
179+
overflow: visible;
180+
clip: auto;
181+
white-space: nowrap;
182+
background: var(--primary);
183+
color: var(--primary-foreground);
184+
border-radius: var(--radius);
185+
box-shadow: var(--shadow-lg);
186+
z-index: 1000;
187+
}
188+
189+
/* Button focus states */
190+
button:focus-visible,
191+
[role="button"]:focus-visible {
192+
outline: 2px solid var(--ring);
193+
outline-offset: 2px;
194+
}
195+
196+
/* Link focus states */
197+
a:focus-visible {
198+
outline: 2px solid var(--ring);
199+
outline-offset: 2px;
200+
text-decoration: underline;
201+
text-decoration-thickness: 2px;
202+
text-underline-offset: 3px;
203+
}
204+
205+
/* Form element focus states */
206+
input:focus-visible,
207+
textarea:focus-visible,
208+
select:focus-visible {
209+
outline: 2px solid var(--ring);
210+
outline-offset: 2px;
211+
border-color: var(--ring);
212+
}
213+
214+
/* Ensure interactive elements have minimum touch target size */
215+
button,
216+
[role="button"],
217+
input[type="button"],
218+
input[type="submit"],
219+
input[type="reset"] {
220+
min-height: 44px;
221+
min-width: 44px;
222+
}
223+
224+
/* Improved link styling for better accessibility */
225+
a {
226+
color: var(--primary);
227+
text-decoration-skip-ink: auto;
228+
}
229+
230+
a:hover {
231+
text-decoration: underline;
232+
text-decoration-thickness: 2px;
233+
text-underline-offset: 3px;
234+
}

src/components/layout/header.tsx

Lines changed: 106 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ export function Header() {
5656
<Container>
5757
<div className="flex h-16 items-center justify-between">
5858
{/* Logo */}
59-
<Link href="/" className="flex items-center space-x-2">
59+
<Link
60+
href="/"
61+
className="flex items-center space-x-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-md"
62+
aria-label="CodeStorm Hub - Return to homepage"
63+
>
6064
<Logo size="sm" />
6165
</Link>
6266

@@ -80,22 +84,34 @@ export function Header() {
8084
{/* Community Dropdown */}
8185
<DropdownMenu.Root>
8286
<DropdownMenu.Trigger asChild>
83-
<Button variant="ghost" size="sm" className="h-10 text-sm font-medium text-foreground/60 hover:text-foreground">
87+
<Button
88+
variant="ghost"
89+
size="sm"
90+
className="h-10 text-sm font-medium text-foreground/60 hover:text-foreground"
91+
aria-label="Community menu"
92+
aria-haspopup="true"
93+
aria-expanded="false"
94+
>
8495
Community
85-
<ChevronDownIcon className="ml-1 h-3 w-3" />
96+
<ChevronDownIcon className="ml-1 h-3 w-3" aria-hidden="true" />
8697
</Button>
8798
</DropdownMenu.Trigger>
8899
<DropdownMenu.Portal>
89-
<DropdownMenu.Content className="min-w-[200px] bg-popover border border-border rounded-md shadow-md p-1" sideOffset={5}>
100+
<DropdownMenu.Content
101+
className="min-w-[200px] bg-popover border border-border rounded-md shadow-md p-1"
102+
sideOffset={5}
103+
aria-label="Community submenu"
104+
>
90105
{navigationItems.community.map((item) => (
91106
<DropdownMenu.Item key={item.href} asChild>
92107
<Link
93108
href={item.href}
94-
className={`flex items-center px-3 py-2 text-sm rounded-sm cursor-default focus:outline-none transition-colors ${
109+
className={`flex items-center px-3 py-2 text-sm rounded-sm cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors ${
95110
isActivePath(item.href)
96111
? "bg-accent text-accent-foreground"
97112
: "text-popover-foreground hover:bg-accent hover:text-accent-foreground"
98113
}`}
114+
role="menuitem"
99115
>
100116
{item.label}
101117
</Link>
@@ -108,22 +124,34 @@ export function Header() {
108124
{/* Resources Dropdown */}
109125
<DropdownMenu.Root>
110126
<DropdownMenu.Trigger asChild>
111-
<Button variant="ghost" size="sm" className="h-10 text-sm font-medium text-foreground/60 hover:text-foreground">
127+
<Button
128+
variant="ghost"
129+
size="sm"
130+
className="h-10 text-sm font-medium text-foreground/60 hover:text-foreground"
131+
aria-label="Resources menu"
132+
aria-haspopup="true"
133+
aria-expanded="false"
134+
>
112135
Resources
113-
<ChevronDownIcon className="ml-1 h-3 w-3" />
136+
<ChevronDownIcon className="ml-1 h-3 w-3" aria-hidden="true" />
114137
</Button>
115138
</DropdownMenu.Trigger>
116139
<DropdownMenu.Portal>
117-
<DropdownMenu.Content className="min-w-[200px] bg-popover border border-border rounded-md shadow-md p-1" sideOffset={5}>
140+
<DropdownMenu.Content
141+
className="min-w-[200px] bg-popover border border-border rounded-md shadow-md p-1"
142+
sideOffset={5}
143+
aria-label="Resources submenu"
144+
>
118145
{navigationItems.resources.map((item) => (
119146
<DropdownMenu.Item key={item.href} asChild>
120147
<Link
121148
href={item.href}
122-
className={`flex items-center px-3 py-2 text-sm rounded-sm cursor-default focus:outline-none transition-colors ${
149+
className={`flex items-center px-3 py-2 text-sm rounded-sm cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors ${
123150
isActivePath(item.href)
124151
? "bg-accent text-accent-foreground"
125152
: "text-popover-foreground hover:bg-accent hover:text-accent-foreground"
126153
}`}
154+
role="menuitem"
127155
>
128156
{item.label}
129157
</Link>
@@ -137,13 +165,27 @@ export function Header() {
137165
{/* Mobile Menu Button */}
138166
<div className="flex items-center space-x-2">
139167
<ThemeToggle />
140-
<Button variant="ghost" size="sm" asChild className="hidden sm:flex">
141-
<Link href="https://github.com/CodeStorm-Hub" target="_blank" rel="noopener noreferrer">
142-
<GitHubLogoIcon className="h-4 w-4 mr-2" />
168+
<Button
169+
variant="ghost"
170+
size="sm"
171+
asChild
172+
className="hidden sm:flex focus-visible:ring-2 focus-visible:ring-ring"
173+
>
174+
<Link
175+
href="https://github.com/CodeStorm-Hub"
176+
target="_blank"
177+
rel="noopener noreferrer"
178+
aria-label="Visit CodeStorm Hub GitHub repository (opens in new tab)"
179+
>
180+
<GitHubLogoIcon className="h-4 w-4 mr-2" aria-hidden="true" />
143181
GitHub
144182
</Link>
145183
</Button>
146-
<Button size="sm" className="hidden sm:inline-flex">
184+
<Button
185+
size="sm"
186+
className="hidden sm:inline-flex"
187+
aria-label="Join the CodeStorm Hub community"
188+
>
147189
Join Us
148190
</Button>
149191

@@ -155,29 +197,34 @@ export function Header() {
155197
onClick={toggleMobileMenu}
156198
aria-expanded={isMobileMenuOpen}
157199
aria-controls="mobile-menu"
158-
aria-label="Toggle navigation menu"
200+
aria-label={isMobileMenuOpen ? "Close navigation menu" : "Open navigation menu"}
159201
>
160202
{isMobileMenuOpen ? (
161-
<Cross1Icon className="h-4 w-4" />
203+
<Cross1Icon className="h-4 w-4" aria-hidden="true" />
162204
) : (
163-
<HamburgerMenuIcon className="h-4 w-4" />
205+
<HamburgerMenuIcon className="h-4 w-4" aria-hidden="true" />
164206
)}
165207
</Button>
166208
</div>
167209
</div>
168210

169211
{/* Mobile Navigation Menu */}
170212
{isMobileMenuOpen && (
171-
<div id="mobile-menu" className="lg:hidden border-t border-border">
213+
<nav
214+
id="mobile-menu"
215+
className="lg:hidden border-t border-border"
216+
aria-label="Mobile navigation"
217+
>
172218
<div className="px-2 pt-2 pb-3 space-y-1">
173219
{/* Main Navigation */}
174-
<div className="space-y-1">
220+
<div className="space-y-1" role="group" aria-labelledby="mobile-main-nav">
221+
<h3 id="mobile-main-nav" className="sr-only">Main navigation</h3>
175222
{navigationItems.main.map((item) => (
176223
<Link
177224
key={item.href}
178225
href={item.href}
179226
onClick={() => setIsMobileMenuOpen(false)}
180-
className={`block px-3 py-2 text-base font-medium rounded-md transition-colors ${
227+
className={`block px-3 py-2 text-base font-medium rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${
181228
isActivePath(item.href)
182229
? "text-foreground bg-accent"
183230
: "text-foreground/60 hover:text-foreground hover:bg-accent/50"
@@ -189,16 +236,19 @@ export function Header() {
189236
</div>
190237

191238
{/* Community Section */}
192-
<div className="pt-4">
193-
<div className="px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
239+
<div className="pt-4" role="group" aria-labelledby="mobile-community-nav">
240+
<h3
241+
id="mobile-community-nav"
242+
className="px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider"
243+
>
194244
Community
195-
</div>
245+
</h3>
196246
{navigationItems.community.map((item) => (
197247
<Link
198248
key={item.href}
199249
href={item.href}
200250
onClick={() => setIsMobileMenuOpen(false)}
201-
className={`block px-3 py-2 text-base font-medium rounded-md transition-colors ${
251+
className={`block px-3 py-2 text-base font-medium rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${
202252
isActivePath(item.href)
203253
? "text-foreground bg-accent"
204254
: "text-foreground/60 hover:text-foreground hover:bg-accent/50"
@@ -210,16 +260,19 @@ export function Header() {
210260
</div>
211261

212262
{/* Resources Section */}
213-
<div className="pt-4">
214-
<div className="px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
263+
<div className="pt-4" role="group" aria-labelledby="mobile-resources-nav">
264+
<h3
265+
id="mobile-resources-nav"
266+
className="px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider"
267+
>
215268
Resources
216-
</div>
269+
</h3>
217270
{navigationItems.resources.map((item) => (
218271
<Link
219272
key={item.href}
220273
href={item.href}
221274
onClick={() => setIsMobileMenuOpen(false)}
222-
className={`block px-3 py-2 text-base font-medium rounded-md transition-colors ${
275+
className={`block px-3 py-2 text-base font-medium rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${
223276
isActivePath(item.href)
224277
? "text-foreground bg-accent"
225278
: "text-foreground/60 hover:text-foreground hover:bg-accent/50"
@@ -231,35 +284,53 @@ export function Header() {
231284
</div>
232285

233286
{/* Mobile CTA Buttons */}
234-
<div className="pt-4 space-y-2">
235-
<Button asChild className="w-full justify-center">
236-
<Link href="https://github.com/CodeStorm-Hub" target="_blank" rel="noopener noreferrer">
237-
<GitHubLogoIcon className="h-4 w-4 mr-2" />
287+
<div className="pt-4 space-y-2" role="group" aria-label="Call to action buttons">
288+
<Button
289+
asChild
290+
className="w-full justify-center focus-visible:ring-2 focus-visible:ring-ring"
291+
>
292+
<Link
293+
href="https://github.com/CodeStorm-Hub"
294+
target="_blank"
295+
rel="noopener noreferrer"
296+
aria-label="View CodeStorm Hub on GitHub (opens in new tab)"
297+
onClick={() => setIsMobileMenuOpen(false)}
298+
>
299+
<GitHubLogoIcon className="h-4 w-4 mr-2" aria-hidden="true" />
238300
View on GitHub
239301
</Link>
240302
</Button>
241-
<Button variant="outline" className="w-full justify-center">
303+
<Button
304+
variant="outline"
305+
className="w-full justify-center"
306+
aria-label="Join the CodeStorm Hub community"
307+
>
242308
Join Community
243309
</Button>
244310
</div>
245311

246312
{/* Legal Links */}
247-
<div className="pt-4 border-t border-border">
313+
<div
314+
className="pt-4 border-t border-border"
315+
role="group"
316+
aria-labelledby="mobile-legal-nav"
317+
>
318+
<h3 id="mobile-legal-nav" className="sr-only">Legal and policy links</h3>
248319
<div className="flex flex-wrap gap-4 px-3 py-2">
249320
{navigationItems.legal.map((item) => (
250321
<Link
251322
key={item.href}
252323
href={item.href}
253324
onClick={() => setIsMobileMenuOpen(false)}
254-
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
325+
className="text-sm text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded"
255326
>
256327
{item.label}
257328
</Link>
258329
))}
259330
</div>
260331
</div>
261332
</div>
262-
</div>
333+
</nav>
263334
)}
264335
</Container>
265336
</header>

src/components/ui/button.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ import { cva, type VariantProps } from "class-variance-authority"
55
import { cn } from "@/lib/utils"
66

77
const buttonVariants = cva(
8-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
8+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98]",
99
{
1010
variants: {
1111
variant: {
12-
default: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80",
13-
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80",
14-
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground active:bg-accent/80",
15-
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70",
16-
ghost: "hover:bg-accent hover:text-accent-foreground active:bg-accent/80",
17-
link: "text-primary underline-offset-4 hover:underline active:text-primary/80",
18-
success: "bg-success text-success-foreground hover:bg-success/90 active:bg-success/80",
19-
warning: "bg-warning text-warning-foreground hover:bg-warning/90 active:bg-warning/80",
12+
default: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 focus-visible:ring-primary/70",
13+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 focus-visible:ring-destructive/70",
14+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground active:bg-accent/80 focus-visible:ring-accent/70",
15+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70 focus-visible:ring-secondary/70",
16+
ghost: "hover:bg-accent hover:text-accent-foreground active:bg-accent/80 focus-visible:ring-accent/70",
17+
link: "text-primary underline-offset-4 hover:underline active:text-primary/80 focus-visible:ring-primary/70 focus-visible:ring-offset-0",
18+
success: "bg-success text-success-foreground hover:bg-success/90 active:bg-success/80 focus-visible:ring-success/70",
19+
warning: "bg-warning text-warning-foreground hover:bg-warning/90 active:bg-warning/80 focus-visible:ring-warning/70",
2020
},
2121
size: {
2222
default: "h-10 px-4 py-2",

0 commit comments

Comments
 (0)