Skip to content

Latest commit

 

History

History
113 lines (94 loc) · 6.73 KB

File metadata and controls

113 lines (94 loc) · 6.73 KB

Accessibility Conformance Report

The GTFS Scorecard targets WCAG 2.2 Level AAA across the public site: the landing page (web/index.html), the single-page app (web/app/ plus web/src/app.js and web/src/styles.css), the prerendered pages generated by pipeline/src/scorecard_pipeline/render_site.py, and the standalone pages (web/subscribe.html, web/submit.html, web/try.html, web/how-to-read/, web/about/).

This report records per-criterion conformance, how the colour-selection mechanism works, and an honest list of anything still short of full AAA.

How the theme (colour-selection) mechanism works

web/src/theme.js is loaded on every page. It renders an accessible, keyboard- operable menu (a <button> with aria-haspopup/aria-expanded opening a role="menu" of role="menuitemradio" items) labelled "Theme". The choice is saved to localStorage under scorecard-theme and applied as a data-theme attribute on <html>, which overrides the CSS custom properties in both web/src/styles.css and the landing page's inline tokens.

Four options are offered: System (default; follows prefers-color-scheme), Light, High contrast, and Dark. Each theme is verified to clear AAA contrast by pipeline/scripts/check_contrast.py, which computes the WCAG 2.x relative-luminance ratio for every text/background pair the site uses and asserts >=7:1 for normal text (>=4.5:1 for large text) across all three explicit palettes. To avoid a flash of the wrong theme, each page also applies the saved data-theme inline in <head> before first paint.

The OS-following defaults are scoped to :root:not([data-theme]) in both stylesheets, so an explicit Light choice wins over an OS dark preference (the shared stylesheet lacked this guard until 2026-07; the landing page always had it).

This control is what satisfies 1.4.8's requirement for a mechanism to select foreground and background colours.

Final contrast ratios (light theme, normal text, threshold 7:1)

Computed with python3 pipeline/scripts/check_contrast.py. Text tokens were kept or darkened; the 2026-07 signage palette brightened the grounds, which raises every dark-text ratio.

Pair Ratio
--ink-soft #3d4339 on --paper #f2f3ee (app) 9.14
--ink-soft #3d4339 on --card #fbfcf8 9.89
--ink-soft #3d4339 on --paper-deep #e5e8df (fix effort) 8.22
--green #163a2c link on --paper 11.24
--green #163a2c link on --card 12.16
--green-bright #1d4633 on --card 10.31
severity --warning #6b490e text on --card 7.89
severity --info #3a4753 text on --card 9.24
.score-of board-soft #bcccbd on --board #102a20 9.10
.peer-context #d3e0d4 on --board 11.19
landing --ink-soft #3c4840 on --paper #f2f3ee 8.59
landing --ink-soft #3c4840 on --paper-2 #e5e8df 7.72
landing --green #11522a on --paper 8.32
landing --green #11522a on --paper-2 7.48
landing --rust #822c12 on --paper 8.12

The High contrast and Dark themes clear the same threshold with wider margin (typically 9:1 to 21:1); run the script for the full table.

Per-criterion conformance

Criterion Status Notes
1.4.6 Contrast (Enhanced) 7:1 MET All normal-text pairs darkened to >=7:1 (>=4.5:1 large) across all themes; checked by check_contrast.py.
1.4.8 Visual Presentation MET Prose measure capped to ~70ch (--measure); no justified text; the theme menu provides the colour-selection mechanism.
2.4.8 Location MET The filled nav stop carries aria-current="page" when it is the page being read and aria-current="true" when it is the hub of the section that page sits in, so a hub is never announced as the current page; a nav[aria-label="Breadcrumb"] on prerendered agency, program, fix, and section pages.
2.4.9 Link Purpose (Link Only) MET Bare-filename and arrow/"view" links given self-describing text or aria-label ("standards crosswalk", "the change feed", "Open transit data downloads", "Source code on GitHub").
2.4.13 Focus Appearance MET All :focus-visible outlines are >=3px solid with >=3:1 contrast; the choropleth has a real outline plus a thick dark stroke. The dark pine chrome band (header controls and footer links) rings in --amber rather than the blue --focus, which is tuned for the light page and would fall to ~1.6:1 on pine; amber clears >=9:1 on pine in every theme (check_contrast.py).
2.5.5 Target Size (Enhanced) 44x44 MET (one documented exception) Chips, filter buttons, follow/remove, selects, copy buttons, search, form controls, and dense nav/footer links are >=44px. See the choropleth exception below.
3.1.3 Unusual Words MET A glossary lives at /how-to-read/#glossary; first uses are <dfn> and linked to it.
3.1.4 Abbreviations MET First meaningful use of GTFS, GTFS-Realtime/GTFS-RT, RT, NTD, FTA, ACS, D-10, MDB, GBFS, YAML, CI, SHA-256, JSON wrapped in <abbr title="...">.
3.1.5 Reading Level MET Dense NTD, conformance, and equity copy each carry a one-line plain-language summary alongside it.

Choropleth target-size exception (2.5.5)

The US state choropleth on the directory is a progressive-enhancement convenience over the always-present, fully accessible state chip grid and agency list. Small states render below 44x44 as SVG paths; enlarging every state to 44x44 would distort the map geometry. The exception is acceptable because an equivalent control of adequate size exists: every state is also a

=44px-tall .state-chip, and the full agency list is reachable without the map. The map paths still receive a visible >=3px focus indicator and are keyboard operable.

Residual items and honest caveats

  • Choropleth cell size (2.5.5): documented exception above; the equivalent chip grid satisfies the requirement.
  • Third-party map page (/map/): the national MapLibre map is a convenience layer. Its internal canvas controls are outside our styling; the page links prominently to the accessible /agencies/ list, so no user is stranded. We do not claim AAA for the embedded map widget itself.
  • External link reading level: linked external documents (Federal Register, validator docs) are not under our control and may exceed a lower-secondary reading level; we summarize them in plain language on our own pages.
  • Legacy .grade-stamp: an unused legacy hero style remains in the stylesheet; current pages render the dark status board, whose text is a fixed light cream independent of theme.

Verifying

python3 pipeline/scripts/check_contrast.py     # contrast across all themes
node --check web/src/app.js
node --check web/src/theme.js
cd pipeline && uv run scorecard render-site     # regenerate prerendered pages