high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

BufdirValidationSummaryBanner is a ConsumerWidget that reads a Riverpod provider exposing BufdirValidationSummaryData
When isEmpty == true, the banner animates out using AnimatedSize and AnimatedOpacity with duration 200ms and Curves.easeInOut
When isEmpty == false, the banner animates in with the same transition
Error count chip uses design token color errorForeground on errorContainer background (WCAG AA contrast ≥ 4.5:1 for normal text)
Warning count chip uses design token color warningForeground on warningContainer background (WCAG AA contrast ≥ 4.5:1)
Banner remains visible (sticky) during scroll when implemented with SliverPersistentHeader; alternatively a Stack overlay positioned at top of CustomScrollView achieves the same effect
Banner renders correctly on screen widths 320px–480px without overflow
Tapping the banner does not crash when the issue list is empty (no-op guard)
Semantics label on the banner container reads 'Validation summary: {n} errors, {m} warnings' (announced on focus)
No inline color values — all colors reference design token constants

Technical Requirements

frameworks
Flutter
Riverpod
data models
bufdir_column_schema
bufdir_export_audit_log
performance requirements
AnimatedOpacity/AnimatedSize must not trigger full subtree rebuilds — use const constructors where possible
Riverpod provider should use select() to avoid rebuild when unrelated state changes
security requirements
Banner must not display raw field values or user PII — counts only
WCAG 2.2 AA contrast compliance is a security-of-access requirement for visually impaired users
ui components
BufdirValidationSummaryBanner (new)
ErrorCountChip (inline or extracted widget)
WarningCountChip (inline or extracted widget)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Prefer Stack + AnimatedPositioned or AnimatedSlide over SliverPersistentHeader to avoid complexity with CustomScrollView delegate sizing. Use AnimatedSize wrapping a Visibility(maintainState: false) for clean zero-height collapse. The Riverpod provider should be a StateNotifierProvider so tests can push state changes directly. Store color tokens in a BufdirColorTokens constants class rather than using Theme.of(context) to ensure Bufdir-specific overrides are possible.

Animate in on first render only if data is non-empty — do not animate on initial widget mount if banner should be hidden.

Testing Requirements

Widget tests using flutter_test: (1) banner is absent from widget tree when BufdirValidationSummaryData.isEmpty == true after animation completes (use tester.pump(Duration(milliseconds: 250))); (2) banner is present and displays correct counts when data has errors/warnings; (3) pump with new data having zero issues triggers hide animation; (4) golden test for banner in error state, warning state, and mixed state against WCAG color tokens; (5) accessibility test: SemanticsNode for banner has correct label. Use pumpWidget with a ProviderScope to inject test data.

Component
Validation Summary Banner
ui low
Epic Risks (2)
high impact medium prob technical

Implementing the tap-to-scroll-and-focus behavior from the validation banner to a specific field row in a long scrollable list is complex in Flutter. If focus management is incorrectly implemented, VoiceOver users who navigate to the banner and select an issue will not be moved to the relevant field row, breaking the accessibility workflow and violating WCAG 2.4.3 (Focus Order).

Mitigation & Contingency

Mitigation: Use BufdirAccessibilityUtils focus management utilities (built in the foundation epic) with explicit GlobalKey-based scroll anchors on each field row. Test with a real iOS device running VoiceOver during widget development, not only in the Flutter accessibility inspector.

Contingency: If programmatic scroll-to-focus cannot be reliably achieved before the TestFlight deadline, fall back to a navigation approach where tapping a banner issue opens a modal detail sheet for that field row rather than scrolling in place, and file a follow-up ticket for the inline scroll implementation.

medium impact low prob technical

The validation summary banner must reactively update its issue count as underlying aggregated data changes (e.g., if the coordinator has navigated away and data was refreshed). If the banner's Riverpod provider is not correctly scoped, it may display stale issue counts or fail to disappear when all issues are resolved, eroding coordinator trust in the validation system.

Mitigation & Contingency

Mitigation: Drive the banner exclusively from the same Riverpod provider that powers the full preview model — do not maintain a separate local state for issue counts. Write a widget test that simulates a data refresh mid-review and asserts the banner updates within one frame.

Contingency: If stale state reaches production, add a manual refresh button to the banner as a short-term workaround while the provider scoping is corrected in the next release cycle.