mirror of
https://github.com/coder/coder.git
synced 2026-06-03 21:18:24 +00:00
4e2640e506
## Summary Targeted WCAG 2.1 AA accessibility remediation — continuation of #22673 — addressing remaining semantic, landmark, and tooling gaps identified in the frontend accessibility review. ### Changes #### Document semantics (WCAG 3.1.1) - **`site/index.html`**: Added `<html lang="en">` root wrapper so screen readers and browser features correctly identify the document language. #### Landmark & bypass (WCAG 1.3.1, 2.4.1) - **`DashboardLayout.tsx`**: Replaced `<div id="main-content">` with `<main id="main-content">` so assistive technology exposes a proper main landmark and the skip link targets a semantic region. #### Table header relationships (WCAG 1.3.1) - **`Table.tsx`**: `TableHead` now renders `scope="col"` by default (overridable via prop), giving data cells an explicit header relationship. #### Semantic interactive controls (WCAG 2.1.1, 4.1.2) - **`AuditLogRow.tsx`**: Replaced `<div role="button" tabIndex={0}>` with native `<button type="button">`, removing the manual keyboard handler (native button provides Enter/Space for free). - **`Autocomplete.tsx`**: Replaced clear `<span role="button" tabIndex={0}>` with native `<button type="button" aria-label="Clear selection">`. #### Reduced motion (WCAG 2.3.3 best practice) - **`index.css`**: Added global `@media (prefers-reduced-motion: reduce)` block that suppresses non-essential animations and transitions. #### Accessibility regression tooling - **Storybook**: Added `@storybook/addon-a11y` (version-matched to existing Storybook 10.x). - **vitest-axe**: Added `vitest-axe` with setup wiring and an exemplar `Table.axe.test.tsx` that runs axe-core assertions in vitest. ### Test plan - 12 new/updated tests pass across 5 test files: - `DashboardLayout.test.tsx` — main landmark + skip link behavior - `Table.test.tsx` — scope default + override - `Table.axe.test.tsx` — axe-core violation scan - `AuditPage.test.tsx` — keyboard toggle with native button - `Autocomplete.test.tsx` — clear control semantics - `pnpm lint` clean (biome, TypeScript, circular deps) - Manual keyboard traversal: skip link → main content, audit row toggle, autocomplete clear
66 lines
2.4 KiB
HTML
66 lines
2.4 KiB
HTML
<!doctype html>
|
|
|
|
<!--
|
|
-####### +######- ########+ ########## ########+. ###########
|
|
+#####--###### +#####--#####+ ############ ########## ####+++#####- ###########
|
|
####- -#### ####- ##### #### ####+ #### #### .#### ###########
|
|
.#### #### #### #### #### ######### ####...+##+ ###########
|
|
####. .#### #### +#### #### +#### #### ####+####### ###########
|
|
#####- -##### ######..###### ############- ########## #### ##### ###########
|
|
.########+ -########. #########+ ########## #### .#### ###########
|
|
-->
|
|
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>Coder</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<meta name="theme-color" content="#17172E" />
|
|
<meta name="application-name" content="{{ .ApplicationName }}" />
|
|
<meta property="og:type" content="website" />
|
|
<meta property="csrf-token" content="{{ .CSRF.Token }}" />
|
|
<meta property="build-info" content="{{ .BuildInfo }}" />
|
|
<meta property="user" content="{{ .User }}" />
|
|
<meta property="entitlements" content="{{ .Entitlements }}" />
|
|
<meta property="appearance" content="{{ .Appearance }}" />
|
|
<meta property="userAppearance" content="{{ .UserAppearance }}" />
|
|
<meta property="experiments" content="{{ .Experiments }}" />
|
|
<meta property="regions" content="{{ .Regions }}" />
|
|
<meta property="docs-url" content="{{ .DocsURL }}" />
|
|
<meta property="logo-url" content="{{ .LogoURL }}" />
|
|
<meta property="tasks-tab-visible" content="{{ .TasksTabVisible }}" />
|
|
<meta property="agents-tab-visible" content="{{ .AgentsTabVisible }}" />
|
|
<meta property="permissions" content="{{ .Permissions }}" />
|
|
<meta property="organizations" content="{{ .Organizations }}" />
|
|
<link
|
|
rel="alternate icon"
|
|
type="image/png"
|
|
href="/favicons/favicon-light.png"
|
|
media="(prefers-color-scheme: dark)"
|
|
/>
|
|
<link
|
|
rel="icon"
|
|
type="image/svg+xml"
|
|
href="/favicons/favicon-light.svg"
|
|
media="(prefers-color-scheme: dark)"
|
|
/>
|
|
<link
|
|
rel="alternate icon"
|
|
type="image/png"
|
|
href="/favicons/favicon-dark.png"
|
|
media="(prefers-color-scheme: light)"
|
|
/>
|
|
<link
|
|
rel="icon"
|
|
type="image/svg+xml"
|
|
href="/favicons/favicon-dark.svg"
|
|
media="(prefers-color-scheme: light)"
|
|
/>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="./src/index.tsx"></script>
|
|
</body>
|
|
</html>
|