Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 61 additions & 10 deletions docs/contributing/accessibility.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
---
sidebar_position: 13
title: Accessibility Best Practices
description: Standards for accessible documentation, CLI output, and the dataviewer web application
description: Standards for accessible documentation, CLI output, and project-owned web applications
author: Microsoft Robotics-AI Team
ms.date: 2026-09-19
ms.topic: reference
---

Apply accessibility requirements when authoring documentation, CLI output, and dataviewer user interfaces.
This document defines accessibility requirements for documentation, runtime web interfaces, generated evidence, and CLI output in this repository. Use it when changing user-facing content or interaction behavior.

## Scope

This project applies accessibility best practices to three areas.
This project targets WCAG 2.2 Level AA for project-owned web interfaces. Section 508 and EN 301 549 require an explicit scope decision based on authoritative organizational facts.

| Area | What the project controls |
|---------------|----------------------------------------------------------------------------|
| Documentation | Markdown files rendered on GitHub and documentation sites |
| CLI output | Shell scripts in `infrastructure/setup/` and `scripts/` that emit messages |
| Web UI | React application in `data-management/viewer/frontend/` |
| Area | What the project controls |
|--------------------|----------------------------------------------------------------------------------|
| Documentation | Markdown files rendered on GitHub and the Docusaurus site |
| Dataset Viewer | Project-owned React interaction, adaptive layout, and assistive-technology paths |
| Docusaurus runtime | Local production build, navigation, search, content semantics, and visual states |
| Generated evidence | Project mappings, state proofs, composed bundles, and non-attestation summaries |
| CLI output | Shell scripts in `infrastructure/setup/` and `scripts/` that emit messages |

GitHub Pages hosting behavior remains provider-owned. Accessibility criterion evidence targets the immutable local Docusaurus production build before publication.

> [!NOTE]
> The dataviewer frontend enables the recommended `jsx-a11y` ESLint rules. Static linting is not evidence of WCAG conformance; interaction and assistive-technology checks remain part of UI review.
> The Dataset Viewer enables the recommended `jsx-a11y` ESLint rules. Static linting is not evidence of WCAG conformance; interaction and assistive-technology checks remain part of UI review.

## Documentation Accessibility

Expand All @@ -32,6 +36,8 @@ All Markdown files follow these conventions, which are enforced by markdownlint
* Use tables and lists for structured data rather than dense paragraphs
* Use GitHub alerts (`> [!NOTE]`, `> [!WARNING]`) for important callouts
* Provide text equivalents for any diagrams or visual content
* Confirm that headings, table relationships, alerts, code, lists, task states, and links retain their intended semantics after rendering
* Run source activation guards when content introduces media, timed behavior, forms, authentication, gestures, or new interactive components

### Alt Text Guidelines

Expand Down Expand Up @@ -76,9 +82,54 @@ Use semantic controls, accessible names, visible keyboard focus, and keyboard-op

The [frontend ESLint configuration](../../data-management/viewer/frontend/eslint.config.js) defines the automated accessibility checks.

## Runtime Accessibility Validation

The Docusaurus test workflow separates deterministic automation from qualified human judgment. Automated tooling can decide only the propositions its method is adequate to verify; it does not establish complete conformance.

| Evidence layer | Required coverage |
|--------------------|-------------------------------------------------------------------------------------------------------------|
| Source and unit | Content activation guards, component semantics, route and feature inventories |
| Browser automation | Axe, keyboard paths, focus, live regions, accessibility-tree relationships, contrast, reflow, and geometry |
| Qualified review | Spoken output, reading order, meaning, label quality, graphic equivalence, and exception approval |
| Release evaluation | Current automated evidence plus approved, digest-bound qualified-human results |

Run deterministic Docusaurus checks against the local production build:

```bash
npm --prefix docs/docusaurus run test:coverage
npm --prefix docs/docusaurus run ci:test:e2e
```

The pull-request scope covers deterministic Docusaurus journeys DCS01-DCS12. Release scope adds DCS13 and every required qualified-human cell. Missing manual evidence remains `NOT_ASSESSED`; it does not block deterministic bundle production, but it prevents release completeness.

