Skip to content

Add layered accessibility conformance harness for Docusaurus site#2009

Merged
WilliamBerryiii merged 26 commits into
mainfrom
fix/hub-card-icon-alt-accessibility
Jun 17, 2026
Merged

Add layered accessibility conformance harness for Docusaurus site#2009
WilliamBerryiii merged 26 commits into
mainfrom
fix/hub-card-icon-alt-accessibility

Conversation

@WilliamBerryiii

Copy link
Copy Markdown
Member

Pull Request

Description

This PR began as a fix for the missing accessible treatment of decorative hub-card icons and expanded into a complete layered accessibility conformance harness for the Docusaurus documentation site.

Component and CSS conformance fixes (WCAG):

  • Marked the decorative hub-card icon as aria-hidden="true" so assistive technology ignores it (SC 1.1.1) in BoxCard.tsx.
  • Added content-link underlines (SC 1.4.1), a visible focus-visible outline (SC 2.4.7, SC 1.4.11), a dark-mode link-color override, and local-search muted-text contrast overrides for light and dark modes (SC 1.4.3) in custom.css.
  • Added a SearchBar swizzle that mirrors aria-owns to aria-controls and tags the "See all results" footer anchor with role="option" to complete the WAI-ARIA APG combobox/listbox pattern.

Layered accessibility CI gate:

A four-layer accessibility architecture wired into the docusaurus-tests workflow as a blocking guardrail targeting WCAG 2.2 AA at a zero-violation threshold:

  • Static linting via eslint-plugin-jsx-a11y (flat eslint.config.mjs).
  • Component-level axe assertions via Jest (jest-axe) with coverage reporting.
  • A full-site crawl via pa11y-ci (.pa11yci, WCAG2AA, threshold 0) across representative URLs.
  • Behavioral end-to-end checks via Playwright + axe-core covering color-mode, doc navigation, focus management, keyboard navigation, the mobile menu, reflow, search, and the skip link, each mapped to specific WCAG criteria (1.4.4, 1.4.10, 2.1.1, 2.1.2, 2.4.1, 2.4.3).

Workflow and dependency wiring:

  • Extended .github/workflows/docusaurus-tests.yml with the accessibility lint, coverage test, pa11y scan, and Playwright e2e steps, plus Codecov coverage upload using OIDC (id-token: write).
  • Added root and site npm scripts (docs:test:coverage, docs:test:e2e, docs:a11y, docs:lint, lint:docs-site, and their site-level counterparts) and the accessibility/test devDependencies with corresponding lockfile updates.

Planning artifacts:

  • Added a BRD and PRD documenting the accessibility conformance requirements and goals.
  • Added ADR-0003 adopting the layered four-tool CI gate and bumped the ADR config last_decision_id.

Linter accommodations:

  • Extended AdrBodyParser.psm1 to recognize dotfile path segments (e.g., .adr-config.yml).
  • Reworked null-value handling in Validate-MarkdownFrontmatter.ps1 so nullable schema fields validate correctly.

Related Issue(s)

None

Type of Change

Select all that apply:

Code & Documentation:

  • Bug fix (non-breaking change fixing an issue)
  • New feature (non-breaking change adding functionality)
  • Breaking change (fix or feature causing existing functionality to change)
  • Documentation update

Infrastructure & Configuration:

  • GitHub Actions workflow
  • Linting configuration (markdown, PowerShell, etc.)
  • Security configuration
  • DevContainer configuration
  • Dependency update

AI Artifacts:

  • Reviewed contribution with prompt-builder agent and addressed all feedback
  • Copilot instructions (.github/instructions/*.instructions.md)
  • Copilot prompt (.github/prompts/*.prompt.md)
  • Copilot agent (.github/agents/*.agent.md)
  • Copilot skill (.github/skills/*/SKILL.md)

Other:

  • Script/automation (.ps1, .sh, .py)
  • Other (please describe):

Testing

  • Added component tests in BoxCard.test.tsx asserting the decorative image is hidden from assistive technology and a jest-axe no-violations check with the icon present.
  • Added nine Playwright end-to-end specs under docs/docusaurus/e2e/ validating keyboard navigation, focus management and trap-escape, roving tabindex, skip link, reflow, mobile menu, color-mode toggle, and search combobox behavior, each running region-scoped axe scans.
  • Added a pa11y-ci full-site scan (WCAG2AA, zero-violation threshold) across five representative URLs.
  • Enabled Jest coverage collection with lcov and text-summary reporters and Codecov upload.
  • All four layers run in the docusaurus-tests GitHub Actions workflow as merge gates.

Checklist

Required Checks

  • Documentation is updated (if applicable)
  • Files follow existing naming conventions
  • Changes are backwards compatible (if applicable)
  • Tests added for new functionality (if applicable)

