diff --git a/apps/app_seeds/lib/ui/calendar_screen.dart b/apps/app_seeds/lib/ui/calendar_screen.dart index 7693e3d..8ceba0d 100644 --- a/apps/app_seeds/lib/ui/calendar_screen.dart +++ b/apps/app_seeds/lib/ui/calendar_screen.dart @@ -164,9 +164,9 @@ class _MonthStripState extends State<_MonthStrip> { ), selected: selected, onSelected: (_) => widget.onSelect(m), - selectedColor: seedPrimaryContainer, + selectedColor: seedGreen, labelStyle: TextStyle( - color: selected ? seedOnPrimaryContainer : seedMuted, + color: selected ? Colors.white : seedMuted, fontWeight: selected ? FontWeight.w600 : FontWeight.w400, ), ); diff --git a/apps/app_seeds/lib/ui/inventory_list_screen.dart b/apps/app_seeds/lib/ui/inventory_list_screen.dart index bbadda3..1e2d99b 100644 --- a/apps/app_seeds/lib/ui/inventory_list_screen.dart +++ b/apps/app_seeds/lib/ui/inventory_list_screen.dart @@ -308,7 +308,7 @@ class _FilterBar extends StatelessWidget { avatar: Icon( Icons.calendar_month, size: 18, - color: state.sowThisMonthOnly ? null : seedGreen, + color: state.sowThisMonthOnly ? Colors.white : seedGreen, ), label: Text(t.calendar.filterChip), selected: state.sowThisMonthOnly, @@ -320,7 +320,7 @@ class _FilterBar extends StatelessWidget { avatar: Icon( Icons.volunteer_activism_outlined, size: 18, - color: state.sharingOnly ? null : seedGreen, + color: state.sharingOnly ? Colors.white : seedGreen, ), label: Text(t.share.filterChip), selected: state.sharingOnly, @@ -332,7 +332,7 @@ class _FilterBar extends StatelessWidget { avatar: Icon( Icons.eco, size: 18, - color: state.organicOnly ? null : seedGreen, + color: state.organicOnly ? Colors.white : seedGreen, ), label: Text(t.editVariety.organic), selected: state.organicOnly, @@ -344,7 +344,7 @@ class _FilterBar extends StatelessWidget { avatar: Icon( Icons.autorenew, size: 18, - color: state.needsReproductionOnly ? null : seedGreen, + color: state.needsReproductionOnly ? Colors.white : seedGreen, ), label: Text(t.inventory.needsReproductionFilter), selected: state.needsReproductionOnly, @@ -446,16 +446,23 @@ class _SwatchFilterChip extends StatelessWidget { @override Widget build(BuildContext context) { + // Idle: a soft same-hue fill with dark ink. Selected: the ink becomes the + // fill and the content turns white, so the chip clearly reads as "on" while + // keeping its family/form hue. White on every ink meets WCAG AA — see + // test/ui/category_palette_contrast_test.dart. + final ink = selected ? Colors.white : swatch.ink; return FilterChip( label: Text(label), - avatar: icon == null ? null : Icon(icon, size: 18, color: swatch.ink), + avatar: icon == null ? null : Icon(icon, size: 18, color: ink), selected: selected, onSelected: (_) => onSelected(), backgroundColor: swatch.fill, - selectedColor: swatch.ink.withValues(alpha: 0.24), - checkmarkColor: swatch.ink, - side: BorderSide(color: swatch.ink.withValues(alpha: 0.35)), - labelStyle: TextStyle(color: swatch.ink, fontWeight: FontWeight.w500), + selectedColor: swatch.ink, + checkmarkColor: Colors.white, + side: BorderSide( + color: swatch.ink.withValues(alpha: selected ? 0 : 0.35), + ), + labelStyle: TextStyle(color: ink, fontWeight: FontWeight.w500), ); } } diff --git a/apps/app_seeds/lib/ui/market_screen.dart b/apps/app_seeds/lib/ui/market_screen.dart index 0b05fcc..5413717 100644 --- a/apps/app_seeds/lib/ui/market_screen.dart +++ b/apps/app_seeds/lib/ui/market_screen.dart @@ -493,7 +493,7 @@ class _MarketFilterBar extends StatelessWidget { avatar: Icon( Icons.eco, size: 18, - color: state.organicOnly ? null : seedGreen, + color: state.organicOnly ? Colors.white : seedGreen, ), label: Text(t.editVariety.organic), selected: state.organicOnly, diff --git a/apps/app_seeds/lib/ui/theme.dart b/apps/app_seeds/lib/ui/theme.dart index 392e37a..714ad7d 100644 --- a/apps/app_seeds/lib/ui/theme.dart +++ b/apps/app_seeds/lib/ui/theme.dart @@ -105,5 +105,24 @@ ThemeData buildTaneTheme() { thickness: 1, color: seedDivider, ), + // Filter/choice chips: idle chips stay light with dark ink, but a *selected* + // chip fills solid green with white ink so "on" reads unmistakably against + // the pale canvas (the soft tonal default sat too close to it). White on + // seedGreen meets WCAG AA — see test/ui/theme_contrast_test.dart. Chips that + // carry their own hue (family/form swatches) override this per-swatch. + chipTheme: const ChipThemeData( + selectedColor: seedGreen, + checkmarkColor: Colors.white, + labelStyle: TextStyle( + color: seedOnSurface, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + secondaryLabelStyle: TextStyle( + color: Colors.white, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), ); } diff --git a/apps/app_seeds/test/ui/category_palette_contrast_test.dart b/apps/app_seeds/test/ui/category_palette_contrast_test.dart index c751b96..0c4f379 100644 --- a/apps/app_seeds/test/ui/category_palette_contrast_test.dart +++ b/apps/app_seeds/test/ui/category_palette_contrast_test.dart @@ -35,4 +35,19 @@ void main() { reason: '$type ink on fill'); } }); + + // A *selected* swatch chip fills solid with its ink and draws white content + // (label, icon, checkmark). That pairing must clear AA too. + test('white content meets AA on every selected (ink-filled) swatch', () { + for (var i = 0; i < 60; i++) { + final s = categorySwatch('family-$i'); + expect(contrast(Colors.white, s.ink), greaterThanOrEqualTo(4.5), + reason: 'white on category-$i ink'); + } + for (final type in LotType.values) { + final s = lotTypeSwatch(type); + expect(contrast(Colors.white, s.ink), greaterThanOrEqualTo(4.5), + reason: 'white on $type ink'); + } + }); }