## Qualified Accessibility Review

Use Windows NVDA with Microsoft Edge and a human-led JAWS pass for the supported screen-reader baseline. VoiceOver and mobile assistive technology are unsupported until an explicit scope change adds them.

Review DCS02-DCS08 and DCS10-DCS11 against the exact local production build:

1. Record the source revision, build digest, browser and assistive-technology versions, route, state, viewport or zoom, input mode, and expected result.
2. Verify spoken search status and result position, heading and landmark navigation, table relationships, reading order, labels, link purpose, diagram equivalence, browser zoom, focus perception, and approved exceptions.
3. Classify each result as `PASS`, `FAIL`, `CANT_TELL`, `NOT_ASSESSED`, or `INAPPLICABLE`. Include a limitation and re-entry trigger for every `NOT_ASSESSED` result.
4. Store raw speech, transcripts, screenshots with personal data, and restricted observations outside the repository and ordinary CI artifacts.
5. Author a privacy-minimized result using the upstream `qualified-human-result.schema.json`. Include a non-secret reviewer identifier, qualification and approval record digests, approved observation summary, artifact digests, validity, and supersession fields.
6. Recompose against the prior bundle and an independently retained prior-bundle digest. Supply the reviewer-registry digest from caller-controlled CI configuration rather than the registry file.

Qualified review uses two cadences:

| Cadence | Review boundary |
|------------------|---------------------------------------------------------------------------------------------------------|
| Initial baseline | Full-site evaluation of all applicable routes, states, complete processes, and qualified-human methods |
| Routine release | Representative WCAG-EM sample plus a random 10 percent of the eligible page set |
| Full reevaluation| Repeat after changes to build identity, navigation, search, rendering, evidence methods, or scope |

The composed bundle always retains `attestation: false`. A qualified result contributes evidence; it does not independently authorize a public conformance claim.

## Generated Artifacts

Automated pipelines, Terraform plans, and Helm chart outputs are out of scope. Accessibility standards apply only to human-authored content committed to the repository.
Accessibility requirements apply to generated content that people consume or use to make decisions, including diagrams, previews, reports, and evidence summaries. Terraform plans and Helm outputs remain outside the current user-journey inventory unless a project decision activates them.

Retained accessibility bundles contain approved summaries, method and scope limits, stable identifiers, and artifact digests. They exclude credentials, environment-specific endpoints, raw screen-reader speech, private reviewer identities, and restricted transcript paths.

## OpenSSF Compliance

Expand Down
17 changes: 16 additions & 1 deletion docs/data-pipeline/chunking-compression-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,22 @@ topics:
| Write latency is high | Measure sustained disk throughput and cache behavior; bag splitting is not a flush control |
| Upload is slow | Transfer completed files; test split sizes against the upload workflow |

Compression, caches, and chunk sizes trade CPU, memory, and throughput. Benchmark each change and validate recorded messages; none guarantees durability after a crash.
```mermaid
flowchart TD
accTitle: Recording performance troubleshooting flow
accDescr: Decision tree for measuring recording performance and selecting tests for storage growth, CPU saturation, write latency, and slow uploads.
A[Recording problem detected] --> B{Primary observation?}
B -- Storage fills quickly --> C[Measure bag rate]
C --> D[Test writer compression<br/>or reduce publisher rate]
B -- CPU saturated --> E[Test a faster codec or level<br/>or reduce input volume]
B -- Write latency high --> F[Measure sustained disk throughput<br/>and cache behavior]
F --> G[Do not treat bag splitting<br/>as a flush control]
B -- Upload slow --> H[Transfer completed files]
H --> I[Test split sizes against<br/>the upload workflow]
```

> [!IMPORTANT]
> Compression, caches, and chunk sizes trade CPU, memory, and throughput. Benchmark each change and validate recorded messages; none guarantees durability after a crash.

## Troubleshooting