AI Artifact Contributions

  • Used /prompt-analyze to review contribution
  • Addressed all feedback from prompt-builder review
  • Verified contribution follows common standards and type-specific requirements

Required Automated Checks

The following validation commands must pass before merging:

  • Markdown linting: npm run lint:md
  • Spell checking: npm run spell-check
  • Frontmatter validation: npm run lint:frontmatter
  • Skill structure validation: npm run validate:skills
  • Link validation: npm run lint:md-links (deferred to CI)
  • PowerShell analysis: npm run lint:ps
  • Plugin freshness: npm run plugin:generate
  • Docusaurus tests: npm run docs:test

Security Considerations

  • This PR does not contain any sensitive or NDA information
  • Any new dependencies have been reviewed for security issues
  • Security-related scripts follow the principle of least privilege

Additional Notes

  • The branch name reflects the original icon fix; the change scope grew to a full layered accessibility harness plus planning artifacts.
  • The BRD targets WCAG 2.1 AA and the PRD targets WCAG 2.2 AA; the PRD documents this as an intentional supersession.
  • Manual assistive-technology validation (NVDA, JAWS, VoiceOver) is out of scope for this phase per the PRD.

…cusaurus site

- add pa11y + Playwright axe e2e suite and CI gate for the docs site
- fix hub-card SVG icons to use decorative-icon semantics
- add accessibility BRD, PRD, and ADR 0003 planning docs
- support ADR frontmatter validation for new planning artifacts

♿ - Generated by Copilot
…-accessibility

# Conflicts:
#	scripts/linting/Validate-MarkdownFrontmatter.ps1
- add docs:test:e2e, docs:a11y, and lint:docs-site scripts to root package.json
- wire a11y serve-and-test script and start-server-and-test dep in docusaurus
- refresh accessibility CI gate ADR table and conformance BRD

♿ - Generated by Copilot
…-accessibility

# Conflicts:
#	docs/docusaurus/package-lock.json
#	docs/docusaurus/package.json
Add networkidle, activedescendant, and cursored to the cspell word
allowlist for the Docusaurus e2e specs, and ignore the coverage output
directory.

✅ - Generated by Copilot
@WilliamBerryiii WilliamBerryiii requested a review from a team as a code owner June 15, 2026 22:51
…-accessibility

# Conflicts:
#	docs/planning/adrs/.adr-config.yml
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 2 package(s) with unknown licenses.
  • ⚠️ 80 packages with OpenSSF Scorecard issues.

View full job summary

…ecov OIDC

- resolve PR Validation startup_failure from reusable workflow permission mismatch

🔧 - Generated by Copilot
Comment thread docs/docusaurus/package.json Fixed
@codecov-commenter

codecov-commenter commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.74%. Comparing base (a847cfa) to head (c2df1fe).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2009      +/-   ##
==========================================
- Coverage   80.82%   80.74%   -0.08%     
==========================================
  Files         117      127      +10     
  Lines       19095    19176      +81     
  Branches        0       12      +12     
==========================================
+ Hits        15433    15484      +51     
- Misses       3662     3689      +27     
- Partials        0        3       +3     
Flag Coverage Δ
docusaurus 61.84% <100.00%> (?)
pester 84.64% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
docs/docusaurus/src/components/Cards/BoxCard.tsx 100.00% <100.00%> (ø)
scripts/linting/Modules/AdrBodyParser.psm1 94.26% <100.00%> (ø)
scripts/linting/Validate-MarkdownFrontmatter.ps1 78.70% <100.00%> (+0.15%) ⬆️
scripts/security/Test-ActionVersionConsistency.ps1 93.10% <100.00%> (+0.03%) ⬆️
scripts/security/Test-DependencyPinning.ps1 89.90% <100.00%> (+0.04%) ⬆️

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- add pkg:npm/uri-js to dependency-review allow-dependencies-licenses (compound BSD SPDX)
- pin js-yaml override in docusaurus package.json
- exempt file:/link: protocol refs from npm pinning analyzer
- add analyzer tests for local-path protocol references

🔒 - Generated by Copilot
…-accessibility

# Conflicts:
#	.github/workflows/dependency-review.yml
@github-actions github-actions Bot mentioned this pull request Jun 16, 2026
WilliamBerryiii and others added 13 commits June 15, 2026 18:30
- regenerate package-lock.json so npm ci resolves js-yaml@>=4.2.0

🔒 - Generated by Copilot
…ockfile

- remove forced js-yaml 4.2.0 override that broke gray-matter/@istanbuljs (js-yaml 3.x API)
- reconcile lockfile back to js-yaml 3.14.2 via npm install
- accept availability-only DoS risk (CWE-407); build parses only first-party YAML

🔒 - Generated by Copilot
…ht caching

- add steps to resolve and cache puppeteer and Playwright versions
- include new test fixtures for version consistency checks

🔧 - Generated by Copilot
- ensure that incomplete Chrome builds are removed to prevent installation failures

