critical priority low complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

The portal link interactive area measures at least 44×44 logical pixels in the Flutter widget tree as confirmed by Flutter DevTools Widget Inspector
The logout button interactive area measures at least 44×44 logical pixels in the Flutter widget tree
Any additional interactive elements introduced by dependent tasks (e.g., icon buttons, secondary links) also meet the 44×44 dp minimum
The visual footprint (rendered ink/highlight area) is constrained so the touch target does not overlap adjacent interactive elements and cause accidental activation
On iOS, touch targets satisfy the Apple HIG minimum of 44×44 pt, which equals 44×44 dp at 1× scale — no platform-specific branching is required
On Android, touch targets satisfy the Material Design minimum of 48×48 dp — if a target is exactly 44 dp it must be wrapped with an additional 2 dp padding per side OR the MaterialTapTargetSize.padded strategy is applied via Theme
Tapping the dead-zone area surrounding a small label (e.g., a narrow text link) correctly triggers the associated action without requiring precise tap accuracy
The widget continues to pass meetsGuideline(androidTapTargetGuideline) and meetsGuideline(iOSTapTargetGuideline) in flutter_test
No visual regression: the rendered label size and position are unchanged; only the invisible tap area is enlarged

Technical Requirements

frameworks
Flutter
performance requirements
Touch target wrappers must not introduce additional layout passes — use constraints on the existing widget rather than nested Stacks
ui components
SizedBox with constraints (minWidth: 44, minHeight: 44)
InkWell or GestureDetector with HitTestBehavior.opaque
MaterialTapTargetSize.padded via ButtonStyle for ElevatedButton/TextButton
Semantics widget to preserve label scope for accessibility tree

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Prefer ButtonStyle.minimumSize (Size(44, 44)) for ElevatedButton and TextButton components — this is the idiomatic Flutter approach and avoids adding extra wrapper widgets. For text-only links rendered as a TextSpan inside a Text.rich, wrap the tappable span in a GestureDetector with a SizedBox(width: double.infinity, height: 44) or use a ConstrainedBox. Do not use Padding alone to increase tap area — padding does not contribute to hit-testing on all widget types. Test on a physical device or emulator at 1× DPI after implementing to confirm the tap zone feels comfortable.

Note that the 44 dp minimum applies to both width AND height independently — a wide but short button (e.g., 200×36 dp) still fails and needs the height increased. Use the accessible-touch-target-wrapper pattern as a shared widget if multiple locations across the app need this treatment, to avoid repeating the boilerplate.

Testing Requirements

Extend the widget test suite with a group 'touch_targets'. For each interactive element: (1) pump the widget, (2) use tester.getSize(find.byKey(Key('portal-link-touch-target'))) and assert width >= 44 and height >= 44, (3) repeat for the logout button. Also call meetsGuideline(androidTapTargetGuideline) and meetsGuideline(iOSTapTargetGuideline) on the full widget. Integration: tap the portal link touch zone at its corner coordinate and assert the url_launcher mock receives the correct URL.

Tap the logout button at the edge and assert the sign-out BLoC event fires.

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.