critical priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

ExpenseTypeOptionCard is a StatelessWidget accepting: ExpenseTypeOption model, bool isSelected, bool isDisabled, VoidCallback? onTap
Three visually distinct states render correctly: enabled (default), selected (highlighted border + fill), disabled (reduced opacity, no interaction)
Card displays label text using design token typography (no hardcoded font sizes or colors)
Card displays icon using design token icon sizing
Card background, border, and text colors are sourced exclusively from design tokens — zero hardcoded hex values
Tap callback fires only when isDisabled is false; tapping a disabled card is a no-op
Card has a minimum touch target of 48×48 dp per WCAG 2.2 AA guidelines
Widget renders correctly on both iOS and Android at standard and large text sizes
Golden test snapshots pass for all three visual states

Technical Requirements

frameworks
Flutter
flutter_test
data models
ExpenseTypeOption
performance requirements
Widget build time must not exceed 16ms on a mid-range device
No unnecessary rebuilds — widget is purely stateless
security requirements
No sensitive data stored or logged inside this widget
ui components
ExpenseTypeOptionCard (new widget)
Design token constants (AppColors, AppSpacing, AppTypography, AppRadii)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define an ExpenseTypeOption model with at minimum: String id, String label, IconData icon. Use AnimatedContainer or AnimatedOpacity for smooth state transitions between enabled/selected/disabled to avoid jarring jumps. Apply design tokens via a centralized AppTokens or AppTheme class — never inline. The card border should use a 2dp selected-state ring drawn via BoxDecoration.

Disabled state should use 0.38 opacity (Material Design guideline for disabled elements). Keep the widget file under 150 lines; extract any sub-builders if needed. Do not reference BLoC, Riverpod, or any state management from inside this widget.

Testing Requirements

Unit/widget tests using flutter_test: (1) render test for each of the three states (enabled, selected, disabled), (2) tap callback fires when enabled and does not fire when disabled, (3) golden image tests for all three states at standard and 1.5× text scale, (4) verify no hardcoded color or spacing values exist via lint rule or code review checklist. Minimum 90% branch coverage on this widget file.

Component
Expense Type Picker Widget
ui medium
Epic Risks (2)
medium impact medium prob technical

If the expense calculation preview subscribes to the full BLoC state stream, every unrelated state property change (e.g. a loading flag toggle) triggers a widget rebuild. With complex card animations for the disabled-state transition, this could cause frame drops on low-end Android devices used by some peer mentors.

Mitigation & Contingency

Mitigation: Use select() on the Riverpod provider to subscribe only to the specific state slice each widget needs; write a performance test asserting rebuild count on a rapid sequence of toggle events.

Contingency: If jank is detected in device testing, replace animated disabled-state transitions with instant opacity changes and defer animation polish to a follow-up sprint.

medium impact low prob integration

The disabled card state requires a specific contrast-safe colour combination that communicates unavailability without relying solely on colour (WCAG 1.4.1). If the current design token palette does not include a disabled-state token with sufficient contrast for text on the disabled background, the widget will either fail WCAG AA or require a last-minute design token addition that could break other components.

Mitigation & Contingency

Mitigation: Audit the existing design token manifest for disabled-state tokens at the start of the epic; if missing, raise with the design lead and add a contrast-validated token before widget implementation begins.

Contingency: If no design review is available, use the established --color-text-disabled and --color-surface-disabled tokens with an added strikethrough or lock icon to satisfy WCAG 1.4.1 non-colour requirement, and document the deviation for design review.