Auth Method Selector WCAG 2.2 AA accessibility
epic-bankid-vipps-login-ui-task-002 — Apply full WCAG 2.2 AA accessibility to the Authentication Method Selector Screen: correct semantic labels on all method cards and buttons, minimum 44x44 dp touch targets, 4.5:1 contrast ratio for all text against background, and logical focus order for keyboard and switch-access users. Add VoiceOver and TalkBack announcements when the screen loads so screen reader users are immediately oriented.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Wrap the entire method card list in a FocusTraversalGroup using ReadingOrderTraversalPolicy so focus order matches visual layout regardless of widget tree order. Use SemanticsService.announce() inside a post-frame callback (WidgetsBinding.instance.addPostFrameCallback) at screen init to fire the orientation announcement after the screen is rendered — calling it before the frame causes it to be swallowed by some screen readers. For contrast checking, add a dev-only debug flag that overlays contrast ratios using the design token system to catch regressions during development. All Semantics labels must be defined as constants in a dedicated a11y_strings.dart file to enable easy localization and testing.
The Blindeforbundet requirements make screen-reader quality non-negotiable — treat any VoiceOver regression as a P0 bug. Do not rely solely on Flutter's default semantics inheritance; explicitly set label, hint, and value on all custom widgets. Minimum touch target padding should be applied via Semantics(container: true) with an explicit size constraint rather than just increasing visible padding, so the visual design is not impacted.
Testing Requirements
Unit tests: verify Semantics node labels match expected strings for each method card using flutter_test's SemanticsController. Widget tests: assert touch-target sizes are >= 44x44 dp for all interactive widgets using tester.getSize(). Accessibility integration tests: run Flutter's built-in accessibility audit (SemanticsNode tree validation) and assert zero violations. Manual testing: exercise the screen on a physical iPhone with VoiceOver enabled and a physical Android device with TalkBack enabled, verifying announcement text is natural and orientation message fires on entry.
Dynamic type test: set system font scale to 1.0, 1.5, and 2.0 and assert no overflow RenderFlex errors appear in the widget tree.
BankID on mobile uses a WebView or external app redirect that has known compatibility issues with Flutter's WebView package on certain Android versions. BankID's JavaScript-heavy broker pages may also trigger CSP or mixed-content errors in a Flutter WebView, preventing the authentication flow from completing.
Mitigation & Contingency
Mitigation: Use the flutter_inappwebview package (more mature than webview_flutter for complex OAuth pages) and validate BankID WebView rendering on the broker's test environment before integrating with the service layer. Prefer external browser redirect where the broker supports it.
Contingency: If WebView approach fails for certain BankID brokers, implement the full external browser redirect + deep link callback pattern as the primary flow and treat WebView as a fallback only.
The OAuth redirect flows (both Vipps and BankID) temporarily move the user outside the Flutter app into an external browser or the Vipps/BankID app. Screen reader users may lose focus context during this transition and become disoriented when the app callback returns them to the loading state, failing the WCAG 2.2 AA mandate.
Mitigation & Contingency
Mitigation: Implement explicit accessibility announcements (live region announcements) at each transition point: when launching the external flow ('Opening Vipps'), during the loading wait state ('Waiting for Vipps confirmation'), and on return ('Login successful' or 'Login failed — please try again'). Test with VoiceOver on iOS and TalkBack on Android during development.
Contingency: If OAuth transition accessibility is unresolvable on a specific platform, add an explicit accessibility user guide in the onboarding flow explaining the external app redirect behavior to set user expectations.
Biometric UI varies significantly across devices — Face ID (iPhone), fingerprint sensor (most Android), front-facing camera biometrics (some Android), and devices with no biometrics at all. Flutter's local_auth handles the OS dialog but the surrounding UI must gracefully handle all these cases, and testing coverage for all permutations is difficult.
Mitigation & Contingency
Mitigation: Use local_auth's getAvailableBiometrics() to detect the exact biometric type and render appropriate iconography (Face ID icon vs. fingerprint icon). For devices with no biometrics, skip the biometric screen entirely and route directly to full re-authentication.
Contingency: If a specific device configuration produces unexpected local_auth behavior in production, implement a user-accessible toggle in Settings to disable biometric login entirely, routing those users to the standard BankID/Vipps flow without biometrics.