Skip to content

Commit 571f5af

Browse files
committed
feat(a11y): improve accessibility across all pages
- Add skip-to-content link on community, download, and dashboard pages (home page already had it; now all pages are consistent) - Add aria-current="page" to active navigation links in Navbar, both desktop and mobile menus — screen readers now announce current page - Add aria-labelledby to major page sections (Features, How It Works, Supported Games on home; Preview Features on community) using matching id attributes on h2 elements - Add role="region" + aria-label to dashboard charts container - Add role="img" + aria-labelledby to individual chart wrappers (Recharts canvases are opaque to screen readers) - Add scope="col" to all dashboard table headers for correct cell-header associations in screen readers - Add aria-label="Recent ping test results" to the results table - Add aria-hidden="true" to purely decorative icons (Download, Construction, animated status dot) so screen readers skip them - Add aria-label and aria-disabled to download button for clear state announcement during loading - Add .sr-only utility class to globals.css (visually hidden but accessible to assistive tech) — used for the preview features section heading on the community page No new dependencies. No existing features removed. Build passes clean.
1 parent b0f5b66 commit 571f5af

File tree

6 files changed

+57
-27
lines changed

6 files changed

+57
-27
lines changed

web/src/app/community/page.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ import { Footer } from "@/components/Footer";
77
export default function CommunityPage() {
88
return (
99
<div className="min-h-screen">
10+
<a href="#main-content" className="skip-to-content focus-ring">
11+
Skip to main content
12+
</a>
13+
1014
<Navbar />
1115

12-
<main className="max-w-6xl mx-auto px-4 py-16">
16+
<main id="main-content" className="max-w-6xl mx-auto px-4 py-16">
1317
{/* Coming Soon Banner */}
1418
<div className="text-center mb-16">
1519
<div className="inline-flex items-center justify-center w-20 h-20 bg-yellow-500/20 rounded-2xl mb-6">
16-
<Construction className="w-10 h-10 text-yellow-500" />
20+
<Construction className="w-10 h-10 text-yellow-500" aria-hidden="true" />
1721
</div>
1822
<h1 className="text-4xl font-bold mb-4">Community Hub</h1>
1923
<p className="text-zinc-400 text-lg max-w-xl mx-auto">
@@ -25,7 +29,8 @@ export default function CommunityPage() {
2529
</div>
2630

2731
{/* Preview Features */}
28-
<div className="grid md:grid-cols-3 gap-6 mb-16">
32+
<section aria-labelledby="preview-features-heading" className="grid md:grid-cols-3 gap-6 mb-16">
33+
<h2 id="preview-features-heading" className="sr-only">Upcoming community features</h2>
2934
<div className="bg-zinc-900/50 border border-zinc-800 rounded-xl p-6 opacity-60">
3035
<div className="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center mb-4">
3136
<MessageSquare className="w-6 h-6 text-blue-500" />
@@ -55,7 +60,7 @@ export default function CommunityPage() {
5560
See the best ping results by region, ISP, and server location.
5661
</p>
5762
</div>
58-
</div>
63+
</section>
5964

6065
{/* CTA */}
6166
<div className="text-center bg-zinc-900/50 border border-zinc-800 rounded-2xl p-8">

web/src/app/dashboard/page.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,14 @@ export default function DashboardPage() {
226226

227227
return (
228228
<div className="min-h-screen">
229+
<a href="#main-content" className="skip-to-content focus-ring">
230+
Skip to main content
231+
</a>
232+
229233
<Navbar />
230234

231235
{/* Main Content */}
232-
<main className="max-w-6xl mx-auto px-4 py-8">
236+
<main id="main-content" className="max-w-6xl mx-auto px-4 py-8">
233237
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 mb-8">
234238
<div>
235239
<h1 className="text-3xl font-bold">Dashboard</h1>
@@ -398,11 +402,11 @@ export default function DashboardPage() {
398402
</div>
399403

400404
{/* Charts */}
401-
<div className="grid md:grid-cols-2 gap-6 mb-8">
405+
<div className="grid md:grid-cols-2 gap-6 mb-8" role="region" aria-label="Performance charts">
402406
{/* Ping History Chart */}
403407
<div className="bg-zinc-900 border border-zinc-800 rounded-xl p-6">
404-
<h3 className="text-lg font-semibold mb-4">Ping History</h3>
405-
<div className="h-64">
408+
<h3 className="text-lg font-semibold mb-4" id="ping-history-heading">Ping History</h3>
409+
<div className="h-64" role="img" aria-labelledby="ping-history-heading">
406410
<ResponsiveContainer width="100%" height="100%">
407411
<LineChart data={chartData}>
408412
<CartesianGrid strokeDasharray="3 3" stroke="#333" />
@@ -429,8 +433,8 @@ export default function DashboardPage() {
429433

430434
{/* Server Comparison Chart */}
431435
<div className="bg-zinc-900 border border-zinc-800 rounded-xl p-6">
432-
<h3 className="text-lg font-semibold mb-4">Server Comparison</h3>
433-
<div className="h-64">
436+
<h3 className="text-lg font-semibold mb-4" id="server-comparison-heading">Server Comparison</h3>
437+
<div className="h-64" role="img" aria-labelledby="server-comparison-heading">
434438
<ResponsiveContainer width="100%" height="100%">
435439
<BarChart data={serverChartData}>
436440
<CartesianGrid strokeDasharray="3 3" stroke="#333" />
@@ -459,15 +463,15 @@ export default function DashboardPage() {
459463
</span>
460464
</div>
461465
<div className="overflow-x-auto">
462-
<table className="w-full">
466+
<table className="w-full" aria-label="Recent ping test results">
463467
<thead>
464468
<tr className="text-left text-zinc-400 text-sm">
465-
<th className="pb-4">Server</th>
466-
<th className="pb-4">Ping</th>
467-
<th className="pb-4">Jitter</th>
468-
<th className="pb-4">Loss</th>
469-
<th className="pb-4">ISP</th>
470-
<th className="pb-4">Time</th>
469+
<th scope="col" className="pb-4">Server</th>
470+
<th scope="col" className="pb-4">Ping</th>
471+
<th scope="col" className="pb-4">Jitter</th>
472+
<th scope="col" className="pb-4">Loss</th>
473+
<th scope="col" className="pb-4">ISP</th>
474+
<th scope="col" className="pb-4">Time</th>
471475
</tr>
472476
</thead>
473477
<tbody>

web/src/app/download/page.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,14 @@ export default function DownloadPage() {
6565

6666
return (
6767
<div className="min-h-screen">
68+
<a href="#main-content" className="skip-to-content focus-ring">
69+
Skip to main content
70+
</a>
71+
6872
<Navbar />
6973

7074
{/* Main Content */}
71-
<main className="max-w-4xl mx-auto px-4 py-16">
75+
<main id="main-content" className="max-w-4xl mx-auto px-4 py-16">
7276
<div className="text-center mb-12">
7377
<h1 className="text-4xl font-bold mb-4">Download PingDiff</h1>
7478
<p className="text-zinc-400 text-lg">
@@ -111,8 +115,10 @@ export default function DownloadPage() {
111115
<a
112116
href={release?.downloadUrl || "#"}
113117
className={`inline-flex items-center gap-2 btn-primary px-8 py-4 rounded-xl font-semibold text-lg focus-ring ${loading ? 'opacity-50 pointer-events-none' : ''}`}
118+
aria-label={loading ? "Loading download link" : `Download ${getFileName()} for Windows`}
119+
aria-disabled={loading}
114120
>
115-
<Download className="w-5 h-5" />
121+
<Download className="w-5 h-5" aria-hidden="true" />
116122
{loading ? "Loading..." : `Download ${getFileName()}`}
117123
</a>
118124
</div>

web/src/app/globals.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ body {
9595
top: 0;
9696
}
9797

98+
/* Screen-reader only utility — visually hidden but accessible to assistive tech */
99+
.sr-only {
100+
position: absolute;
101+
width: 1px;
102+
height: 1px;
103+
padding: 0;
104+
margin: -1px;
105+
overflow: hidden;
106+
clip: rect(0, 0, 0, 0);
107+
white-space: nowrap;
108+
border-width: 0;
109+
}
110+
98111
/* Custom scrollbar */
99112
::-webkit-scrollbar {
100113
width: 8px;

web/src/app/page.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export default function Home() {
6565
href="/download"
6666
className="inline-flex items-center justify-center gap-2 btn-primary px-8 py-4 rounded-xl font-semibold text-lg focus-ring"
6767
>
68-
<Download className="w-5 h-5" />
68+
<Download className="w-5 h-5" aria-hidden="true" />
6969
Download for Windows
7070
</Link>
7171
<Link
@@ -94,9 +94,9 @@ export default function Home() {
9494
</section>
9595

9696
{/* Features Section */}
97-
<section className="bg-zinc-900/50 py-16 md:py-24">
97+
<section className="bg-zinc-900/50 py-16 md:py-24" aria-labelledby="features-heading">
9898
<div className="max-w-6xl mx-auto px-4">
99-
<h2 className="text-2xl md:text-3xl font-bold text-center mb-4">Why PingDiff?</h2>
99+
<h2 id="features-heading" className="text-2xl md:text-3xl font-bold text-center mb-4">Why PingDiff?</h2>
100100
<p className="text-zinc-400 text-center mb-12 max-w-2xl mx-auto">
101101
Stop guessing. Start knowing. Get real data about your connection before every game.
102102
</p>
@@ -198,9 +198,9 @@ export default function Home() {
198198
</section>
199199

200200
{/* How It Works */}
201-
<section className="py-16 md:py-24">
201+
<section className="py-16 md:py-24" aria-labelledby="how-it-works-heading">
202202
<div className="max-w-6xl mx-auto px-4">
203-
<h2 className="text-2xl md:text-3xl font-bold text-center mb-4">How It Works</h2>
203+
<h2 id="how-it-works-heading" className="text-2xl md:text-3xl font-bold text-center mb-4">How It Works</h2>
204204
<p className="text-zinc-400 text-center mb-12">
205205
Three simple steps to better gaming
206206
</p>
@@ -240,16 +240,16 @@ export default function Home() {
240240
</section>
241241

242242
{/* Supported Games */}
243-
<section className="bg-zinc-900/50 py-16 md:py-24">
243+
<section className="bg-zinc-900/50 py-16 md:py-24" aria-labelledby="supported-games-heading">
244244
<div className="max-w-6xl mx-auto px-4 text-center">
245-
<div className="inline-flex items-center gap-2 bg-blue-500/10 border border-blue-500/20 rounded-full px-4 py-2 mb-4">
245+
<div className="inline-flex items-center gap-2 bg-blue-500/10 border border-blue-500/20 rounded-full px-4 py-2 mb-4" aria-hidden="true">
246246
<span className="relative flex h-2 w-2">
247247
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
248248
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
249249
</span>
250250
<span className="text-sm text-blue-400">9 games • 141 servers worldwide</span>
251251
</div>
252-
<h2 className="text-2xl md:text-3xl font-bold mb-4">Supported Games</h2>
252+
<h2 id="supported-games-heading" className="text-2xl md:text-3xl font-bold mb-4">Supported Games</h2>
253253
<p className="text-zinc-400 mb-12">
254254
Test your connection to your favorite games. More coming soon.
255255
</p>

web/src/components/Navbar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export function Navbar() {
6060
className={`transition focus-ring rounded-lg px-2 py-1 ${
6161
isActive(href) ? "text-white font-medium" : "text-zinc-400 hover:text-white"
6262
}`}
63+
aria-current={isActive(href) ? "page" : undefined}
6364
>
6465
{label}
6566
</Link>
@@ -93,6 +94,7 @@ export function Navbar() {
9394
isActive(href) ? "text-white font-medium" : "text-zinc-400 hover:text-white"
9495
}`}
9596
onClick={() => setMobileMenuOpen(false)}
97+
aria-current={isActive(href) ? "page" : undefined}
9698
>
9799
{label}
98100
</Link>

0 commit comments

Comments
 (0)