high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Tab key (hardware keyboard / switch access) cycles through all interactive overlay controls in logical order: filter panel toggle → view toggle FAB → zoom in → zoom out
The map canvas itself is excluded from the tab order (ExcludeSemantics or equivalent) to prevent focus from getting trapped in a non-semantic canvas node
Each control receives a visible focus indicator meeting WCAG 2.2 AA focus appearance requirements (min 2px outline, sufficient contrast)
Pressing Enter or Space on a focused control activates it (same as tap)
The MapViewScreen Semantics description includes a brief keyboard shortcut reference (e.g., 'Use Tab to navigate controls. Use the list view for full keyboard access.')
Focus is not lost when the filter panel opens or closes; focus moves to the first interactive element inside the panel on open, and returns to the filter toggle button on close
Flutter integration test traverses all controls using FocusTraversalPolicy assertions and confirms no non-interactive nodes are in the tab order
Controls remain keyboard-reachable when the list fallback view is active
Switch access (sequential scanning) reaches every control without requiring the user to interact with the map canvas

Technical Requirements

frameworks
Flutter
BLoC
data models
FilterState
performance requirements
Focus traversal must respond within one frame; no async gaps during tab navigation
ui components
MapViewScreen (239-map-view-screen)
MapFilterPanel (242-map-filter-panel)
ViewToggleButton (243-view-toggle-button)
Zoom controls (overlay widgets)

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Wrap the map canvas in an ExcludeSemantics widget and a FocusTraversalGroup with policy: NeverFocusedFocusTraversalPolicy so keyboard users skip it entirely. Use FocusTraversalGroup with OrderedTraversalPolicy on the overlay controls stack to enforce tab order independent of widget tree position. Manage filter panel focus with a FocusNode: call FocusScope.of(context).requestFocus(firstPanelFocusNode) when the panel opens, and call filterToggleFocusNode.requestFocus() when it closes. Add the keyboard hint to MapViewScreen via Semantics(label: '...', child: ...) at the scaffold level, not on individual buttons.

Ensure all focusable widgets have a visible focusDecoration — Flutter's default focus highlight may be invisible on dark backgrounds, so use a Theme override or custom focus indicator using FocusHighlight.

Testing Requirements

Write a Flutter integration test using tester.sendKeyEvent(LogicalKeyboardKey.tab) to cycle through all controls and assert each receives focus in the expected order. Assert that the map canvas widget is not in the focus traversal path using FocusTraversalGroup with the policy set to skip the canvas subtree. Test focus restoration: open filter panel, assert focus is inside panel; close panel, assert focus returns to filter toggle button. Run flutter_test semantics dump to confirm no unlabelled focusable nodes.

Test on both physical keyboard and simulated switch access.

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.