🔧 - Generated by Copilot
…ests

- verify Chrome executable presence before running tests
- clear puppeteer cache if Chrome is missing

🔧 - Generated by Copilot
- change cache key format for puppeteer
- improve error handling during Chrome installation
- ensure Chrome executable verification is robust

🔧 - Generated by Copilot
…ests

- handle intermittent failures during Chrome installation
- retry installation up to three times if executable is missing
- clear puppeteer cache on installation failure

🔧 - Generated by Copilot
…essibility tests

- ensure single-source provisioning of Chrome
- clear corrupted cache before reinstallation
- update cache keys for puppeteer and Playwright

🔧 - Generated by Copilot
… tests

- reclaim disk space before browser installs
- ensure complete re-download of Chrome on installation failures
- enhance error reporting during Chrome installation attempts

🔧 - Generated by Copilot
- add disk usage and cache state logging on install failure
- ensure clean extraction of Playwright browser builds

🔧 - Generated by Copilot
- remove unnecessary disk cleanup steps
- ensure Chrome is provisioned from the system instead of downloading
- update Playwright configuration to use the system Chrome

🔧 - Generated by Copilot
Comment thread .github/workflows/docusaurus-tests.yml Outdated
Comment thread docs/docusaurus/eslint.config.mjs
Comment thread package.json Outdated
Comment thread docs/docusaurus/.pa11yci Outdated
Comment thread docs/planning/adrs/0006-adopt-layered-accessibility-ci-gate.md
Comment thread docs/docusaurus/jest.config.js

@katriendg katriendg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessibility harness review (comments only — not approving or requesting changes)

Strong, well-architected feature. Test logic verified locally on this branch: lint:a11y clean, Jest 104/104, pa11y 5/5, Playwright e2e 24/24 (non-CI). The inline comments below cover one blocking CI defect plus completeness gaps. Three items that don't map to a diff line are listed here:

  • docs/docusaurus/README.md (not modified) — Documentation, Major: README has no mention of the new harness. Please add contributor docs for the four layers, the Google Chrome prerequisite, and local commands (docs:test:coverage, docs:a11y, docs:test:e2e, docs:test:e2e:setup, lint:docs-site).
  • BoxCard.tsx L23 (outside diff) — Code Quality, Minor (pre-existing): const iconUrl = icon ? useBaseUrl(icon) : undefined; calls a hook conditionally (rules-of-hooks). Not introduced here and not flagged because eslint-plugin-react-hooks isn't configured, but worth fixing while the component is touched: const resolvedIcon = useBaseUrl(icon ?? ''); const iconUrl = icon ? resolvedIcon : undefined;.
  • PR scope/branch hygiene — Maintainability, Minor: the branch began as a one-line icon fix and now bundles four general-purpose PowerShell linter changes and a dependency-review allowlist edit. Each is reasonable and ships with tests, but they widen the review surface beyond accessibility — consider splitting or explicitly calling them out.

Manual AT validation (NVDA/JAWS/VoiceOver) is out of scope per the PRD — a candidate follow-up.

@bindsi bindsi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved: the layered accessibility conformance harness, tests, and documentation are consistent with the Docusaurus validation surface. I did not find actionable accessibility or CI issues.

…tests

- update reuseExistingServer to true for CI environments
- prevent port conflicts and redundant rebuilds

🔧 - Generated by Copilot
- add typecheck step to CI workflow
- update accessibility scan command for clarity
- refine README to detail accessibility conformance layers
- adjust jest coverage thresholds for better reporting
- fix icon resolution logic in BoxCard component

🔧 - Generated by Copilot
@WilliamBerryiii

Copy link
Copy Markdown
Member Author

Thanks @katriendg for the thorough review — really appreciate the detail. Following up on the three points from your review summary that weren't tied to a specific diff line:

  • README harness mention — added an "Accessibility conformance harness" section to docs/docusaurus/README.md documenting the four layers (eslint jsx-a11y, jest + jest-axe, Playwright e2e, pa11y-ci) and the npm run entry points.
  • BoxCard conditional hook — fixed the rules-of-hooks issue in BoxCard.tsx: useBaseUrl is now called unconditionally (const resolvedIcon = useBaseUrl(icon ?? '')) and the result is gated afterward (const iconUrl = icon ? resolvedIcon : undefined).
  • PR scope hygiene — good flag. This branch also carries the unrelated PowerShell linter and dependency-review allowlist changes; I've called that out in the PR description so reviewers know they're intentional carry-along rather than part of the accessibility harness.

All six inline comments have been addressed with replies on each thread.

… README

Replace em-dashes and bolded-prefix list items in accessibility harness section with plain colon-delimited list. Refresh ms.date to current to clear the 90-day freshness gate.
@WilliamBerryiii WilliamBerryiii merged commit 70a0307 into main Jun 17, 2026
73 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants