Skip to content

Commit 82a37b8

Browse files
committed
Make VitePress docs fully WCAG 2.1 AA accessible
- Add ARIA tabs pattern (roving tabindex, keyboard nav) to UseCaseTabs - Add table semantics (caption, scope, aria-label) to ComparisonTable - Fix heading hierarchy and landmarks in InstallSection, ProductionCta, TestimonialsSection, HowItWorks - Fix contrast failures: .ct-feature-desc, .ct-tool-alt, .ts-role → text-1/2; .ts-avatar #CC88FF → #8833cc; .td-ps #9933ff → #aa55ff - Switch Shiki light theme to github-light-high-contrast (fixes #D73A49, #6A737D, #22863A tokens below 4.5:1); add CSS fallback overrides - aria-hidden="true" on decorative TerminalDemo - Add .sr-only utility and global :focus-visible ring to custom.css - Fix hero alt="" (decorative image) in docs/index.md - Add .github/workflows/a11y.yml (pa11y-ci, WCAG2AA, sitemap-driven) - Add .pa11yci.json with WCAG2AA config and F77 ignore (Mermaid SVG ids) - Add docs:a11y and docs:build:a11y scripts (VITEPRESS_HOSTNAME override so sitemap.xml uses localhost URLs during CI audit) - Split mermaid+d3 into dedicated Rollup chunk; raise chunkSizeWarningLimit to 2500 kB to silence legitimate Mermaid size warning - Increase HowItWorks step description font-size 14px → 15px
1 parent 851e4d8 commit 82a37b8

File tree

17 files changed

+360
-65
lines changed

17 files changed

+360
-65
lines changed

.github/workflows/a11y.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Accessibility audit — WCAG 2.1 AA
2+
#
3+
# Runs pa11y-ci against the built VitePress docs to ensure the homepage and
4+
# key pages remain WCAG 2.1 AA compliant.
5+
#
6+
# Triggers:
7+
# - push to main touching docs/** or this workflow / config
8+
# - every pull_request touching the same paths
9+
# - workflow_dispatch (manual run)
10+
#
11+
# Strategy:
12+
# 1. Build the docs with `bun run docs:build:a11y` (sets VITEPRESS_HOSTNAME=
13+
# http://localhost:4173 so the generated sitemap.xml contains localhost
14+
# URLs that pa11y-ci can reach directly)
15+
# 2. Start `vitepress preview` in the background (serves on port 4173)
16+
# 3. Wait until the server is accepting connections
17+
# 4. Run pa11y-ci configured in .pa11yci.json (WCAG 2.1 AA, errors only)
18+
#
19+
# Chrome: Ubuntu-latest ships google-chrome-stable. We skip Puppeteer's own
20+
# Chromium download (PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1) and point directly to
21+
# the system Chrome via PUPPETEER_EXECUTABLE_PATH at runtime. This cuts ~200 MB
22+
# from every CI run.
23+
name: Accessibility audit (WCAG 2.1 AA)
24+
25+
on:
26+
push:
27+
branches: [main]
28+
paths:
29+
- "docs/**"
30+
- ".github/workflows/a11y.yml"
31+
- ".pa11yci.json"
32+
pull_request:
33+
paths:
34+
- "docs/**"
35+
- ".github/workflows/a11y.yml"
36+
- ".pa11yci.json"
37+
workflow_dispatch:
38+
39+
# One audit at a time per branch; cancel stale runs on new push.
40+
concurrency:
41+
group: a11y-${{ github.ref }}
42+
cancel-in-progress: true
43+
44+
jobs:
45+
audit:
46+
name: WCAG 2.1 AA pages audit
47+
runs-on: ubuntu-latest
48+
49+
steps:
50+
- name: Checkout
51+
uses: actions/checkout@v6
52+
53+
- name: Setup Bun
54+
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3cf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3
55+
with:
56+
bun-version: latest
57+
58+
- name: Install dependencies
59+
run: bun install --frozen-lockfile
60+
61+
- name: Build docs (a11y — sitemap will use localhost URLs)
62+
run: bun run docs:build:a11y
63+
64+
# Start VitePress preview in the background; base URL: /github-code-search/
65+
# --port 4173 matches the URLs in .pa11yci.json
66+
- name: Start VitePress preview server
67+
run: bun run docs:preview -- --port 4173 &
68+
69+
# Poll until the preview server responds (max 60 s = 30 × 2 s).
70+
- name: Wait for preview server to be ready
71+
run: |
72+
echo "Waiting for VitePress preview on http://localhost:4173/github-code-search/ …"
73+
for i in $(seq 1 30); do
74+
if curl -sf http://localhost:4173/github-code-search/ > /dev/null 2>&1; then
75+
echo "Server ready after $((i * 2)) seconds."
76+
exit 0
77+
fi
78+
echo "Attempt $i/30 — retrying in 2 s …"
79+
sleep 2
80+
done
81+
echo "ERROR: preview server did not start within 60 seconds." >&2
82+
exit 1
83+
84+
# Run the audit. The env vars tell Puppeteer (used by pa11y) to use the
85+
# pre-installed system Chrome instead of downloading a Chromium binary.
86+
- name: Run accessibility audit (pa11y-ci)
87+
env:
88+
PUPPETEER_EXECUTABLE_PATH: /usr/bin/google-chrome-stable
89+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "1"
90+
run: bun run docs:a11y
91+
92+
# Upload the pa11y-ci JSON report as an artifact so failures are
93+
# easy to inspect without re-running the workflow.
94+
- name: Upload audit report
95+
if: always()
96+
uses: actions/upload-artifact@v4
97+
with:
98+
name: pa11y-ci-report
99+
path: a11y-report.json
100+
if-no-files-found: ignore

