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.
ten9876
pushed a commit
that referenced
this pull request
Jun 8, 2026
Release-prep for **v26.6.2**, a consolidated release rolling up the 26.6.1 feature wave, the v26.6.1.1 hotfixes, and new work through #3443. Bumps version 26.6.1.1 → 26.6.2 in `CMakeLists.txt` and the `README.md` banner, and prepends the v26.6.2 `CHANGELOG.md` entry. No code changes. Headline content: runtime theming system, three HID controller classes (StreamDeck+, Ulanzi Dial, Icom RC-28), the packet-radio suite (KISS-over-TCP TNC #3279, connected-mode BBS terminal #3381, PMS mailbox), and the Microsoft Store MSIX packaging path (#3178/#3205/#3225/#3281). 💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat Co-authored-by: Codex <noreply@openai.com>
This was referenced Jun 10, 2026
nigelfenton
added a commit
to nigelfenton/AetherSDR
that referenced
this pull request
Jun 10, 2026
Adds a "Scale text" dropdown (8-14 pt) to the per-pan Display overlay menu, mirroring the aethersdr#1390 grid-spacing pattern: per-pan persisted setting (DisplayFreqScaleFontPt), signal wiring, state sync on menu open, and Reset-defaults support. The fixed FREQ_SCALE_H strip height becomes freqScaleH(), derived from the configured font at all 28 layout/hit-test sites, so larger labels get a taller strip instead of clipped ascenders (never below the existing 20 px at the 8 pt default). drawFreqScale() also gains a right-column guard: ruler labels are clamped/skipped clear of the dBm / waterfall-time scale column, fixing a collision that existed marginally at 8 pt and became obvious at 14. Reported by KN7K via Discord; first concrete item under the aethersdr#3443 accessibility initiative. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jensenpat
pushed a commit
that referenced
this pull request
Jun 11, 2026
Adds a "Scale text" dropdown (8-14 pt) to the per-pan Display overlay menu, mirroring the #1390 grid-spacing pattern: per-pan persisted setting (DisplayFreqScaleFontPt), signal wiring, state sync on menu open, and Reset-defaults support. The fixed FREQ_SCALE_H strip height becomes freqScaleH(), derived from the configured font at all 28 layout/hit-test sites, so larger labels get a taller strip instead of clipped ascenders (never below the existing 20 px at the 8 pt default). drawFreqScale() also gains a right-column guard: ruler labels are clamped/skipped clear of the dBm / waterfall-time scale column, fixing a collision that existed marginally at 8 pt and became obvious at 14. Reported by KN7K via Discord; first concrete item under the #3443 accessibility initiative. Squashed-from: #3503 Co-authored-by: Nigel Fenton <2791007+nigelfenton@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pat Jensen <jensenpat@users.noreply.github.com>
G6PWY-Chris
pushed a commit
to G6PWY-Chris/AetherSDR
that referenced
this pull request
Jun 22, 2026
…tions (aethersdr#3443) ## Summary Follow-up to aethersdr#3289 closing five gaps surfaced by reviewing `docs/a11y.md` against the WCAG/POUR-grounded coverage at [accessibilitychecker.org/blog/a11y/](https://www.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 1. **"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."* 2. **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. 3. **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. 4. **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 aethersdr#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 section** — between "Suppressing the lint" and "CI enforcement." Names the three platform screen readers ([VoiceOver](https://www.apple.com/accessibility/), [NVDA](https://www.nvaccess.org/) / Narrator, [Orca](https://help.gnome.org/users/orca/stable/)) 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 | | Before | After | |---|---|---| | Lines | 166 | 254 | | Sections | 7 | 9 | Section order now flows: motivation → grounding → patterns → escape valves → verification → mechanism. ## What I *didn't* import from the article - **Legal framing** ("legally mandated… legal action or fines"). Out of voice. AetherSDR's a11y motivation is "a blind ham operator filed aethersdr#3288 and we want AetherSDR usable for him" — that's a stronger frame than fear of lawsuits. - **POUR as the organizing structure of the whole doc**. The article is structured around POUR; we'd lose clarity if we reshuffled our concrete Qt sections under POUR headings. Mention WCAG/POUR once near the top, keep our pragmatic structure. - **Alt text on images / captions on video**. We have ~zero of either in AetherSDR — no help text videos, the only "images" are panadapter renders (covered by our custom-painted-widgets section). ## Test plan - [x] `docs/a11y.md` parses as valid markdown (9 `##` sections, no orphaned formatting). - [x] Links checked: WCAG 2.1 quickref, WebAIM contrast checker + articles/contrast, NVDA, Apple Accessibility, Orca docs — all resolve. - [x] No code changes — `tools/check_a11y.py` and the workflow are untouched, so no CI behaviour change. Refs aethersdr#3288 aethersdr#3289
G6PWY-Chris
pushed a commit
to G6PWY-Chris/AetherSDR
that referenced
this pull request
Jun 22, 2026
Release-prep for **v26.6.2**, a consolidated release rolling up the 26.6.1 feature wave, the v26.6.1.1 hotfixes, and new work through aethersdr#3443. Bumps version 26.6.1.1 → 26.6.2 in `CMakeLists.txt` and the `README.md` banner, and prepends the v26.6.2 `CHANGELOG.md` entry. No code changes. Headline content: runtime theming system, three HID controller classes (StreamDeck+, Ulanzi Dial, Icom RC-28), the packet-radio suite (KISS-over-TCP TNC aethersdr#3279, connected-mode BBS terminal aethersdr#3381, PMS mailbox), and the Microsoft Store MSIX packaging path (aethersdr#3178/aethersdr#3205/aethersdr#3225/aethersdr#3281). 💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat Co-authored-by: Codex <noreply@openai.com>
G6PWY-Chris
pushed a commit
to G6PWY-Chris/AetherSDR
that referenced
this pull request
Jun 22, 2026
Adds a "Scale text" dropdown (8-14 pt) to the per-pan Display overlay menu, mirroring the aethersdr#1390 grid-spacing pattern: per-pan persisted setting (DisplayFreqScaleFontPt), signal wiring, state sync on menu open, and Reset-defaults support. The fixed FREQ_SCALE_H strip height becomes freqScaleH(), derived from the configured font at all 28 layout/hit-test sites, so larger labels get a taller strip instead of clipped ascenders (never below the existing 20 px at the 8 pt default). drawFreqScale() also gains a right-column guard: ruler labels are clamped/skipped clear of the dBm / waterfall-time scale column, fixing a collision that existed marginally at 8 pt and became obvious at 14. Reported by KN7K via Discord; first concrete item under the aethersdr#3443 accessibility initiative. Squashed-from: aethersdr#3503 Co-authored-by: Nigel Fenton <2791007+nigelfenton@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pat Jensen <jensenpat@users.noreply.github.com>
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