Palette: seven roles (accent, good, caution added; highlight renamed alarm); no hard-coded colours left
background, foreground, subdued, accent (emphasis without danger), and the state gradient good → caution → alarm; surface and onFill derived. smart-serow's four-key themes still load (highlight = alarm; the new roles fall back). Material's scheme follows: buttons in accent instead of the alarm colour. Link state, mock banner, signal tiles, raw log, checklist button and marks, camera page all take palette roles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
+81
-23
@@ -1,14 +1,22 @@
|
||||
// The palette system, from smart-serow's HUD: four semantic colours, each with a dark
|
||||
// (low ambient light) and a bright (high ambient light) variant, read from a theme
|
||||
// JSON in assets/themes/. Same file format as smart-serow's extra/themes/, so themes
|
||||
// move between the two as they are. Widgets ask for meanings, never for colours:
|
||||
// The palette system, from smart-serow's HUD, widened to seven roles: each a meaning,
|
||||
// each with a dark (low ambient light) and a bright (high ambient light) variant, read
|
||||
// from a theme JSON in assets/themes/. Widgets ask for meanings, never for colours:
|
||||
//
|
||||
// final p = AppTheme.of(context);
|
||||
// color: p.foreground
|
||||
//
|
||||
// The roles: background, foreground, subdued (muted), accent (emphasis without danger:
|
||||
// buttons, links, the current item), and the good → caution → alarm gradient for state.
|
||||
// Two more are derived, never authored: surface (raised bars and panels) and onFill
|
||||
// (text on a filled colour).
|
||||
//
|
||||
// smart-serow's four-key files load unchanged: their `highlight` is `alarm`, and the
|
||||
// three newer roles fall back (accent and good to foreground, caution to subdued).
|
||||
//
|
||||
// Pick the theme at build time: `--dart-define=YUKART_THEME=default` (default: rei, smart-serow's).
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:math' show max, min;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -24,45 +32,87 @@ class Palette {
|
||||
const Palette({
|
||||
required this.background,
|
||||
required this.foreground,
|
||||
required this.highlight,
|
||||
required this.subdued,
|
||||
required this.accent,
|
||||
required this.good,
|
||||
required this.caution,
|
||||
required this.alarm,
|
||||
});
|
||||
|
||||
/// Behind everything.
|
||||
final Color background;
|
||||
|
||||
/// Values: what is read at a glance.
|
||||
/// Values and primary text: what is read at a glance.
|
||||
final Color foreground;
|
||||
|
||||
/// Warnings, and the one thing that must be seen.
|
||||
final Color highlight;
|
||||
|
||||
/// Labels, units, grids, and anything missing or stale.
|
||||
/// Labels, units, grids, secondary text, and anything missing or stale.
|
||||
final Color subdued;
|
||||
|
||||
/// Emphasis without danger: buttons, links, the current item.
|
||||
final Color accent;
|
||||
|
||||
/// Connected, done, healthy.
|
||||
final Color good;
|
||||
|
||||
/// In progress, unconfirmed, mock data.
|
||||
final Color caution;
|
||||
|
||||
/// Down, refused, destructive, over a limit: the one thing that must be seen.
|
||||
final Color alarm;
|
||||
|
||||
/// Raised bars and panels: background with a little foreground mixed in.
|
||||
Color get surface => Color.lerp(background, foreground, 0.08)!;
|
||||
|
||||
/// Text drawn on a filled [fill]: background or foreground, whichever contrasts more.
|
||||
Color onFill(Color fill) {
|
||||
double contrast(Color a, Color b) {
|
||||
final (x, y) = (a.computeLuminance(), b.computeLuminance());
|
||||
return (max(x, y) + 0.05) / (min(x, y) + 0.05);
|
||||
}
|
||||
|
||||
return contrast(fill, background) >= contrast(fill, foreground)
|
||||
? background
|
||||
: foreground;
|
||||
}
|
||||
|
||||
static Palette _fromJson(Map<String, dynamic> j) {
|
||||
Color c(String k) =>
|
||||
Color(0xFF000000 | int.parse((j[k] as String).substring(1), radix: 16));
|
||||
Color? c(String k) => j[k] is String
|
||||
? Color(
|
||||
0xFF000000 | int.parse((j[k] as String).substring(1), radix: 16),
|
||||
)
|
||||
: null;
|
||||
final foreground = c('foreground')!;
|
||||
final subdued = c('subdued')!;
|
||||
return Palette(
|
||||
background: c('background'),
|
||||
foreground: c('foreground'),
|
||||
highlight: c('highlight'),
|
||||
subdued: c('subdued'),
|
||||
background: c('background')!,
|
||||
foreground: foreground,
|
||||
subdued: subdued,
|
||||
accent: c('accent') ?? foreground,
|
||||
good: c('good') ?? foreground,
|
||||
caution: c('caution') ?? subdued,
|
||||
// smart-serow calls it highlight.
|
||||
alarm: (c('alarm') ?? c('highlight'))!,
|
||||
);
|
||||
}
|
||||
|
||||
/// smart-serow's hard-coded last resort, used when no theme file loads.
|
||||
/// The last resort, used when no theme file loads (smart-serow's four, plus three).
|
||||
static const fallbackDark = Palette(
|
||||
background: Color(0xFF000000),
|
||||
foreground: Color(0xFFFFFFFF),
|
||||
highlight: Color(0xFFFF5555),
|
||||
subdued: Color(0xFF808080),
|
||||
accent: Color(0xFF5EC8F2),
|
||||
good: Color(0xFF6CCB5F),
|
||||
caution: Color(0xFFFFC107),
|
||||
alarm: Color(0xFFFF5555),
|
||||
);
|
||||
static const fallbackBright = Palette(
|
||||
background: Color(0xFFF0F0F0),
|
||||
foreground: Color(0xFF1A1A1A),
|
||||
highlight: Color(0xFFCC0000),
|
||||
subdued: Color(0xFF606060),
|
||||
accent: Color(0xFF0277BD),
|
||||
good: Color(0xFF2E7D32),
|
||||
caution: Color(0xFF8D6E00),
|
||||
alarm: Color(0xFFCC0000),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,7 +156,8 @@ class Themes extends ChangeNotifier {
|
||||
return Themes('fallback', Palette.fallbackDark, Palette.fallbackBright);
|
||||
}
|
||||
|
||||
/// Material's theme for the rest of the app, from the same four colours.
|
||||
/// Material's theme for the rest of the app, from the same roles: buttons and
|
||||
/// selection in accent, errors in alarm, raised surfaces in surface.
|
||||
ThemeData material() {
|
||||
final p = current;
|
||||
final brightness = _isDark ? Brightness.dark : Brightness.light;
|
||||
@@ -116,13 +167,20 @@ class Themes extends ChangeNotifier {
|
||||
scaffoldBackgroundColor: p.background,
|
||||
colorScheme:
|
||||
ColorScheme.fromSeed(
|
||||
seedColor: p.highlight,
|
||||
seedColor: p.accent,
|
||||
brightness: brightness,
|
||||
).copyWith(
|
||||
surface: p.background,
|
||||
onSurface: p.foreground,
|
||||
primary: p.highlight,
|
||||
error: p.highlight,
|
||||
surfaceContainerHigh: p.surface,
|
||||
surfaceContainerHighest: p.surface,
|
||||
primary: p.accent,
|
||||
onPrimary: p.onFill(p.accent),
|
||||
primaryContainer: Color.lerp(p.background, p.accent, 0.25),
|
||||
onPrimaryContainer: p.foreground,
|
||||
secondary: p.accent,
|
||||
error: p.alarm,
|
||||
onError: p.onFill(p.alarm),
|
||||
outline: p.subdued,
|
||||
onSurfaceVariant: p.subdued,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user