Gevell PDF Accessibility Roadmap (Q4 2026 – Q3 2027)
HECVAT question: “Can you provide a current, detailed accessibility roadmap with delivery timelines?” Internal question (Larry): “What accessibility features are we missing?”
Scope: the Gevell PDF web app itself (apps/web, served at pdf.gevell.com):
landing, sign-in, upload/dashboard, conversion progress, file detail/preview, the
remediation tools (tag editor, reading order, tab order, alt text), account/settings,
teams and tenant-admin. The quality of the documents we generate (PDF/UA, WCAG of
output HTML) is a separate track in section 5.
Honesty rule for this document: a gap is listed as Verified only where code evidence was found (file + line cited). Anything else is Suspected (code pattern suggests a risk) or Needs manual test (cannot be decided from code). Nothing here has yet been through a formal screen-reader pass — that is itself roadmap item A1.
1. Current state — what exists
1.1 Test harness
| Asset | Location | State |
|---|---|---|
test:a11y script | apps/web/package.json → playwright test --project=a11y | Exists |
| axe-core via Playwright | @axe-core/playwright in apps/web/package.json; tags wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa | Exists |
| Playwright config | apps/web/playwright.config.ts — projects smoke, a11y, mobile (iPhone 14 only) | Exists |
| a11y specs | apps/web/e2e/: home.a11y, home.keyboard.a11y (focus visible + no trap + skip link + FAQ accordion on 6 public routes), dashboard.a11y, tag-editor.a11y, crm.a11y, issues.a11y | See gaps |
| Help-site scan | .github/workflows/docs.yml runs pa11y-ci (WCAG2AA) over help.gevell.com | In CI, gating |
| Flipbook viewer | .github/workflows/test.yml (~line 180) — Playwright + axe on the shared flipbook player | In CI |
| VPAT/ACR | apps/web/VPAT.md + apps/web/vpat-evidence.json (axe run 2026-07-05 against localhost, landing page only) | Draft |
| Public statement | apps/home/src/app/accessibility/page.tsx (gevell.com/accessibility) | Exists, WCAG 2.1 AA framing |
1.2 Good patterns already in place (verified)
- Drag alternatives (2.5.7): reorder by drag is always paired with buttons or keys —
components/TabOrderBrowser.tsx:361-370(“Move … earlier/later”),components/file-detail/FlyerReadingOrder.tsx:286-300(“Move … up/down to position N” plus a polite live region at :231),packages/tag-editor-ui/src/components/TreeView.tsx(Alt+Up/Down + “Move up” buttons,role="tree"/treeitem/aria-expanded, roving tabindex),OutlineView.tsx,MillerColumns.tsx. Canvas drag inElementOverlay.tsx:146relies on those outline alternatives. - Dropzone: real
<button>s for “browse” and “upload a folder” (components/upload/DropZone.tsx:~236-243); the drop target itself is not required. - Accessible authentication (3.3.8): email and voice OTP use a single field with
autocomplete="one-time-code"+inputMode="numeric"(packages/accessible-org-ui/src/components/EmailOtpLogin.tsx:205-208,VoiceOtpLogin.tsx:213-216); password fields carryautocomplete; no CAPTCHA anywhere inapps/web; SSO and OAuth available. - Reduced motion: global
prefers-reduced-motionoverride (apps/web/src/app/globals.css:1077); toasts honor it too. - Focus: global
:focus-visiblering (globals.css:62) and a targeted fix for the shared header menu (globals.css:1089-1106). - Skip link +
<main>landmark tested byhome.keyboard.a11y.spec.ts. - Upload wizard progress:
role="progressbar"+ polite status (components/wizard/WizardStep1Upload.tsx:63,78).
2. Gap analysis
2.1 Verified gaps (code evidence)
| # | Gap | WCAG / standard | Evidence |
|---|---|---|---|
| G1 | a11y tests are not in CI, and the two app-surface specs are non-gating and unauthenticated | Process (supports all SC) | No workflow runs test:a11y (only docs.yml pa11y and the flipbook test). e2e/dashboard.a11y.spec.ts and e2e/tag-editor.a11y.spec.ts end with expect(true).toBe(true); both load without a session, so they audit a sign-in redirect / empty shell. No specs for file detail, preview, remediation tools, wizard, settings, teams, tenant-admin, auth pages. Resolved for Gevell PDF (apps/web) by #3661: a11y (apps/web) and mobile (apps/web) jobs in test.yml run on every PR that touches apps/web or its packages and on every push to main, and gate (the required check) depends on both. The expect(true) specs are deleted. Axe runs on every customer route, signed in via a mocked session, and fails on any violation not in the allowlist (entries keyed by rule, route and selector). The allowlist also fails the build on an expired entry. Runbook: docs/admin/accessibility-ci.md. Resolved for Gevell Report Card (apps/vpat) by #3675: a11y (apps/vpat) and mobile (apps/vpat) jobs, same shape, on the shared harness packages/a11y-harness. Resolved for Gevell Org Chart (apps/org-chart) by #3676: a11y (apps/org-chart) and mobile (apps/org-chart), same harness. Resolved for Gevell Audit (apps/audit) by #3674: a11y (apps/audit) and mobile (apps/audit), same harness. All four apps named in the security packets are now gated |
| G2 | VPAT is a draft: no report date, 51 _TBD_ rows, WCAG 2.1 only (no 2.2) | Procurement | apps/web/VPAT.md header “Report Date: DRAFT — pending manual review” |
| G3 | Toast notifications: success/info toasts auto-dismiss after 5 s with no pause on hover/focus; fixed w-96 (384 px) width; dismiss buttons p-0.5; each toast is role="alert" nested inside a role="status" aria-live="polite" container | 2.2.1 Timing Adjustable (risk), 1.4.10 Reflow, 2.5.8 Target Size, 4.1.3 Status Messages | packages/accessible-org-ui/src/toast/Toast.tsx:7 (AUTO_DISMISS_MS = 5000), :98 (role="alert"), :100 (w-96), :131,:175 (p-0.5); ToastProvider.tsx:75-77. “Conversion complete” (hooks/useDashboardState.ts:560) is announced only via this toast. Resolved by A4 (#3566); line numbers are as of the audit and p-0.5 had already been fixed by #3444 |
| G4 | Status changes on the dashboard are not reliably announced: live regions are mounted only while the status holds (Uploading… span exists only when status==='uploading'), and processing → completed/failed has no persistent live region | 4.1.3 Status Messages | components/dashboard/control-center/FileRow.tsx:240-247, FileCard.tsx:212-219 |
| G5 | Error detail only in a title tooltip on the Retry button (unavailable to keyboard/touch and inconsistently to AT) | 1.3.1, 3.3.1 (risk) | components/dashboard/control-center/FileRow.tsx:258 |
| G6 | Small targets: notification dismiss is p-0.5 around a 14 px icon (~18 px) next to another control; dialog close buttons are p-1 + 16 px icon (24 px — AA minimum, below the 44 px project standard) | 2.5.8 (fails AA for the ~18 px case); project 44 px rule | components/dashboard/control-center/NotificationBell.tsx:137-139; components/dashboard/TagDialog.tsx:113-116, ShareDialog.tsx:~160, components/layout/ServiceBanner.tsx:24 |
| G7 | Sub-12 px text: 59 uses of text-[9px]–text-[11px] across 23 non-admin files (e.g. FileCard.tsx Convert button text-[11px]); apps/web has no global 12 px floor | Project standard (min 12 px); 1.4.4 risk | `grep text-[(9 |
| G8 | No forced-colors / Windows High Contrast handling anywhere (no forced-colors media query in apps/web or packages/accessible-org-ui); focus indication relies on Tailwind ring (box-shadow), which forced-colors mode removes | 1.4.11, 2.4.7 in forced-colors (needs manual confirmation of impact) | repo-wide grep: zero matches for forced-colors |
| G9 | English only: one locale file, translations used in ~6 files; most UI strings hard-coded | Not a WCAG AA requirement; procurement / Title VI LEP expectation; project i18n standard | apps/web/locales/en.json only; useTranslations/useTranslation in 6 files |
| G10 | No end-user accessibility-features documentation (keyboard shortcuts for the tag editor — Alt+Up/Down etc. — screen-reader tips, how to report an a11y issue) on help.gevell.com | 3.2.6 support; procurement | docs/user/src/content/docs/ has no product-accessibility page |
| G11 | Unhedged claim on the sign-in page: tagline “WCAG 2.1 AA compliant output in seconds” violates the hedged-claims policy | Policy (docs/admin/accessibility-claims-audit.md, #3293) | apps/web/src/app/auth/login/page.tsx:40 |
| G12 | Loading skeleton fixed at 612 px wide in the compare PDF viewer | 1.4.10 Reflow (at 320 px) | components/compare/PdfViewer.tsx:28-36 (style={{ width: 612 }}) |
2.2 Suspected gaps (pattern found; confirm manually)
| # | Gap | WCAG | Why suspected |
|---|---|---|---|
| S1 | Focus Not Obscured — sticky header (@anglinai/ui CorporateHeader sticky top-0 z-50), fixed mobile bottom nav (components/dashboard/control-center/Sidebar.tsx:76), fixed bottom update banner (components/VersionChecker.tsx:100), fixed top promo banner (components/marketing/AccessUBanner.tsx:12), fixed toast stack; no scroll-padding/scroll-margin anywhere in apps/web/src | 2.4.11 (2.2 AA) | Tabbing to elements near the viewport edge can land under a fixed bar |
| S2 | Wizard progress region wraps a whole card in role="status" aria-live="polite" (WizardStep1Upload.tsx:63) — may re-announce the entire card on each progress tick | 4.1.3 (verbosity) | Container-level live region with changing percentages |
| S3 | Multiple color schemes (classic / warm / premium-dark, components/dashboard/control-center/statusStyles.ts) multiply contrast surfaces; axe specs run only the default scheme, light mode | 1.4.3, 1.4.11 | Untested variants |
| S4 | Tenant branding colors applied directly to link text (app/tenant-admin/page.tsx:92-122 style={{ color: tenant.colors.primary }}) — contrast depends on customer input | 1.4.3 | Branding page computes “effective colors”, but these links use the raw value |
| S5 | PDF preview: react-pdf canvas + text layer (components/compare/PdfViewer.tsx:109-119) exposes flat text with no page/heading structure; workspace preview disables the text layer (components/workspace/WorkspacePdfPage.tsx:58) | 1.1.1 / 1.3.1 | The accessible HTML is the equivalent, but the relationship isn’t stated to AT users |
2.3 Needs manual test (cannot decide from code)
| Area | WCAG | What to test |
|---|---|---|
Tag editor canvas (packages/tag-editor-ui), reading-order and tab-order tools end-to-end with NVDA+Firefox, JAWS+Chrome, VoiceOver+Safari | 2.1.1, 4.1.2, 1.3.2 | Complete a real remediation task keyboard-only and with a screen reader |
| Zoom 200% / 400% reflow of dashboard, file detail, tag editor | 1.4.4, 1.4.10 | 1280 px viewport at 400% = 320 CSS px |
| Text spacing override | 1.4.12 | Bookmarklet on dashboard/file detail |
| Modal dialogs (TagDialog, ShareDialog, FlyerChoiceDialog, TabOrderBrowser): focus in, trap, Esc, focus return | 2.4.3, 2.1.2 | Keyboard walk |
Consistent help: help/support link position across dashboard, file detail, tenant-admin, auth (only some routes render CCPageShell/footer) | 3.2.6 (2.2 A) | Compare the location of the Support link across page templates |
| Redundant entry: checkout, workshop registration, SSO intake, team invites | 3.3.7 (2.2 A) | Is previously entered info auto-filled or selectable within a flow? |
| Dragging: any drag-only interaction remaining (benchmark page, admin boards are internal) | 2.5.7 | Pointer-only single-click alternatives on customer-facing routes |
| Session/time limits in checkout (Stripe) and conversion polling | 2.2.1 | Warning + extension? |
| Character-key shortcuts in tag editor | 2.1.4 | Single-key shortcuts, if any, can be turned off/remapped |
| Mobile: iPad and Pixel 7 (config only emulates iPhone 14), touch targets, orientation | 1.3.4, 2.5.8 | Playwright device matrix + real devices |
| Forced-colors impact of G8 | 1.4.11, 2.4.7 | Windows High Contrast / Edge forced-colors emulation |
3. Roadmap
Priorities: P0 = blocks an honest VPAT or is a verified AA failure on a core flow; P1 = verified gap on a secondary surface or WCAG 2.2 AA new criteria; P2 = best practice / AAA / procurement nice-to-have.
Quarters: Q4 2026 = Oct–Dec 2026 … Q3 2027 = Jul–Sep 2027. Note the ADA Title II compliance date for large public entities is April 26, 2027 — customers will scrutinize our own UI hardest in Q1 2027, so P0/P1 work is front-loaded.
| ID | Item | WCAG SC | Current state | Priority | Target |
|---|---|---|---|---|---|
| A1 | Formal manual audit (keyboard + NVDA/JAWS/VoiceOver + zoom) of all customer-facing routes; findings filed as issues | All | Not done — Needs manual test | P0 | Q4 2026 |
| A2 | Wire test:a11y into CI as a gating job; authenticated Playwright fixture; make dashboard/tag-editor specs assert zero violations | Process | Done for apps/web (#3661): the a11y (apps/web) and mobile (apps/web) jobs run on every PR that touches apps/web or its packages and on every push to main, and block merges through gate; naming them as required checks is pending #3619. Authenticated fixture: a mocked Supabase session against a mocked API; the Playwright network sandbox (apps/web/e2e/support/mock-backend.ts) aborts every request that is not local or to the mocked hosts. No spec passes unconditionally: expect(true) is gone, each route must render its own data before axe runs, and an unmocked call, blocked request or app-reported error fails the test. Done for apps/vpat (#3675): a11y (apps/vpat) and mobile (apps/vpat) gate merges through gate the same way; the sandbox also answers the app’s direct PostgREST reads from seed fixtures. The harness is shared (packages/a11y-harness). Done for apps/org-chart (#3676) the same way, plus a spec for the keyboard alternative to drag-and-drop reparenting on the preview. Done for apps/audit (#3674) the same way; the sandbox also answers its direct Supabase reads, and its scan clients now take the sandbox API host from NEXT_PUBLIC_A11Y_API_URL (production still always calls api.gevell.com) | P0 | Q4 2026 |
| A3 | Add axe specs for file detail, preview, wizard, remediation tools, settings, teams, tenant-admin (all pages), auth pages; run light + dark and each color scheme | 1.4.3, 4.1.2 etc. | Automated part done for apps/web (#3661): every customer route (inventory apps/web/e2e/support/routes.ts, enforced by route-inventory.a11y.spec.ts) in light and dark, dashboard-shell routes in each color scheme, plus the 10 remediation-tool dialogs, the tools menu and the guided wizard; every route also at 375/768/412 px. Fixed in the same PR: dark-mode and badge contrast, duplicate <main> on login, tools-menu ARIA, unfocusable scroll panes, page reflow at 375 px, off-screen Download menu, dashboard file names hidden at 768 px, team tabs pushed off-screen. No WCAG (axe) exceptions; controls under the 44 px project standard are baselined in #3673 (expires 2027-03-31). Automated part done for apps/vpat (#3675): all 11 pages in light and dark (saved evaluation loaded from a seed record, auth error states, the audit trail and its commit drill-down with data loaded), and at 375/768/412 px with the primary flow (dashboard → saved report card → new-evaluation upload). Fixed in the same PR: dark-mode contrast on the hero, settings back link and password toggle; low-contrast confirm-page footnote; invalid <dl> nesting in the resource details; controls raised to 44 px. No WCAG (axe) exceptions; 44 px baseline in #3693 (expires 2027-03-31). Automated part done for apps/org-chart (#3676): every customer page (25 route states) in light and dark with a chart record loaded (editor, preview, branding, share in civic and radial, review), keyboard reparenting on the preview (move mode and after the move), and at 375/768/412 px with the primary flow (upload picker → editor → preview → Download menu). Fixed in the same PR: contrast on the marketing pricing copy, billing reference, usage and credit amounts, developer key field, review comment field and the radial error text; page-level overflow from the editor grid, dashboard status tabs and developer curl sample; the preview Download menu off-screen on phones; the side rail covering the left 64 px of every signed-in page on phones, and marketing/legal pages starting under the fixed header (both now caught by a new covered-control check); header, sidebar and dashboard row controls raised to 44 px. No WCAG (axe) exceptions; 44 px baseline in #3701 (expires 2027-03-31), editor people grid on phones in #3700 (expires 2026-12-31). Automated part done for apps/audit (#3674): all 6 pages (7 route states, including the sign-in error and callback landings) in light and dark with a credit balance and CI history loaded, plus a website scan and a PDF audit run against fixtures so both reports are audited with every collapsible section and markup viewer open and with the Download menu open; and at 375/768/412 px with the primary flow (scan a URL → report → PDF file picker). Fixed in the same PR: invalid <dl> in the report summary, a finding’s rule-reference link distinguished by colour alone, dark-mode contrast on the “Dark mode” finding badge, the pressed Show HTML toggle and the history summary labels; header, scan-form, history and report controls raised to 44 px. No WCAG (axe) exceptions; 44 px baseline for shared-package controls in #3772 (expires 2027-03-31). The manual keyboard/screen-reader pass is A1 | P0 | Q4 2026 |
| A4 | Toast rework: pause on hover/focus, no auto-dismiss for anything with an action, single polite live region (no nested alert), max-w-[calc(100vw-2rem)], 24 px+ (target 44 px) dismiss targets | 2.2.1, 1.4.10, 2.5.8, 4.1.3 | Done (#3566): countdown pauses on hover and focus-within; toasts with an action or of type error/warning never auto-dismiss; visual toasts carry no live role — the provider owns one polite role=status region plus one assertive role=alert region for errors only; max-w-[calc(100vw-2rem)]; dismiss/copy targets were already 44 px (#3444) | P0 | Q4 2026 |
| A5 | Persistent dashboard announcer: one always-mounted polite live region announcing “X is processing / ready / failed”; throttle to state transitions | 4.1.3 | Done (#3567): useAnnounce() speaks through the toast provider’s always-mounted polite region; lib/dashboard-announcements.ts diffs file statuses on every file-list update and announces bucket changes only (uploaded / processing / ready / failed), collapsing to a count above 3 files; completion is announced once via the existing toast; wizard (S2) live role moved from the card to the phase text. Needs manual SR pass under A1 | P0 | Q4 2026 |
| A6 | Replace title-only error text with visible/disclosable text linked via aria-describedby | 1.3.1, 3.3.1 | Verified (G5) | P1 | Q4 2026 |
| A7 | Target-size sweep to ≥ 24 px everywhere (AA) and 44 px on primary controls (project rule); lint rule for p-0.5/p-1 icon buttons | 2.5.8 | Verified (G6) | P1 | Q1 2027 |
| A8 | Focus-not-obscured: scroll-padding-top/bottom equal to fixed bar heights; ensure toasts/banners never cover focused elements | 2.4.11 | Suspected (S1) | P1 | Q1 2027 |
| A9 | Reflow fixes: responsive skeletons, toast width, tables → stacked layout under 640 px; verify at 400% | 1.4.10 | Partial (#3571): G12 skeleton now w-[612px] max-w-full aspect-[612/792]; G3 toast width now capped at max-w-[calc(100vw-2rem)] (done under A4, #3566). Tables step 1 (PR for #3571): all 30 rendered customer-facing tables in apps/web wrapped in TableScroller (scope: everything outside app/admin/**; excludes the unreferenced ClassicDashboard.tsx and the HTML-string report table in pipeline/page.tsx). 7 had no scroll wrapper: 5 were clipped by CCPageShell or their own overflow-hidden card, 2 forced page-level horizontal scroll. Every scroll region is now a focusable labelled group when it overflows on either axis (2.1.1), with an inset focus ring so overflow-hidden cards can’t hide it (2.4.7). Step 2 (PR for #3571): StackedTable in @accessible-org/ui collapses record-style tables into labelled cards below 640 px (real <table> kept, ARIA roles restored, data-label from headers); adopted on 14 apps/web tables (transactions ×2, budget alerts, API keys, webhook deliveries, team members + allowances, URL results, tenant-admin users/departments/domains/SSO logins, S3 + SharePoint sync events). Left scroll-in-place on purpose: comparison grids (pricing, benchmark, pipeline, remediation console ×4, font audit), the already-responsive dashboard file table, and the per-group usage matrices (tenant usage ×2, department caps/usage) whose columns are metered groups meant to be compared across a row. Step 3 (2026-09-27): manual-run 320 px sweep (tools/a11y-reflow-sweep) of 57 live public routes in light and dark found one page-scroll bug (gevell.com/why-gevell H1, fixed in #3801), one table that scrolled in an unfocusable region (orgchart /wcag, stacked in open PR #3802), and a clipped third-party API-docs button (pdf /docs/api, open). Results and the 400% manual checklist: a9-reflow-400-zoom-checklist.md. Remaining: the manual 400% pass and VoiceOver check; a CI reflow gate at 320 px (a harness VIEWPORTS entry, plus a reflow check in the nightly contrast-sweep for signed-in screens) — Needs manual test | P1 | Q1 2027 |
| A10 | Forced-colors support: @media (forced-colors: active) focus outlines (outline: 2px solid CanvasText), status badges with borders, icons with currentColor | 1.4.11, 2.4.7 | Verified absence (G8) | P1 | Q1 2027 |
| A11 | Enforce 12 px minimum text in apps/web (font-size: max(12px, 1em) floor like apps/home; replace text-[9-11px]) | 1.4.4 (risk), project rule | Verified (G7) | P1 | Q1 2027 |
| A12 | Screen-reader UX for the tag editor & reading-order/tab-order tools: documented keyboard model, aria-keyshortcuts, announcements after every reorder, SR-test scripts | 2.1.1, 4.1.2, 4.1.3 | Partially in place (1.2); Needs manual test | P1 | Q1 2027 |
| A13 | Consistent help: a Help/Support entry in the same location on every template (header or footer), incl. file detail, tenant-admin, auth | 3.2.6 | Needs manual test | P1 | Q1 2027 |
| A14 | Redundant entry review of checkout / registration / intake / invites | 3.3.7 | Needs manual test | P2 | Q2 2027 |
| A15 | Accessible authentication re-verification (OTP paste, password-manager fill, SSO) — add to E2E | 3.3.8 | Likely supports (1.2); confirm | P2 | Q1 2027 |
| A16 | Preview semantics: state in the preview region that the accessible HTML/tagged PDF is the accessible equivalent; offer “open accessible version” link adjacent | 1.1.1, 1.3.1 | Suspected (S5) | P2 | Q2 2027 |
| A17 | Tenant branding contrast: clamp tenant colors to ≥ 4.5:1 wherever they color text (reuse branding page’s effective-color logic) | 1.4.3 | Suspected (S4) | P1 | Q1 2027 |
| A18 | Fix unhedged login tagline (G11) and add CI grep to the claims-audit guard | Policy | Verified (G11) | P0 | Q4 2026 |
| A19 | Publish VPAT 2.5 (WCAG 2.2 + 508 + EN 301 549) for Gevell PDF after A1–A5, dated and signed; semi-annual refresh thereafter; ACR for each Gevell product on the same cadence | Procurement | Verified draft (G2) | P0 | Draft Q4 2026; published Q1 2027; refresh Q3 2027 |
| A20 | help.gevell.com “Accessibility features” page: keyboard shortcuts, SR tips per tool, supported AT matrix, how to report issues (accessibility@gevell.com), conformance status link | 3.2.6 support | Verified absence (G10) | P1 | Q1 2027 |
| A21 | Mobile device matrix: add iPad + Pixel 7 projects to playwright.config.ts, run test:mobile in CI | 1.3.4, 1.4.10, 2.5.8 | Config has iPhone 14 only | P1 | Q1 2027 |
| A22 | Adopt WCAG 2.2 AA as the product standard (CLAUDE.md, statement page, VPAT); axe tags already include wcag22aa | All 2.2 | Standard is 2.1 AA today | P1 | Q1 2027 |
| A23 | Internationalization: externalize strings (react-i18next/next-intl per project standard), ship Spanish UI first; lang switching; RTL-safe layout audit | 3.1.1/3.1.2 hygiene; Title VI LEP | Verified gap (G9) | P2 | Spanish Q3 2027 |
| A24 | Accessibility regression dashboard: trend axe violations per route in CI artifacts; Grafana panel | Process | Not present | P2 | Q2 2027 |
| A25 | User research: 2 sessions/quarter with disabled users (screen reader, switch/voice control, low vision) on the remediation tools | All | Not present | P2 | Q2 2027 onward |
| A26 | AAA-leaning enhancements where cheap: 2.4.13 focus appearance (3 px #046307 ring per design system), 2.3.3 animation from interactions | AAA | Partially (2 px ring today) | P2 | Q3 2027 |
4. Answers to “What accessibility features are we missing?”
In order of impact:
- Proof. We have no current, dated conformance evidence for our own app: the VPAT is a draft with 51 TBDs and the a11y test suite isn’t run in CI (G1, G2). This matters more than any single bug for procurement.
- Announcements for long-running work. Screen-reader users get a 5-second toast when a conversion finishes and live regions that appear and disappear with the status (G3, G4). A persistent, throttled announcer is needed.
- WCAG 2.2 AA items. Focus-not-obscured (S1) and target size (G6) are the likely failures; dragging (2.5.7) and accessible authentication (3.3.8) look good.
- Forced-colors / high-contrast support — none at all (G8).
- Reflow and small text — fixed-width toast/skeleton, 59 sub-12 px text uses (G7, G12).
- Documentation of accessibility features for users (G10) and a published accessibility conformance cadence.
- Languages other than English (G9).
5. Separate track — accessibility of generated output
The product’s output quality is a different promise from the UI’s accessibility and should be reported separately in questionnaires.
| ID | Item | Current state | Target |
|---|---|---|---|
| O1 | PDF/UA-1 machine validation (veraPDF) on every tagged PDF, result shown to user | In place for the tagged-PDF paths (veraPDF referenced from workers/api/src/scheduler/chunk-scheduler.ts, utils/flyer-report.ts, utils/tag-tree.ts) | Maintain; publish pass-rate metric quarterly — Q1 2027 |
| O2 | WCAG 2.1 AA automated check of generated HTML (axe) with the hedged “passed our automated checks” language | Policy in docs/admin/accessibility-claims-audit.md | Add WCAG 2.2 rule tags + corpus benchmark — Q1 2027 |
| O3 | Human-review requirements for reading order / alt text are surfaced to users (never “machine-verified accessible”) | Partially — bulk epic #3364 requires human review | Q2 2027 |
| O4 | Corpus regression benchmark (PDF/UA pass %, alt-text coverage, table header accuracy) published to customers | Internal tooling exists (tools/pdf-inventory/) | External-facing summary Q2 2027 |
| O5 | Output-format parity: EPUB/HTML output conformance statement | Not started | Q3 2027 |
6. Effort (rough)
| Bundle | Items | Engineer-days |
|---|---|---|
| Test harness & CI gating | A2, A3, A21, A24 | 6–8 |
| Manual audit (internal or external firm) | A1 | 5 internal days or ~$8–15k external |
| Announcements + toasts | A4, A5, A6 | 4–5 |
| WCAG 2.2 + visual sweep | A7, A8, A9, A10, A11, A17 | 8–10 |
| Tag editor SR hardening | A12, A16 | 5–8 |
| Docs, VPAT, policy | A13, A18, A19, A20, A22 | 5–6 |
| i18n (Spanish) | A23 | 15–20 (+ translation cost) |
7. Suggested questionnaire wording
Yes. Gevell maintains a dated accessibility roadmap for Gevell PDF, reviewed quarterly. Our product standard is WCAG 2.1 Level AA, and we are adopting WCAG 2.2 Level AA in Q1 2027. Current state: automated axe-core accessibility tests (WCAG 2.0/2.1/2.2 A and AA rules) and keyboard-navigation tests are part of our test suite; our help site is scanned against WCAG 2 AA on every change; drag-and-drop interactions have keyboard and button alternatives; sign-in supports password managers, one-time-code autofill and SSO with no cognitive tests; and the interface honors reduced-motion preferences.
Planned delivery: Q4 2026 — full manual audit with NVDA, JAWS and VoiceOver; automated accessibility testing of every authenticated page as a required check before release; improved screen-reader announcements for document-conversion progress and notifications. Q1 2027 — WCAG 2.2 AA remediation (target size, focus not obscured), Windows High Contrast / forced-colors support, 400% zoom reflow fixes, an accessibility-features guide on help.gevell.com, and a published VPAT 2.5 (WCAG 2.2, Section 508, EN 301 549). Q2–Q3 2027 — usability sessions with users with disabilities, public accessibility metrics for generated documents, Spanish-language interface, and a semi-annual VPAT refresh. Known issues are tracked and prioritized in our issue tracker; accessibility issues can be reported to accessibility@gevell.com. The roadmap document is available on request under NDA.
Do not attach this internal file as-is; send a customer-facing summary of section 3. Update the “current state” sentence as A2/A19 land — do not claim a published VPAT before A19 ships.