Validate 4.5:1 contrast ratios for all text
epic-no-access-screen-ui-task-008 — Audit every text element on the no-access-screen-widget against the WCAG 2.2 AA 4.5:1 contrast ratio requirement. Use the contrast-safe-color-palette and design token colours. Fix any elements that do not meet the threshold. Document colour token choices in a brief inline comment.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Start by listing every Text, RichText, and SelectableText widget in the no-access-screen-widget tree. Cross-reference each against the design token palette using an online contrast checker (e.g., WebAIM) with the actual token hex values. For any failure, replace the colour assignment with the nearest passing token — prefer AppColors.textPrimary (highest contrast) for body copy and AppColors.textSecondary only where it meets the threshold. Avoid using Opacity wrappers to adjust colour — this reduces effective contrast without changing the token value and is harder to audit.
If a token does not exist that meets the threshold for a given background, escalate to the design system owner before inventing a new value. The contrast requirement applies to both light and dark ThemeData — use Theme.of(context) branches if needed. Document the final contrast ratio next to each token reference so future developers can verify at a glance without re-running the audit.
Testing Requirements
Write or extend the existing flutter_test widget test suite for 049-no-access-screen-widget. Add a test group 'contrast_audit' containing at least: (1) a test that instantiates the widget in light theme and asserts that all Text widgets use only approved design token colours (check via widget.style.color identity against AppColors constants); (2) a parallel test for dark theme. Additionally run meetsGuideline(textContrastGuideline) from package:flutter_test/flutter_test.dart against the pumped widget in both themes.
Golden tests from task-012 will provide regression coverage — ensure contrast fixes are visible in the golden baseline.
Flutter's live region (SemanticsProperties.liveRegion) announcement may be delayed or swallowed by the OS accessibility engine if the Semantics tree is not fully built when the screen mounts, causing screen-reader users to miss the denial announcement.
Mitigation & Contingency
Mitigation: Trigger the live region announcement from a post-frame callback (WidgetsBinding.addPostFrameCallback) to ensure the Semantics tree is committed before the announcement fires. Test on both VoiceOver (iOS) and TalkBack (Android) physical devices.
Contingency: If live region timing is unreliable, fall back to using SemanticsService.announce() directly in the initState post-frame callback, which provides more deterministic announcement timing.
The organisation logo may fail to load (network error, missing asset) leaving a broken image in an otherwise functional screen, degrading the professional appearance and potentially confusing users.
Mitigation & Contingency
Mitigation: Wrap the logo widget in an error builder that renders a styled fallback (organisation name text or a generic icon) when the logo asset or network image fails to load.
Contingency: If logo loading is persistently unreliable across organisations, remove the logo from the no-access screen entirely in favour of a text-only header using the organisation's display name from the design token system.