Expand Down
5 changes: 5 additions & 0 deletions docs/docusaurus/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.docusaurus/
build/
node_modules/
coverage/
playwright-report/
playwright-report-*/
test-results/
test-results-*/
122 changes: 122 additions & 0 deletions docs/docusaurus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: Docusaurus Site Operations
description: Install, validate, test, serve, and troubleshoot the documentation site
author: Microsoft Robotics-AI Team
ms.date: 2026-09-14
ms.topic: how-to
---

Operate the repository documentation site from the repository root or from `docs/docusaurus`.

## 📋 Prerequisites

| Tool | Requirement | Purpose |
|---------------|-------------|------------------------------------------|
| Node.js | 24+ | Docusaurus build and test runtime |
| npm | Bundled | Locked dependency installation |
| Google Chrome | Current | Production-build Playwright verification |

## 🚀 Local Development

Install the locked dependency graph, then start the development server:

```bash
cd docs/docusaurus
npm ci
npm start
```

The site uses the `/physical-ai-toolchain/` base path.

## ✅ Validation

Run the local-safe validation lane from the repository root:

```bash
npm run validate:docs
```

This command runs accessibility lint, label consistency, mandatory TypeScript checks, Jest behavior and axe tests,
LCOV coverage thresholds, Mermaid source validation, and a production build. It does not start a browser.

Run coverage alone when iterating on shared components:

```bash
npm run docs:test:coverage
```

Coverage output is written to `docs/docusaurus/coverage/`. Global statements, branches, functions, and lines must each
remain at or above 80 percent.

## 🌐 Browser Validation

Run the production-build system-Chrome suite separately:

```bash
npm run ci:docs:setup:e2e
npm run ci:docs:test:e2e
```

Run the setup command once per environment to install Google Chrome and its system dependencies. The test command
builds the site once, generates route and Mermaid manifests, starts a non-reused loopback server, executes
representative keyboard, search, adaptive, table, and Mermaid journeys, and crawls every deployed route with axe.

Browser evidence is written to these ignored paths:

| Artifact | Content |
|--------------------------------------------------|-----------------------------------------------------|
| `build/deployed-routes.json` | Versioned deployed-route and exclusion contract |
| `build/mermaid-routes.json` | Mermaid source, route, name, and description joins |
| `test-results/browser-version.json` | Google Chrome channel and runtime version |
| `test-results/playwright-results.json` | Machine-readable Playwright results |
| `test-results/site-crawl-results.json` | Route status, axe violations, and incomplete checks |
| `playwright-report/` and `test-results/*/trace*` | Human-readable report and failure diagnostics |

Set `DOCS_E2E_PORT` to an unoccupied loopback port when 3001 is unavailable. Set `DOCS_E2E_OUTPUT_DIR` and
`DOCS_E2E_REPORT_DIR` to isolate concurrent runs. `DOCS_E2E_FAST=1` may reuse a manually started server for local
iteration, but that mode does not produce acceptance evidence.

CI retains browser screenshots, traces, and the HTML report when browser validation fails or is cancelled. CI retains
LCOV output for 30 days and uploads the `docusaurus` flag to Codecov through OIDC. Local LCOV generation and workflow
validation prove the report and upload configuration. Only a GitHub Actions run proves Codecov ingestion, the named
80 percent status, and file attribution under `docs/docusaurus/src/`.

## 📦 Build and Serve

Build and preview the production output:

```bash
npm run docs:build
npm run docs:serve
```

The Pages workflow preserves test, build, and deploy ordering. Deployment runs only after the reusable Docusaurus
quality workflow passes. A local build does not prove Pages publication. Use the successful workflow artifact,
`github-pages` environment record, and deployment URL as provider evidence.

## 🔍 Troubleshooting

