critical priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

Every interactive widget in all six components has an explicit semanticsLabel — no interactive node in the semantics tree has a null or empty label
All buttons have Semantics.button: true or equivalent SemanticsRole
All toggle controls (ViewToggleButton, availability selector) expose toggled state via Semantics.toggled or checked properties so screen readers announce 'on/off' or 'selected/unselected'
MapMarkerWidget has a label describing the mentor it represents (e.g., 'Mentor [name], available, tap to view details') and is marked as a button
MentorInfoPopup modal/bottom sheet correctly traps focus within itself when open; focus does not escape to background elements
When MentorInfoPopup closes, focus returns to the MapMarkerWidget that triggered it
MapFilterPanel bottom sheet traps focus when open and returns focus to filter toggle button when dismissed
Flutter semantics tree dump (debugDumpSemanticsTree) shows zero nodes with label 'null' or nodes with hasAction but no label
All six components pass automated accessibility checks with no errors from Flutter's AccessibilityGuideline checks (meetsGuideline(androidTapTargetGuideline), meetsGuideline(iOSTapTargetGuideline), meetsGuideline(labeledTapTargetGuideline))
WCAG 2.2 AA colour contrast verified for all text and icon elements across all six components using design tokens

Technical Requirements

frameworks
Flutter
BLoC
data models
PeerMentor
FilterState
AvailabilityStatus
performance requirements
Semantics annotation must not introduce additional build cost — use Semantics widget declaratively, not imperatively in build loops
security requirements
Semantics labels must not expose sensitive mentor data beyond name and availability
ui components
MapViewScreen (239-map-view-screen)
MapMarkerWidget (240-map-marker-widget)
MentorInfoPopup (241-mentor-info-popup)
MapFilterPanel (242-map-filter-panel)
ViewToggleButton (243-view-toggle-button)
MentorListFallback (244-mentor-list-fallback)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Start by running flutter test --plain-name 'semantics' with SemanticsHandle to enumerate all current annotations, then work component by component. For modals and bottom sheets, use FocusTrap widget (available in Flutter 3.x) or implement manually with FocusScope. Label dynamic content using string interpolation directly in Semantics: Semantics(label: '${mentor.name}, ${availabilityLabel}, tap to view details'). For toggle buttons use Semantics(toggled: isToggled, label: 'List view').

Do not rely on child widget text as the semantic label for complex interactive widgets — always provide an explicit label. For the map canvas ExcludeSemantics wrapping (from task-011), ensure the exclusion is applied here too for consistency. Document the full semantics annotation pattern in a brief inline comment per component so future contributors know why Semantics wrappers exist.

Testing Requirements

Write widget tests for each of the six components using tester.getSemantics() and expect(semantics, ...) matchers to assert labels, roles, and actions. Use expectLater(tester, meetsGuideline(labeledTapTargetGuideline)) and meetsGuideline(iOSTapTargetGuideline) in all six component tests. Write a focused semantics tree snapshot test that dumps the tree for MapViewScreen and asserts no null-label interactive nodes exist. Test focus trap in MentorInfoPopup: open popup, send tab key events, assert focus does not leave the popup subtree.

Test focus restoration on close.

Component
Map View Screen
ui high
Epic Risks (3)
high impact high prob technical

Flutter's map canvas (flutter_map) does not natively support semantic focus traversal for screen readers, meaning map markers may be entirely invisible to VoiceOver/TalkBack users. If the accessible list fallback is not treated as a first-class view, screen reader users will have no access to the feature.

Mitigation & Contingency

Mitigation: From sprint 1, treat mentor-list-fallback as a fully featured primary view, not an afterthought. Implement and test it in parallel with the map canvas. Make the view-toggle-button keyboard focusable and announced on every screen state. Conduct VoiceOver testing on device before submitting each PR touching UI components.

Contingency: If map canvas accessibility cannot be achieved for marker focus traversal, make the view-toggle-button the default focus target on screen load for VoiceOver users (detected via screen-reader-detection-service) so they are immediately directed to the list fallback without needing to discover the toggle.

medium impact medium prob technical

The mentor-info-popup must occupy no more than 40% of visible map area on small screens. On devices with screen heights under 667px (iPhone SE), overlapping with the filter panel or obscuring most of the map could severely degrade usability.

Mitigation & Contingency

Mitigation: Implement the popup as a bottom sheet capped at 40% of screen height with a ScrollView for overflow content. Test on iPhone SE (375x667pt) and the smallest commonly used Android form factor in the device lab. Define max-height as a percentage constant in location-privacy-config or design tokens.

Contingency: If the popup cannot fit all required fields within 40% height on smallest targets, truncate assigned contact count and certification badge to icons-only in the compact view, with a 'View Profile' button always visible at the bottom of the popup regardless of scroll position.

medium impact medium prob integration

Filter state must remain perfectly synchronised between the map view and the list fallback. If the filter panel emits state that is not consumed identically by both views, coordinators switching between views will see inconsistent mentor sets, eroding trust in the feature.

Mitigation & Contingency

Mitigation: Store active filter criteria in a single shared Riverpod provider owned by the map-view-screen and consumed by both map-marker-widget (via mentor-location-service) and mentor-list-fallback. Write integration tests that apply a filter, switch views, and assert identical mentor counts in both views.

Contingency: If filter sync proves brittle, simplify to a single filter state object passed explicitly as a constructor argument to both views on each rebuild, eliminating indirect state sharing.