high priority low complexity frontend pending frontend specialist Tier 6

Acceptance Criteria

A dismissible error banner renders above the login form (not as a modal/dialog) whenever LoginFormBLoC emits an authentication failure state
Banner displays the localised error message for each failure type: invalid credentials, network error, account locked, and unverified email
Banner includes a visible dismiss (X) button; tapping it clears the error state in BLoC and removes the banner
Banner is wrapped in a Semantics widget with liveRegion: true so VoiceOver/TalkBack announces the error immediately on appearance
Banner does not appear in the initial/idle state or during loading state
Error banner is styled per design token system (error colour, typography, spacing) — no hardcoded colours
Re-triggering the same error replaces (not appends) the current banner
Network error message is distinct from credential error message to avoid misleading users
Account locked message includes guidance text (e.g., 'Contact your coordinator') per organisation labels system
Unverified email message includes a resend-link action button if applicable in the BLoC state
Banner disappears automatically when BLoC transitions back to initial/loading/success state
No regression on existing LoginScreen layout — banner insertion does not shift form elements

Technical Requirements

frameworks
Flutter
BLoC
flutter_bloc
apis
Supabase Auth (error code mapping)
data models
AuthFailure (sealed class/enum from LoginFormBLoC)
OrganisationLabels
performance requirements
Banner appearance animation completes within 200ms
No unnecessary BLoC rebuilds — use BlocBuilder with buildWhen to scope rebuilds to error state changes only
security requirements
Do not expose raw Supabase error codes or stack traces in the banner message
Account locked messages must not reveal whether the account exists (mitigate user enumeration)
ui components
ErrorBanner widget (new, reusable)
AppButton (dismiss action)
Design token colours: error/danger tokens
Semantics (liveRegion: true wrapper)

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Create a standalone ErrorBanner widget that accepts a message string and an onDismiss callback — keep it decoupled from BLoC so it is reusable across screens. In LoginScreen, use BlocBuilder with buildWhen: (prev, curr) => prev.errorMessage != curr.errorMessage to limit rebuilds. Map Supabase AuthException error codes to localised message keys in a dedicated AuthErrorMapper utility class. Wrap the banner in an AnimatedSwitcher for a smooth slide-in/fade-in.

Place the banner as the first child of the form Column so it sits above all inputs without causing layout shift. Use the organisation labels system for any organisation-specific wording. Avoid GlobalKey or imperative show/hide patterns — keep this fully reactive.

Testing Requirements

Widget tests (flutter_test): (1) BLoC emits invalidCredentials → banner appears with correct message; (2) BLoC emits networkError → banner shows network message; (3) BLoC emits accountLocked → banner shows locked message; (4) BLoC emits unverifiedEmail → banner shows unverified message; (5) tap dismiss button → banner disappears and BLoC reset called; (6) BLoC transitions to loading → banner not visible; (7) Semantics.liveRegion is true on the banner widget. Use bloc_test MockBloc for all BLoC interactions. Ensure no golden regression on LoginScreen layout.

Component
Login Screen
ui low
Epic Risks (3)
high impact medium prob scope

Automated accessibility checks (e.g., flutter_accessibility_service) may pass while manual VoiceOver/TalkBack testing reveals focus-order issues, missing semantic roles, or live region announcements that fire too early or not at all. Discovering these late risks delaying the MVP release.

Mitigation & Contingency

Mitigation: Conduct manual VoiceOver and TalkBack testing on physical devices at the end of every sprint, not only at release. Define accessibility acceptance criteria per component and include them in the DoD. Use Semantics widgets explicitly rather than relying on implicit semantics from Flutter's default widgets for all interactive elements.

Contingency: Maintain a prioritized accessibility bug backlog separate from the main backlog. If critical VoiceOver issues are found close to release, create an explicit accessibility hotfix sprint before TestFlight distribution to Blindeforbundet testers.

medium impact medium prob technical

Keyboard height varies significantly between iOS and Android, between device sizes (iPhone SE vs iPad), and with third-party keyboards. The KeyboardAwareLayout may not correctly adjust scroll offset in all combinations, causing input fields to remain hidden behind the keyboard on certain device/keyboard configurations.

Mitigation & Contingency

Mitigation: Test on a matrix of devices including iPhone SE (small viewport), a mid-size Android phone, and a tablet. Implement the layout using MediaQuery.viewInsets.bottom rather than a fixed padding value to correctly respond to any keyboard height. Include edge cases for floating keyboards on iPads.

Contingency: If device-specific issues are found after release, implement a bottom-padding fallback using BottomPadding inset and allow users to manually scroll. Log affected device/OS combinations for targeted fixes.

high impact low prob dependency

If the design token system's colour palette is updated without re-running contrast validation, form field labels, error messages, or placeholder text could fall below the WCAG 2.2 AA 4.5:1 ratio, causing a compliance regression.

Mitigation & Contingency

Mitigation: Integrate a contrast ratio validator (e.g., a CI lint step using the contrast-ratio-validator component) that checks all colour pairs used in the login form on every pull request. Document which token pairs are used for labels, errors, and backgrounds in the login form.

Contingency: If a contrast regression is detected post-merge, hot-patch the affected design token value. Do not ship a TestFlight build with known WCAG AA failures to Blindeforbundet testers.