| Failure | Action |
|-----------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| Missing or stale dependencies | Run `npm ci` in `docs/docusaurus` |
| TypeScript or Jest failure | Run `npm run docs:test:coverage` |
| Browser test timeout | Confirm Google Chrome is installed and set `DOCS_E2E_PORT` to an unoccupied port |
| Missing route in exhaustive crawl | Inspect `build/deployed-routes.json` and `test-results/site-crawl-results.json`; the route sets must match exactly |
| Browser artifact collision | Set unique `DOCS_E2E_PORT`, `DOCS_E2E_OUTPUT_DIR`, and `DOCS_E2E_REPORT_DIR` values for each concurrent run |
| Axe failure | Inspect the JSON result, retained screenshot, trace, incomplete attachment, and HTML report |
| Mermaid metadata failure | Inspect `build/mermaid-routes.json` and add one active non-empty title and description to each deployed Mermaid block |
| Known dependency audit findings | Review the complete-lock audit record; do not apply forced downgrades |
| Broken-anchor build warning | Repair the named source anchor without weakening strict link settings |

Image zoom, client redirects, stale-document automation, Terraform documentation drift checks, and GitHub Pages
deployment remain separate preserved capabilities. Image zoom and redirects are installed but have no active content
fixture, so configuration proves plugin loading rather than user-visible behavior. Terraform documentation drift is
advisory in both pull-request and main-branch workflows; its JSON artifact records findings without making the check a
blocking policy.

---

<!-- markdownlint-disable MD036 -->
*🤖 Crafted with precision by ✨Copilot following brilliant human instruction,
then carefully refined by our team of discerning human reviewers.*
<!-- markdownlint-enable MD036 -->
8 changes: 8 additions & 0 deletions docs/docusaurus/__tests__/BoxCard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';
import BoxCard from '../src/components/BoxCard';

describe('BoxCard', () => {
Expand Down Expand Up @@ -34,4 +35,11 @@ describe('BoxCard', () => {
const img = screen.getByRole('presentation');
expect(img.getAttribute('src')).toBe('/img/icons/test.svg');
});

it('has no detectable accessibility violations', async () => {
const { container } = render(
<BoxCard title="Test Card" links={links} description="A description" icon="/img/icons/test.svg" />,
);
expect(await axe(container)).toHaveNoViolations();
});
});
25 changes: 24 additions & 1 deletion docs/docusaurus/__tests__/CardGrid.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';
import CardGrid from '../src/components/CardGrid';

describe('CardGrid', () => {
Expand All @@ -14,6 +15,19 @@ describe('CardGrid', () => {
expect(getByText('Child B')).toBeDefined();
});

it('renders every card as a direct list item', () => {
render(
<CardGrid>
<article>Child A</article>
<article>Child B</article>
</CardGrid>,
);

const list = screen.getByRole('list');
expect(screen.getAllByRole('listitem')).toHaveLength(2);
expect(Array.from(list.children).every((child) => child.tagName === 'LI')).toBe(true);
});

it('applies three-column class by default', () => {
const { container } = render(
<CardGrid>
Expand All @@ -40,4 +54,13 @@ describe('CardGrid', () => {
);
expect(container.firstElementChild?.className).toBe('cardGridFour');
});

it('has no detectable accessibility violations', async () => {
const { container } = render(
<CardGrid>
<article>Accessible card</article>
</CardGrid>,
);
expect(await axe(container)).toHaveNoViolations();
});
});
13 changes: 10 additions & 3 deletions docs/docusaurus/__tests__/HeroSection.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';
import HeroSection from '../src/components/HeroSection';

describe('HeroSection', () => {
Expand All @@ -13,8 +14,14 @@ describe('HeroSection', () => {
expect(screen.getByText('Sub text')).toBeDefined();
});

it('renders a header element', () => {
const { container } = render(<HeroSection title="T" subtitle="S" />);
expect(container.querySelector('header')).not.toBeNull();
it('renders a section labelled by the title', () => {
render(<HeroSection title="T" subtitle="S" />);
expect(screen.getByRole('region', { name: 'T' })).toBeDefined();
expect(screen.getByRole('heading', { level: 1, name: 'T' })).toBeDefined();
});

it('has no detectable accessibility violations', async () => {
const { container } = render(<HeroSection title="Welcome" subtitle="Sub text" />);
expect(await axe(container)).toHaveNoViolations();
});
});
Loading
Loading