finos / finos/architecture-as-code
Accessibility: WCAG 2.2 AA audit findings for CALM Hub UI
- Dominant language
- TypeScript
- Stars
- 399
- Forks
- 138
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 37
Description
## Bug Report
WCAG 2.2 AA audit of CALM Hub UI. Confirmed failures at **Level A and AA**. No a11y linting or CI gate currently exists.
### What was run
- `eslint-plugin-jsx-a11y` 6.10.2 (`flatConfigs.recommended`) over `calm-hub-ui/src/**/*.tsx`, tests excluded.
- Playwright 1.62 (Chromium, 1440×900) + `axe-core` 4.12.1 against the dev server, tags `wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa`, routes `#/`, `#/namespace/finos`, `#/domain/security`, `#/admin/entitlements`. API stubbed at the network layer — no backend needed.
- Both themes (`data-theme=light|dark`).
### Findings
**1. Contrast below AA — both themes (1.4.3, AA)** — same three elements fail in each theme; AA requires 4.5:1.
| Theme | Ratio | Colours | Element |
|---|---|---|---|
| light | **2.40:1** | `#9aa6b8` on `#fcfcfd` | `.text-[10px].uppercase.tracking-[0.1em]` section label |
| light | **2.46:1** | `#9aa6b8` on `#ffffff` | explore-rail heading (`NAMESPACES` / `CONTROL DOMAINS`) |
| light | **4.23:1** | `#64748b` on `#eef2f7` | count badge on namespace links |
| dark | **3.07:1** | `#64748b` on `#1e293b` | same section label |
| dark | **3.75:1** | `#64748b` on `#0f172a` | same explore-rail heading |
| dark | **3.38:1** | `#8a94a6` on `#334155` | same count badge |
**2. Link distinguished by colour alone — dark theme only (1.4.1, A)** — `link-in-text-block` on `.hover:underline`. Not reproducible in light theme.
**3. Keyboard-unreachable interactive elements (2.1.1, A)** — 17 static hits (9× `no-static-element-interactions`, 8× `click-events-have-key-events`):
| File | Line |
|---|---|
| `hub/components/value-table/ValueTable.tsx` | 24 |
| `visualizer/components/reactflow/CustomNode.tsx` | 159, 324 |
| `visualizer/components/reactflow/controls-panel/ControlCard.tsx` | 9, 94 |
| `visualizer/components/reactflow/controls-panel/ControlRequirementItem.tsx` | 146 |
| `visualizer/components/reactflow/flows-panel/FlowTransitionItem.tsx` | 7 |
| `visualizer/components/reactflow/edge-components/EdgeBadge.tsx` | 12 |
| `visualizer/components/reactflow/PatternVisualizer.tsx` | 26 |
| `visualizer/components/reactflow/ReactFlowVisualizer.tsx` | 23 |
| `visualizer/components/reactflow/MetadataPanel.tsx` | 138 |
Confirmed genuine — `ValueTable.tsx:24`, the namespace/pattern picker:
```tsx
```
No `role`, no `tabIndex`, no key handler, no alternative keyboard path in the component.
The other 16 need per-component triage, not bulk treatment: the linter also flags components whose keyboard path lives on a sibling (the search bars are operable via `ExplorerSearch.tsx:67-85`).
**4. Combobox incomplete — silent to screen readers (4.1.2, A)** — `ExplorerSearch.tsx`, `GlobalSearchBar.tsx`, `IntroSearchBar.tsx` all declare `role="combobox"` + `role="listbox"` but have **no `aria-activedescendant`**: focus stays on the input while `selectedIndex` moves the highlight, so nothing is announced as the user arrows through results. Also no `aria-controls` (listbox has no `id`), and `` nested in group `
**5. `autoFocus` (advisory)** — `IntroSearchBar.tsx:174`, `SearchBar.tsx:80`.
### Passed
`target-size` (2.5.8, new in 2.2), `button-name`, `link-name`, `image-alt`, `label`.
### Not assessed — needs manual/AT testing
- **2.5.7 Dragging Movements (AA, new in 2.2)** — reactflow node drag (`node-position-service.tsx`, `MetadataPanel.tsx:138`); no single-pointer alternative found.
- **2.4.11 Focus Not Obscured (AA, new in 2.2)** — sticky navbar over drawers/sheets.
- **2.4.1 Bypass Blocks (A)** — axe returned `incomplete`.
- **1.1.1 / 1.3.1** — no text alternative for the architecture graph.
- Screen-reader pass (NVDA / VoiceOver).
### Environment
`main` @ `588d3dcc`, Node 26.4.0, npm workspaces.
Contributor guide
Research direction
Start with the confirmed ValueTable.tsx:24 keyboard issue, then review the listed ReactFlow components and the three search bars. Re-run the stated eslint-plugin-jsx-a11y and Playwright/axe-core checks against the listed routes and both themes. Done means confirmed findings are addressed and the audit reports no remaining targeted failures; manual and screen-reader checks remain separately noted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, typescript
- Domain
- accessibility, frontend, testing-qa
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100