polish(ux): selected filter chips fill solid for clear contrast

The selected state used a pale tonal container (seedPrimaryContainer)
that sat almost on top of the pale canvas, so 'on' barely read. Selected
chips now fill solid — seedGreen for the plain attribute/market chips and
the swatch's own ink for family/form chips — with white label, icon and
checkmark. Idle chips stay light with dark ink, so the on/off jump is
unmistakable. White-on-ink AA is locked by a new contrast test.
This commit is contained in:
vjrj 2026-07-11 07:48:27 +02:00
parent 05227706fd
commit d897d3934c
5 changed files with 53 additions and 12 deletions

View file

@ -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,
),
);

View file

@ -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),
);
}
}

View file

@ -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,

View file

@ -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,
),
),
);
}