Dynamic color: keep card surfaces lighter than the page
Build APK / build (push) Successful in 5m39s

In light mode surfaceContainerLow is slightly darker than the OS surface bg
maps to, so the selection bar and other cards blended into the page. Use
surfaceContainerLowest (light) / surfaceContainer (dark) for NooColors.surface.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Ayushya Amitabh
2026-10-05 22:53:37 -04:00
co-authored by Claude Sonnet 5.5
parent c0a67f3efe
commit 813c613249
3 changed files with 42 additions and 2 deletions
+7
View File
@@ -29,6 +29,13 @@ widgets. Key points:
whole app in `main.dart`. When available and `useDynamicColor` is on, the
OS-provided `ColorScheme` wins over the seed color — always thread both
`dynamicScheme` and `useDynamicColor` through when adding a theme knob.
`NooColors.fromDynamicScheme` keeps `bg` on the OS `surface` but picks
`surface` (cards, selection bar, rows) from the container roles so it
stays lighter than `bg`: `surfaceContainerLowest` (white) in light,
`surfaceContainer` in dark. The dynamic_color package seeds the container
roles from the primary color rather than the OS palette, and in light mode
`surfaceContainerLow` is slightly *darker* than the OS `surface`, which
made cards blend into the page ("the selection bar has no background").
- **Font**: Inter via `google_fonts`, applied through
`GoogleFonts.interTextTheme(...)`. Font files ship in
`assets/google_fonts/` (Instrument Sans, Schibsted Grotesk) and
+11 -2
View File
@@ -139,11 +139,20 @@ class NooColors extends ThemeExtension<NooColors> {
/// paints it, not painted directly - using it as-is for a sheet/dialog's
/// `barrierColor` hid the screen behind completely instead of dimming it,
/// so this uses the fixed palette's already-translucent scrim instead.
///
/// `surface` (cards, bars, rows) has to read as lighter than `bg`, like
/// the fixed palettes' white-on-beige / lifted-on-near-black. In light
/// mode `surfaceContainerLow` is a hair *darker* than the OS-provided
/// `surface` that [bg] uses, so cards (the selection bar, list rows)
/// vanished into the page; `surfaceContainerLowest` (white) doesn't. Dark
/// mode uses `surfaceContainer` rather than `...Low` for a bigger step
/// off the page.
factory NooColors.fromDynamicScheme(ColorScheme cs) {
final fixed = cs.brightness == Brightness.dark ? dark : light;
final dark = cs.brightness == Brightness.dark;
final fixed = dark ? NooColors.dark : NooColors.light;
return NooColors(
bg: cs.surface,
surface: cs.surfaceContainerLow,
surface: dark ? cs.surfaceContainer : cs.surfaceContainerLowest,
surface2: cs.surfaceContainerHigh,
surface3: cs.surfaceContainerHighest,
line: cs.outlineVariant,
+24
View File
@@ -74,6 +74,30 @@ void main() {
});
});
group('NooColors.fromDynamicScheme surfaces', () {
for (final brightness in Brightness.values) {
test('cards are clearly lighter than the page (${brightness.name})', () {
// The OS palette overrides `surface` (what `bg` maps to) but not the
// seeded container roles - mimic that mismatch.
final seeded = ColorScheme.fromSeed(
seedColor: const Color(0xFF3F6B8F),
brightness: brightness,
);
final os = seeded.copyWith(
surface: brightness == Brightness.light
? const Color(0xFFF7F9FC)
: const Color(0xFF101417),
);
final c = NooColors.fromDynamicScheme(os);
// WCAG-style contrast ratio between the card and the page.
final hi = c.surface.computeLuminance();
final lo = c.bg.computeLuminance();
expect(hi, greaterThan(lo));
expect((hi + 0.05) / (lo + 0.05), greaterThan(1.04));
});
}
});
group('NooAvatar', () {
testNooWidgets('current user uses accent-soft/accent-text', (
tester,