.github/workflows/cd.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
uses: actions/checkout@v6
4444

4545
- name: Setup Bun
46-
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
46+
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
4747
with:
4848
bun-version: latest
4949

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
uses: actions/checkout@v6
1717

1818
- name: Setup Bun
19-
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
19+
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
2020
with:
2121
bun-version: latest
2222

.github/workflows/docs.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ jobs:
5757

5858
steps:
5959
- name: Checkout
60-
uses: actions/checkout@v4.2.2
60+
uses: actions/checkout@v6
6161
with:
6262
# Needed to fetch the gh-pages storage branch for versioned snapshots.
6363
fetch-depth: 0
6464

6565
- name: Setup Bun
66-
uses: oven-sh/setup-bun@v2.1.2
66+
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.32.1.3
6767
with:
6868
bun-version: latest
6969

@@ -127,7 +127,7 @@ jobs:
127127

128128
steps:
129129
- name: Checkout
130-
uses: actions/checkout@v4.2.2
130+
uses: actions/checkout@v6
131131
with:
132132
# Full history needed to push to gh-pages and commit versions.json to main.
133133
fetch-depth: 0
@@ -146,7 +146,7 @@ jobs:
146146
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
147147
148148
- name: Setup Bun
149-
uses: oven-sh/setup-bun@v2.1.2
149+
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.32.1.3
150150
with:
151151
bun-version: latest
152152

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ dist/
33
coverage/
44
.env
55
*.local
6+
a11y-report.json
67

78
# VitePress
89
docs/.vitepress/cache

.pa11yci.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"defaults": {
3+
"standard": "WCAG2AA",
4+
"reporters": ["cli", ["json", { "fileName": "./a11y-report.json" }]],
5+
"level": "error",
6+
"wait": 1500,
7+
"timeout": 60000,
8+
"chromeLaunchConfig": {
9+
"args": ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]
10+
},
11+
"ignore": [
12+
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.BgImage",
13+
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G145.BgImage",
14+
"WCAG2AA.Principle4.Guideline4_1.4_1_1.F77"
15+
]
16+
}
17+
}

