docs(a11y): add WCAG/POUR grounding, colour contrast, manual-test sections#3443
Merged
Conversation
…tions Follow-up to #3289 closing five gaps surfaced by reviewing our doc against the WCAG/POUR-grounded coverage at accessibilitychecker.org. Our existing doc is materially deeper than that article on Qt-specific patterns (live value announcements, throttling, custom-painted widgets, lint mechanics), but it was missing five concrete things every Qt-desktop a11y guide should have. All five land as additions; no existing section is changed. 1. "What we're following" — three-line POUR (Perceivable / Operable / Understandable / Robust) grounding right after the Background. Cites WCAG 2.1 quick-ref. Future contributors asking "why does this rule exist" now get "WCAG 2.1 says so" rather than "because the project says so." 2. Form-field instructions — added to the existing "Accessible description" bullet. For QSpinBox / QLineEdit / QComboBox, the accessible name answers "what is this?" and the description answers "what do I type?". Tooltips don't reach the screen reader; the description does — that's the right home for input semantics. 3. Keyboard-only test — added to the existing "Tab focus" bullet. Tab to the widget from a sibling, activate with Space/Return, no mouse. If you can't reach it the focus policy is wrong; if Space/Return doesn't fire it the widget needs a keyPressEvent (or it should have been a QPushButton — links to the QLabel anti-pattern section). 4. Colour contrast — new section between "Live value updates" and "Custom-painted widgets." WCAG 4.5:1 normal text / 3:1 large text and non-text components. Cites #3441 (Reboot Radio button) as the canonical disabled-state-blends-into-background case so future readers see why the section exists. WebAIM Contrast Checker + the OS-level "Increase Contrast" mode as the practical verification workflow. 5. Manual verification — new section between "Suppressing the lint" and "CI enforcement." Names the three platform screen readers (VoiceOver, NVDA/Narrator, Orca) with the actual keystrokes to launch them, and tells contributors to walk the widget the way a screen-reader user would. Lint = static; this = real test. Explicit that the lint catches what it can detect statically and nothing more. Doc grows 166 → 254 lines (+88). Section order flows motivation → grounding → patterns → escape valves → verification → mechanism. Principle XI.
Self-review of #3443 surfaced five issues; all five addressed here. 1. Colour contrast section now matches the theme-tokens-first policy established in #3441 / #3446 (instead of contradicting it). The earlier text read "pick concrete hex values that hit the WCAG ratio against the real surrounding background; reserve the dimmest theme tokens for genuinely decorative state where invisibility is the intent" — which advocated hex over tokens for disabled state and would have read as policy by future contributors. The actual policy from the #3441/#3446 thread: theme tokens are first choice; hex is a valid intermediate state when no semantic token exists, with a follow-up issue to add the proper token (mirroring how #3441 + #3446 played out). Adds the three-step rule of thumb (existing token for this widget × state? token for similar widget? hex + issue?) and cites #3441's merge commit and #3446's open status as the canonical example. 2. POUR Operable softened from overclaim to aspirational. Was: "Operable — keyboard focus and activation paths for every interactive widget; no mouse-only interactions." That's false today — CHAIN drag-drop, frequency drag, drag-to-pan, custom panadapter handlers are all mouse-only. Replaced with "every interactive widget reachable by keyboard, with a non-mouse activation path. (AetherSDR still has surfaces that are mouse-only today — [list] — which we're working toward; new code shouldn't add to the list.)" 3. Tooltip-vs-description claim corrected. Was: "right place to put input semantics, not in a separate tooltip the screen reader can't reach." Qt does expose tooltips to AT via QAccessible::Help — they reach screen readers as help text, fired on explicit request. The argument that descriptions are the better home for input semantics still stands, but now states the actual reason (descriptions are read automatically as part of widget identity; tooltips are help-text-on-request). 4. NVDA "say more" mode reference replaced with neutral language. "Say more" isn't a standard NVDA mode name (it may be confused with VoiceOver's "speak more"). Now says "in verbose announcement modes" without committing to a specific NVDA mode label. 5. WebAIM Contrast Checker workflow fixed. The previous text said "take a screenshot of the widget against its real background and drop it into the WebAIM Contrast Checker" — but that tool takes two hex codes, not an image. Reworded to mention the eyedropper step (Digital Color Meter on macOS, PowerToys Color Picker on Windows, gpick/gcolor3 on Linux) and added a pointer to Colour Contrast Analyser (TPGi) for a one-step screenshot-to-ratio workflow with integrated eyedropper. Doc grows 254 → 286 lines net (+74 modifications inside the existing +89 from the original commit). Section structure unchanged. Principle XI.
292cddf to
2a519ba
Compare
…tters
The per-method opt-out example showed the // a11y-check: skip comment on
the line above the method:
// a11y-check: skip
void FooWidget::setInternalCache(float v) { ... }
But tools/check_a11y.py:200-203 only inspects (a) the method definition
line itself and (b) the body between { and }. A comment on the line above
the method is invisible to it, so a contributor copying the example
as-written would still get a value-method-missing-a11y-update warning.
The prose immediately above the example already says correctly "on the
method line or anywhere in its body" — only the code example was wrong.
Replaced with two valid placements (definition line + body line) and
explicit "checker only reads those two locations" so future readers see
why the line above doesn't work.
Principle XI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #3289 closing five gaps surfaced by reviewing
docs/a11y.mdagainst the WCAG/POUR-grounded coverage at accessibilitychecker.org/blog/a11y/.Our existing doc is materially deeper than that article on Qt-specific patterns (live value announcements, throttling, custom-painted widgets, lint mechanics) — but it was missing five concrete things every Qt-desktop a11y guide should have. All five land as pure additions; no existing section is modified.
What's added
"What we're following" section — three-line POUR (Perceivable / Operable / Understandable / Robust) grounding right after the Background. Cites WCAG 2.1 quick-ref. Future contributors asking "why does this rule exist" now get "WCAG 2.1 says so" rather than "because the project says so."
Form-field instructions guidance — added to the existing "Accessible description" bullet. For
QSpinBox/QLineEdit/QComboBox, the accessible name answers "what is this?" and the description answers "what do I type?". Tooltips don't reach the screen reader; the description does.Keyboard-only test — added to the existing "Tab focus" bullet. Tab to the widget from a sibling, activate with
Space/Return, no mouse. Links to the existing QLabel anti-pattern section as the structural escape.Colour contrast section — between "Live value updates" and "Custom-painted widgets." WCAG 4.5:1 normal text / 3:1 large text and non-text components. Cites fix(radio): make disabled Reboot Radio button legible instead of near-invisible (#3334) #3441 (Reboot Radio button) as the canonical disabled-state-blends-into-background case so future readers see why the section exists. WebAIM Contrast Checker + the OS-level "Increase Contrast" mode as the practical verification workflow.
Manual verification section — between "Suppressing the lint" and "CI enforcement." Names the three platform screen readers (VoiceOver, NVDA / Narrator, Orca) with the actual keystrokes to launch them. Lint = static; this = real test. Explicit that "the lint catches what it can detect statically — this is the real test."
Doc shape
Section order now flows: motivation → grounding → patterns → escape valves → verification → mechanism.
What I didn't import from the article
Test plan
docs/a11y.mdparses as valid markdown (9##sections, no orphaned formatting).tools/check_a11y.pyand the workflow are untouched, so no CI behaviour change.Refs #3288 #3289