From 82a37b853821be1e0f59b33484811a68f3fdcfa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HOUZ=C3=89?= Date: Sun, 8 Mar 2026 17:07:42 +0100 Subject: [PATCH 1/8] Make VitePress docs fully WCAG 2.1 AA accessible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .github/workflows/a11y.yml | 100 ++++++++++++++++++ .github/workflows/cd.yaml | 2 +- .github/workflows/ci.yaml | 2 +- .github/workflows/docs.yml | 8 +- .gitignore | 1 + .pa11yci.json | 17 +++ docs/.vitepress/config.mts | 49 ++++++++- docs/.vitepress/theme/ComparisonTable.vue | 23 ++-- docs/.vitepress/theme/HowItWorks.vue | 20 ++-- docs/.vitepress/theme/InstallSection.vue | 45 ++++---- docs/.vitepress/theme/ProductionCta.vue | 18 +++- docs/.vitepress/theme/TerminalDemo.vue | 11 +- docs/.vitepress/theme/TestimonialsSection.vue | 12 ++- docs/.vitepress/theme/UseCaseTabs.vue | 58 +++++++++- docs/.vitepress/theme/custom.css | 53 ++++++++++ docs/index.md | 2 +- package.json | 4 +- 17 files changed, 360 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/a11y.yml create mode 100644 .pa11yci.json diff --git a/.github/workflows/a11y.yml b/.github/workflows/a11y.yml new file mode 100644 index 0000000..57153cd --- /dev/null +++ b/.github/workflows/a11y.yml @@ -0,0 +1,100 @@ +# Accessibility audit — WCAG 2.1 AA +# +# Runs pa11y-ci against the built VitePress docs to ensure the homepage and +# key pages remain WCAG 2.1 AA compliant. +# +# Triggers: +# - push to main touching docs/** or this workflow / config +# - every pull_request touching the same paths +# - workflow_dispatch (manual run) +# +# Strategy: +# 1. Build the docs with `bun run docs:build:a11y` (sets VITEPRESS_HOSTNAME= +# http://localhost:4173 so the generated sitemap.xml contains localhost +# URLs that pa11y-ci can reach directly) +# 2. Start `vitepress preview` in the background (serves on port 4173) +# 3. Wait until the server is accepting connections +# 4. Run pa11y-ci configured in .pa11yci.json (WCAG 2.1 AA, errors only) +# +# Chrome: Ubuntu-latest ships google-chrome-stable. We skip Puppeteer's own +# Chromium download (PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1) and point directly to +# the system Chrome via PUPPETEER_EXECUTABLE_PATH at runtime. This cuts ~200 MB +# from every CI run. +name: Accessibility audit (WCAG 2.1 AA) + +on: + push: + branches: [main] + paths: + - "docs/**" + - ".github/workflows/a11y.yml" + - ".pa11yci.json" + pull_request: + paths: + - "docs/**" + - ".github/workflows/a11y.yml" + - ".pa11yci.json" + workflow_dispatch: + +# One audit at a time per branch; cancel stale runs on new push. +concurrency: + group: a11y-${{ github.ref }} + cancel-in-progress: true + +jobs: + audit: + name: WCAG 2.1 AA pages audit + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Bun + uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3cf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3 + with: + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build docs (a11y — sitemap will use localhost URLs) + run: bun run docs:build:a11y + + # Start VitePress preview in the background; base URL: /github-code-search/ + # --port 4173 matches the URLs in .pa11yci.json + - name: Start VitePress preview server + run: bun run docs:preview -- --port 4173 & + + # Poll until the preview server responds (max 60 s = 30 × 2 s). + - name: Wait for preview server to be ready + run: | + echo "Waiting for VitePress preview on http://localhost:4173/github-code-search/ …" + for i in $(seq 1 30); do + if curl -sf http://localhost:4173/github-code-search/ > /dev/null 2>&1; then + echo "Server ready after $((i * 2)) seconds." + exit 0 + fi + echo "Attempt $i/30 — retrying in 2 s …" + sleep 2 + done + echo "ERROR: preview server did not start within 60 seconds." >&2 + exit 1 + + # Run the audit. The env vars tell Puppeteer (used by pa11y) to use the + # pre-installed system Chrome instead of downloading a Chromium binary. + - name: Run accessibility audit (pa11y-ci) + env: + PUPPETEER_EXECUTABLE_PATH: /usr/bin/google-chrome-stable + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "1" + run: bun run docs:a11y + + # Upload the pa11y-ci JSON report as an artifact so failures are + # easy to inspect without re-running the workflow. + - name: Upload audit report + if: always() + uses: actions/upload-artifact@v4 + with: + name: pa11y-ci-report + path: a11y-report.json + if-no-files-found: ignore diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index edfb792..65832f1 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -43,7 +43,7 @@ jobs: uses: actions/checkout@v6 - name: Setup Bun - uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 + uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 with: bun-version: latest diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c921956..6efeac1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v6 - name: Setup Bun - uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 + uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 with: bun-version: latest diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 70d69fa..d81f238 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -57,13 +57,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.2.2 + uses: actions/checkout@v6 with: # Needed to fetch the gh-pages storage branch for versioned snapshots. fetch-depth: 0 - name: Setup Bun - uses: oven-sh/setup-bun@v2.1.2 + uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.32.1.3 with: bun-version: latest @@ -127,7 +127,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.2.2 + uses: actions/checkout@v6 with: # Full history needed to push to gh-pages and commit versions.json to main. fetch-depth: 0 @@ -146,7 +146,7 @@ jobs: echo "major=$MAJOR" >> "$GITHUB_OUTPUT" - name: Setup Bun - uses: oven-sh/setup-bun@v2.1.2 + uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.32.1.3 with: bun-version: latest diff --git a/.gitignore b/.gitignore index bd3cb7e..beef780 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ dist/ coverage/ .env *.local +a11y-report.json # VitePress docs/.vitepress/cache diff --git a/.pa11yci.json b/.pa11yci.json new file mode 100644 index 0000000..2c96b54 --- /dev/null +++ b/.pa11yci.json @@ -0,0 +1,17 @@ +{ + "defaults": { + "standard": "WCAG2AA", + "reporters": ["cli", ["json", { "fileName": "./a11y-report.json" }]], + "level": "error", + "wait": 1500, + "timeout": 60000, + "chromeLaunchConfig": { + "args": ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"] + }, + "ignore": [ + "WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.BgImage", + "WCAG2AA.Principle1.Guideline1_4.1_4_3.G145.BgImage", + "WCAG2AA.Principle4.Guideline4_1.4_1_1.F77" + ] + } +} diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index c29c0ce..bedf776 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -119,7 +119,13 @@ export default defineConfig({ content: "https://fulll.github.io/github-code-search/social-preview.png", }, ], - ["meta", { property: "og:url", content: "https://fulll.github.io/github-code-search/" }], + [ + "meta", + { + property: "og:url", + content: "https://fulll.github.io/github-code-search/", + }, + ], // ── Twitter Card ──────────────────────────────────────────────────────── ["meta", { name: "twitter:card", content: "summary_large_image" }], ["meta", { name: "twitter:title", content: "github-code-search" }], @@ -158,11 +164,40 @@ export default defineConfig({ const svgPath = fileURLToPath(new URL("../public/social-preview.svg", import.meta.url)); const pngPath = fileURLToPath(new URL("../public/social-preview.png", import.meta.url)); const svg = readFileSync(svgPath, "utf-8"); - const resvg = new Resvg(svg, { fitTo: { mode: "width", value: 1200 } }); + const resvg = new Resvg(svg, { + fitTo: { mode: "width", value: 1200 }, + }); writeFileSync(pngPath, resvg.render().asPng()); }, }, ], + // ── Chunk splitting ────────────────────────────────────────────────────── + // Mermaid alone is >900 kB minified; split it + the d3 sub-tree into + // dedicated async chunks to eliminate the Rollup 500 kB warning and + // improve long-term caching. No generic vendor catch-all — VitePress + // internals (mark.js etc.) need Rollup's default resolution. + build: { + // Mermaid (bundled with d3) is legitimately large (~2.4 MB minified). + // 2500 kB threshold avoids the Rollup warning without masking real bloat + // on other chunks (next largest is katex at ~260 kB). + chunkSizeWarningLimit: 2500, + rollupOptions: { + output: { + manualChunks(id: string) { + // Mermaid + d3 must be co-located (circular dependency between them). + if ( + id.includes("node_modules/mermaid") || + id.includes("node_modules/vitepress-plugin-mermaid") || + id.includes("node_modules/d3") || + id.includes("node_modules/dagre-d3-es") || + id.includes("node_modules/internmap") || + id.includes("node_modules/robust-predicates") + ) + return "mermaid"; + }, + }, + }, + }, }, themeConfig: { @@ -305,13 +340,19 @@ export default defineConfig({ // ── Markdown ────────────────────────────────────────────────────────────── markdown: { theme: { - light: "github-light", + // github-light-high-contrast fixes WCAG AA contrast for Shiki tokens + // (github-light has #D73A49 4.24:1, #6A737D 4.46:1, #22863A 4.28:1 — all below 4.5:1) + light: "github-light-high-contrast", dark: "github-dark", }, }, // ── Sitemap ─────────────────────────────────────────────────────────────── + // VITEPRESS_HOSTNAME overrides the default for local/CI a11y audits: + // VITEPRESS_HOSTNAME=http://localhost:4173 vitepress build docs + // → sitemap.xml contains localhost URLs that pa11y-ci can reach directly. sitemap: { - hostname: "https://fulll.github.io/github-code-search/", + hostname: + (process.env.VITEPRESS_HOSTNAME ?? "https://fulll.github.io") + "/github-code-search/", }, }); diff --git a/docs/.vitepress/theme/ComparisonTable.vue b/docs/.vitepress/theme/ComparisonTable.vue index fa58ee7..5157f1d 100644 --- a/docs/.vitepress/theme/ComparisonTable.vue +++ b/docs/.vitepress/theme/ComparisonTable.vue @@ -80,15 +80,18 @@ const ROWS: Row[] = [ org-wide code audits and interactive triage.

+ - - + - @@ -248,7 +251,8 @@ thead tr { } .ct-tool-alt { - color: var(--vp-c-text-3); + /* Fix: var(--vp-c-text-3) = 2.87:1, below WCAG AA 4.5:1. text-2 ≥ 5.4:1. */ + color: var(--vp-c-text-2); } .ct-tool-brand { @@ -323,7 +327,8 @@ thead tr { .ct-feature-desc { font-size: 13px; font-weight: 400; - color: var(--vp-c-text-3); + /* Fix: var(--vp-c-text-3) ≈ 2.87:1, below WCAG AA. text-1 ensures ≥4.5:1. */ + color: var(--vp-c-text-1); line-height: 1.45; } diff --git a/docs/.vitepress/theme/HowItWorks.vue b/docs/.vitepress/theme/HowItWorks.vue index b14884f..a42498b 100644 --- a/docs/.vitepress/theme/HowItWorks.vue +++ b/docs/.vitepress/theme/HowItWorks.vue @@ -1,7 +1,7 @@
+ Feature comparison between gh search code and github-code-search +
+
gh search code
+
github-code-search Purpose-built @@ -132,12 +135,12 @@ const ROWS: Row[] = [
- - + + - - + +