Skip to content

Accessibility follow-ups: insights contrast, stat grouping, lint sweep#646

Merged
kasnder merged 3 commits into
masterfrom
claude/a11y-followups-636
Jul 12, 2026
Merged

Accessibility follow-ups: insights contrast, stat grouping, lint sweep#646
kasnder merged 3 commits into
masterfrom
claude/a11y-followups-636

Conversation

@kasnder

@kasnder kasnder commented Jul 12, 2026

Copy link
Copy Markdown
Member

Refs #636

Summary

Addresses the three "Candidate follow-ups" items from the a11y tracking issue that were in scope for automated work (color contrast, stat grouping, lint triage). The remaining items (on-device TalkBack pass, live-region announcements, focus/traversal order review) are intentionally left for a follow-up, as noted in the issue.

1. Color-contrast fixes (insights hero card)

app/src/main/res/layout/item_insights_header.xml sits on the bg_insights_hero gradient (#8B0000#B71C1C#D32F2F, darkest to lightest). Contrast was measured against both the darkest stop (best case) and the lightest stop (worst case, where the text sits over #D32F2F), using the standard WCAG relative-luminance formula.

Element Before Contrast (darkest / lightest stop) After Contrast (darkest / lightest stop)
"Last 7 Days" subtitle (13sp bold, alpha 0.9) white @ 90% alpha 8.2:1 / 4.28:1 (fails 4.5:1) white @ 95% alpha 9.2:1 / 4.62:1 (passes)
Stat labels: "Tracker Hosts Contacted" / "Tracking Companies" / "Blocked" (11sp, alpha 0.85) white @ 85% alpha 7.0:1 / 3.96:1 (fails) white @ 95% alpha 9.2:1 / 4.62:1 (passes)
Protection % value (36sp bold) hardcoded #A5D6A7 6.09:1 / 3.03:1 (marginal, large-text-only) #F1F8E9 (pale green, same accent family) 9.22:1 / 4.59:1 (passes AA for all text sizes)
Stat numbers (36sp bold), "Last 7 Days" icon, Share button white, full alpha 4.98:1–10:1 unchanged unchanged

All text now clears 4.5:1 against the gradient's lightest stop, not just the 3:1 "large text" bar. The green accent on the percentage was kept (spirit of the original design) but lightened enough to pass at its actual (non-large-text-only) contrast requirement.

2. Group stat number + label for TalkBack

item_insights_header.xml: each of the three stat blocks (tracking attempts, tracker companies, protection %) is now a single focusable="true" LinearLayout container; the number and label TextViews inside are marked importantForAccessibility="no". InsightsHeaderAdapter.onBindViewHolder sets a combined contentDescription on each container (e.g. "128, Tracker Hosts Contacted"), so TalkBack reads each metric as one swipe stop instead of two.

3. Lint accessibility triage

Ran ./gradlew :app:lintGithubDebug (full report generated despite the task exiting non-zero on pre-existing, unrelated errors — see Notes). 24 ContentDescription findings triaged:

Fixed (cheap, in this PR):

  • AdapterAccess.java (access.xml ivBlock) — per-connection blocked/allowed icon now gets a contentDescription (mirrors the pattern already used for the access-log adapter in Accessibility sweep: label remaining unlabeled controls (#231) #634).
  • ActivityMain.java (actionmain.xml ivQueue / ivMetered) — "busy" and "metered network" header icons now get a contentDescription built from their existing toast strings (msg_queue, msg_metered).
  • WidgetMain.java (widgetmain.xml ivEnabled) — home-screen widget toggle now gets a state-aware contentDescription via new strings widget_toggle_enable_description / widget_toggle_disable_description.
  • 13 purely decorative icons marked importantForAccessibility="no" (each already has an adjacent text label conveying the same information): 12 icons in legend.xml, plus the onboarding illustrations in item_onboarding.xml / item_onboarding_blocking_mode.xml, and the insights "no data" empty-state icon in activity_insights.xml.

Remaining (listed, not fixed here):

  • actionmain.xml ivIcon (master rocket logo / long-press "about" trigger) — ambiguous purpose (branding + hidden gesture), needs a product decision on wording rather than a mechanical fix.
  • log.xml ivConnection / ivInteractive — lint false positive; these already get a dynamic contentDescription set in AdapterLog.java from Accessibility sweep: label remaining unlabeled controls (#231) #634, just not statically declared in XML.
  • traffic.xml ivTraffic — a live-drawn traffic-rate graph with no static text equivalent; needs a proper dynamic/summary description, out of scope for a "cheap fix".
  • All other lint categories (RtlHardcoded, UseCompoundDrawables, HardcodedText, etc.) are non-accessibility or pre-existing and out of scope for this issue.

Not in this PR (per issue, left for follow-up)

Notes

  • New strings (widget_toggle_enable_description, widget_toggle_disable_description) added to base English strings.xml only, matching the Add dynamic accessibility labels to app-list Internet toggle (#231) #625/Accessibility sweep: label remaining unlabeled controls (#231) #634 pattern; translations follow via the normal localization process.
  • ./gradlew :app:lintGithubDebug currently fails to exit 0 due to pre-existing, unrelated resource-linking/format errors in translated strings (e.g. a mistranslated @string/... reference in values-uk-rUA, and a % formatting issue in values-da-rDK) — these predate this PR and are a known symptom of the Crowdin string-recreation issue tracked separately. The lint report itself still generates correctly and was used for the triage above.
  • ./gradlew :app:compileGithubDebugJavaWithJavac passes.

🤖 Generated with Claude Code

kasnder and others added 2 commits July 12, 2026 10:32
#636)

Addresses the "Candidate follow-ups" checklist items from the a11y
tracking issue:

- Raise low-contrast white text and the hardcoded #A5D6A7 percentage
  on the insights hero gradient to meet WCAG AA (>=4.5:1) against the
  gradient's lightest stop.
- Group each stat number + label in the insights hero card behind a
  single focusable container with a combined contentDescription, so
  TalkBack announces "128, Tracker Hosts Contacted" as one unit
  instead of two separate reads.
- Triage `Accessibility` lint findings: add contentDescriptions to the
  per-app access-log status icon, the busy/metered header icons, and
  the home-screen widget toggle; mark clearly decorative illustrations
  (legend icons, onboarding art, insights empty state) as
  importantForAccessibility="no".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kasnder

kasnder commented Jul 12, 2026

Copy link
Copy Markdown
Member Author

Review finding — localizable accessibility sentence (InsightsHeaderAdapter.java): The new content descriptions assemble value, punctuation, and label directly in Java, which hard-codes English word order. Languages that place the label first or use another separator cannot translate the complete announcement. I have prepared a fix using a formatted string resource with reorderable %1$s and %2$s placeholders.

@kasnder kasnder marked this pull request as ready for review July 12, 2026 10:35
@kasnder kasnder merged commit 288a783 into master Jul 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant