critical priority low complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

The denial headline Text widget is wrapped in Semantics(header: true) so TalkBack/VoiceOver announces it as a heading
The logo Semantics node has a non-empty label and does not have header: true
The admin portal link Semantics node has a descriptive label and link role (or button role if styled as a button)
The logout button Semantics node has a descriptive label and button role
The logical focus/traversal order in the semantics tree is: logo → heading → explanation text → portal link → logout button
No interactive element has an empty or generic semantics label (e.g. 'Button', 'Image')
No decorative-only elements (dividers, background shapes) are exposed to the semantics tree (use ExcludeSemantics or Semantics(excludeSemantics: true))
Running flutter test with semanticsEnabled: true produces no semantics-related assertion failures
Manual VoiceOver (iOS) and TalkBack (Android) audit confirms correct announcement order and heading level
All WCAG 2.2 AA success criteria 1.3.1 (Info and Relationships), 1.3.2 (Meaningful Sequence), and 4.1.2 (Name, Role, Value) are satisfied

Technical Requirements

frameworks
Flutter
performance requirements
Semantics widgets must not introduce additional layout passes — use merge-based Semantics where appropriate
ui components
Semantics(header: true) wrapping the denial headline
Semantics(label: '...') on logo, link, and button elements
ExcludeSemantics on purely decorative widgets
MergeSemantics where a label + child widget should be announced together

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use tester.ensureSemantics() at the top of each semantics test to enable the semantics tree. To verify traversal order, retrieve the root SemanticsNode and walk its children in depth-first order, comparing labels against the expected sequence. Use Semantics(sortKey: OrdinalSortKey(n)) if the default DOM order does not match the desired focus order — but prefer correct widget ordering in the Column first before resorting to sort keys. Check that MergeSemantics is used around the portal link if its visual presentation combines an icon and label text, to prevent the screen reader from announcing them separately.

Document the final semantics tree shape in a code comment at the top of the widget file for future maintainers.

Testing Requirements

Write widget tests using flutter_test with semanticsEnabled: true (via WidgetTester.ensureSemantics()). Test 1: obtain the SemanticsNode tree and assert the heading node exists with isHeader: true. Test 2: traverse all SemanticsNodes and assert no interactive node has an empty label. Test 3: assert the traversal order of SemanticsNodes matches the expected sequence (logo, heading, explanation, link, button).

Test 4: assert decorative elements are absent from the semantics tree using findsNothing on their expected SemanticsNodes. Perform a manual test pass with VoiceOver on a physical iOS device and document results in a test report comment.

Component
No-Access Screen
ui low
Epic Risks (2)
medium impact medium prob technical

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.

low impact low prob scope

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.