docs/.vitepress/config.mts

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,13 @@ export default defineConfig({
119119
content: "https://fulll.github.io/github-code-search/social-preview.png",
120120
},
121121
],
122-
["meta", { property: "og:url", content: "https://fulll.github.io/github-code-search/" }],
122+
[
123+
"meta",
124+
{
125+
property: "og:url",
126+
content: "https://fulll.github.io/github-code-search/",
127+
},
128+
],
123129
// ── Twitter Card ────────────────────────────────────────────────────────
124130
["meta", { name: "twitter:card", content: "summary_large_image" }],
125131
["meta", { name: "twitter:title", content: "github-code-search" }],
@@ -158,11 +164,40 @@ export default defineConfig({
158164
const svgPath = fileURLToPath(new URL("../public/social-preview.svg", import.meta.url));
159165
const pngPath = fileURLToPath(new URL("../public/social-preview.png", import.meta.url));
160166
const svg = readFileSync(svgPath, "utf-8");
161-
const resvg = new Resvg(svg, { fitTo: { mode: "width", value: 1200 } });
167+
const resvg = new Resvg(svg, {
168+
fitTo: { mode: "width", value: 1200 },
169+
});
162170
writeFileSync(pngPath, resvg.render().asPng());
163171
},
164172
},
165173
],
174+
// ── Chunk splitting ──────────────────────────────────────────────────────
175+
// Mermaid alone is >900 kB minified; split it + the d3 sub-tree into
176+
// dedicated async chunks to eliminate the Rollup 500 kB warning and
177+
// improve long-term caching. No generic vendor catch-all — VitePress
178+
// internals (mark.js etc.) need Rollup's default resolution.
179+
build: {
180+
// Mermaid (bundled with d3) is legitimately large (~2.4 MB minified).
181+
// 2500 kB threshold avoids the Rollup warning without masking real bloat
182+
// on other chunks (next largest is katex at ~260 kB).
183+
chunkSizeWarningLimit: 2500,
184+
rollupOptions: {
185+
output: {
186+
manualChunks(id: string) {
187+
// Mermaid + d3 must be co-located (circular dependency between them).
188+
if (
189+
id.includes("node_modules/mermaid") ||
190+
id.includes("node_modules/vitepress-plugin-mermaid") ||
191+
id.includes("node_modules/d3") ||
192+
id.includes("node_modules/dagre-d3-es") ||
193+
id.includes("node_modules/internmap") ||
194+
id.includes("node_modules/robust-predicates")
195+
)
196+
return "mermaid";
197+
},
198+
},
199+
},
200+
},
166201
},
167202

168203
themeConfig: {
@@ -305,13 +340,19 @@ export default defineConfig({
305340
// ── Markdown ──────────────────────────────────────────────────────────────
306341
markdown: {
307342
theme: {
308-
light: "github-light",
343+
// github-light-high-contrast fixes WCAG AA contrast for Shiki tokens
344+
// (github-light has #D73A49 4.24:1, #6A737D 4.46:1, #22863A 4.28:1 — all below 4.5:1)
345+
light: "github-light-high-contrast",
309346
dark: "github-dark",
310347
},
311348
},
312349

313350
// ── Sitemap ───────────────────────────────────────────────────────────────
351+
// VITEPRESS_HOSTNAME overrides the default for local/CI a11y audits:
352+
// VITEPRESS_HOSTNAME=http://localhost:4173 vitepress build docs
353+
// → sitemap.xml contains localhost URLs that pa11y-ci can reach directly.
314354
sitemap: {
315-
hostname: "https://fulll.github.io/github-code-search/",
355+
hostname:
356+
(process.env.VITEPRESS_HOSTNAME ?? "https://fulll.github.io") + "/github-code-search/",
316357
},
317358
});

docs/.vitepress/theme/ComparisonTable.vue

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,18 @@ const ROWS: Row[] = [
8080
<strong>org-wide code audits and interactive triage</strong>.
8181
</p>
8282
<table class="ct-table">
83+
<caption class="sr-only">
84+
Feature comparison between gh search code and github-code-search
85+
</caption>
8386
<thead>
8487
<tr>
85-
<th class="ct-th-feature"></th>
86-
<th class="ct-th-tool">
88+
<th class="ct-th-feature" scope="col" aria-label="Feature"></th>
89+
<th class="ct-th-tool" scope="col">
8790
<div class="ct-tool-header">
8891
<span class="ct-tool-name ct-tool-alt">gh search code</span>
8992
</div>
9093
</th>
91-
<th class="ct-th-tool">
94+
<th class="ct-th-tool" scope="col">
9295
<div class="ct-tool-header">
9396
<span class="ct-tool-name ct-tool-brand">github-code-search</span>
9497
<span class="ct-badge">Purpose-built</span>
@@ -132,12 +135,12 @@ const ROWS: Row[] = [
132135
</span>
133136
</td>
134137
<td class="ct-cell">
135-
<span v-if="row.gh" class="ct-check">✓</span>
136-
<span v-else class="ct-cross">✗</span>
138+
<span v-if="row.gh" class="ct-check" aria-label="Yes">✓</span>
139+
<span v-else class="ct-cross" aria-label="No">✗</span>
137140
</td>
138141
<td class="ct-cell">
139-
<span v-if="row.gcs" class="ct-check">✓</span>
140-
<span v-else class="ct-cross">✗</span>
142+
<span v-if="row.gcs" class="ct-check" aria-label="Yes">✓</span>
143+
<span v-else class="ct-cross" aria-label="No">✗</span>
141144
</td>
142145
</tr>
143146
</tbody>
@@ -248,7 +251,8 @@ thead tr {
248251
}
249252
250253
.ct-tool-alt {
251-
color: var(--vp-c-text-3);
254+
/* Fix: var(--vp-c-text-3) = 2.87:1, below WCAG AA 4.5:1. text-2 ≥ 5.4:1. */
255+
color: var(--vp-c-text-2);
252256
}
253257
254258
.ct-tool-brand {
@@ -323,7 +327,8 @@ thead tr {
323327
.ct-feature-desc {
324328
font-size: 13px;
325329
font-weight: 400;
326-
color: var(--vp-c-text-3);
330+
/* Fix: var(--vp-c-text-3) ≈ 2.87:1, below WCAG AA. text-1 ensures ≥4.5:1. */
331+
color: var(--vp-c-text-1);
327332
line-height: 1.45;
328333
}
329334

docs/.vitepress/theme/HowItWorks.vue

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
2-
<section class="hiw-section">
2+
<section class="hiw-section" aria-labelledby="hiw-heading">
33
<div class="hiw-header">
4-
<h2 class="hiw-title">How it works</h2>
4+
<h2 id="hiw-heading" class="hiw-title">How it works</h2>
55
<p class="hiw-subtitle">From query to structured output in three steps.</p>
66
</div>
77

@@ -229,17 +229,17 @@
229229
230230
.hiw-step-badge {
231231
display: inline-block;
232-
font-size: 11px;
232+
font-size: 12px;
233233
font-weight: 600;
234234
letter-spacing: 0.06em;
235235
text-transform: uppercase;
236236
color: var(--vp-c-brand-1);
237-
margin-bottom: 4px;
237+
margin-bottom: 5px;
238238
}
239239
240240
.hiw-step-title {
241-
margin: 0 0 6px;
242-
font-size: 17px;
241+
margin: 0 0 8px;
242+
font-size: 18px;
243243
font-weight: 700;
244244
letter-spacing: -0.01em;
245245
color: var(--vp-c-text-1);
@@ -249,8 +249,8 @@
249249
250250
.hiw-step-desc {
251251
margin: 0;
252-
font-size: 14px;
253-
line-height: 1.7;
252+
font-size: 15px;
253+
line-height: 1.75;
254254
color: var(--vp-c-text-2);
255255
}
256256
@@ -284,11 +284,11 @@ kbd {
284284
}
285285
286286
.hiw-step-title {
287-
font-size: 15px;
287+
font-size: 16px;
288288
}
289289
290290
.hiw-step-desc {
291-
font-size: 13px;
291+
font-size: 14px;
292292
}
293293
}
294294
</style>

0 commit comments

Comments
 (0)