updates themeing to align better with website harmonies

This commit is contained in:
2026-07-12 01:38:14 -07:00
parent 9706cc9a6d
commit f069fcdbfb
6 changed files with 132 additions and 70 deletions
+27 -19
View File
@@ -111,38 +111,46 @@ class AppConfig {{
def generate_theme_colors(theme: dict) -> str: def generate_theme_colors(theme: dict) -> str:
c = theme["colors"] c = theme["colors"]
# Light mode # Light mode — mapped to match companion website color usage
light_props = [ light_props = [
("primarySeed", hex_to_rgba(c["primary"])), ("headingColor", hex_to_rgba(c["primary"])),
("secondarySeed", hex_to_rgba(c["accent"])), ("accentColor", hex_to_rgba(c["accent"])),
("scaffoldGradientStart", hex_to_rgba(c["background"])), ("scaffoldGradientStart", hex_to_rgba(c["background"])),
("scaffoldGradientEnd", hex_to_rgba(c["light"])), ("scaffoldGradientEnd", hex_to_rgba(c["light"])),
("shellSurface", hex_to_rgba(c["white"], 0.97)), ("shellSurface", hex_to_rgba(c["white"], 0.97)),
("shellBorder", hex_to_rgba(c["medium"], 0.10)), ("shellBorder", hex_to_rgba(c["medium"], 0.10)),
("accent", hex_to_rgba(c["accent_dark"])), ("mutedText", hex_to_rgba(c["medium"])),
("webViewBorder", hex_to_rgba(c["medium"], 0.15)), ("textOnDark", hex_to_rgba(c["white"])),
("webViewBackground", hex_to_rgba(c["white"])), ("textOnDarkSecondary", hex_to_rgba(c["light"])),
("controlsGradientStart", hex_to_rgba(c["primary_muted"])), ("controlsGradientStart", hex_to_rgba(c["primary"])),
("controlsGradientEnd", hex_to_rgba(c["accent_muted"])), ("controlsGradientEnd", hex_to_rgba(c["primary_light"])),
("controlsShadow", hex_to_rgba(c["black"], 0.14)), ("controlsShadow", hex_to_rgba(c["black"], 0.14)),
("statusText", hex_to_rgba(c["text"])), ("bodyText", hex_to_rgba(c["text"])),
("cardSurface", hex_to_rgba(c["white"], 0.08)),
("cardBorder", hex_to_rgba(c["white"], 0.12)),
("successColor", hex_to_rgba(c["success"])),
("dangerColor", hex_to_rgba(c["danger"])),
] ]
# Dark mode # Dark mode — mapped to match companion website color usage
dark_props = [ dark_props = [
("primarySeed", hex_to_rgba(c["primary_light"])), ("headingColor", hex_to_rgba(c["primary_light"])),
("secondarySeed", hex_to_rgba(c["accent_light"])), ("accentColor", hex_to_rgba(c["accent_light"])),
("scaffoldGradientStart", hex_to_rgba(c["black"])), ("scaffoldGradientStart", hex_to_rgba(c["black"])),
("scaffoldGradientEnd", darken(c["primary_dark"], 0.30)), ("scaffoldGradientEnd", darken(c["primary_dark"], 0.30)),
("shellSurface", hex_to_rgba(c["black"], 0.85)), ("shellSurface", hex_to_rgba(c["primary_dark"], 0.85)),
("shellBorder", hex_to_rgba(c["accent_light"], 0.20)), ("shellBorder", hex_to_rgba(c["medium"], 0.20)),
("accent", hex_to_rgba(c["primary_light"])), ("mutedText", hex_to_rgba(c["medium"])),
("webViewBorder", hex_to_rgba(c["accent_light"], 0.20)), ("textOnDark", hex_to_rgba(c["white"])),
("webViewBackground", hex_to_rgba(c["black"])), ("textOnDarkSecondary", hex_to_rgba(c["light"])),
("controlsGradientStart", hex_to_rgba(c["primary_dark"])), ("controlsGradientStart", hex_to_rgba(c["primary_dark"])),
("controlsGradientEnd", hex_to_rgba(c["accent_dark"])), ("controlsGradientEnd", hex_to_rgba(c["primary"])),
("controlsShadow", hex_to_rgba(c["black"], 0.27)), ("controlsShadow", hex_to_rgba(c["black"], 0.27)),
("statusText", hex_to_rgba(c["light"])), ("bodyText", hex_to_rgba(c["light"])),
("cardSurface", hex_to_rgba(c["white"], 0.08)),
("cardBorder", hex_to_rgba(c["white"], 0.12)),
("successColor", hex_to_rgba(c["success"])),
("dangerColor", hex_to_rgba(c["danger"])),
] ]
def props_block(props, indent: str = " ") -> str: def props_block(props, indent: str = " ") -> str:
+55 -30
View File
@@ -4,34 +4,59 @@ import 'generated/theme_colors.dart';
class AppThemeColors { class AppThemeColors {
const AppThemeColors({ const AppThemeColors({
required this.primarySeed, required this.headingColor,
required this.secondarySeed, required this.accentColor,
required this.scaffoldGradientStart, required this.scaffoldGradientStart,
required this.scaffoldGradientEnd, required this.scaffoldGradientEnd,
required this.shellSurface, required this.shellSurface,
required this.shellBorder, required this.shellBorder,
required this.accent, required this.mutedText,
required this.webViewBorder, required this.textOnDark,
required this.webViewBackground, required this.textOnDarkSecondary,
required this.controlsGradientStart, required this.controlsGradientStart,
required this.controlsGradientEnd, required this.controlsGradientEnd,
required this.controlsShadow, required this.controlsShadow,
required this.statusText, required this.bodyText,
required this.cardSurface,
required this.cardBorder,
required this.successColor,
required this.dangerColor,
}); });
final Color primarySeed; /// Website: `primary` → headings (h1h6)
final Color secondarySeed; final Color headingColor;
/// Website: `accent` → links & accent highlights
final Color accentColor;
/// Page background gradient start — website: `background` (cream)
final Color scaffoldGradientStart; final Color scaffoldGradientStart;
/// Page background gradient end — website: `light`
final Color scaffoldGradientEnd; final Color scaffoldGradientEnd;
/// Top header card surface
final Color shellSurface; final Color shellSurface;
/// Top header card border
final Color shellBorder; final Color shellBorder;
final Color accent; /// Website: `medium` → muted / secondary text
final Color webViewBorder; final Color mutedText;
final Color webViewBackground; /// Website: `white` → text on dark surfaces
final Color textOnDark;
/// Website: `light` → secondary text on dark surfaces
final Color textOnDarkSecondary;
/// Controls bar / card surface gradient start — website: `primary_dark`
final Color controlsGradientStart; final Color controlsGradientStart;
/// Controls bar / card surface gradient end — website: `primary_dark`
final Color controlsGradientEnd; final Color controlsGradientEnd;
/// Shadow color for elevated surfaces
final Color controlsShadow; final Color controlsShadow;
final Color statusText; /// Website: `text` → body text
final Color bodyText;
/// Inner card background overlay — derived from `primary_dark` / `primary_light`
final Color cardSurface;
/// Inner card border — derived from `primary_dark` / `primary_light`
final Color cardBorder;
/// Website: `success` → live indicator, success states
final Color successColor;
/// Website: `danger` → error states
final Color dangerColor;
} }
class AppThemeBusinessObject { class AppThemeBusinessObject {
@@ -47,16 +72,16 @@ class AppThemeBusinessObject {
} }
static ThemeData lightTheme() { static ThemeData lightTheme() {
final ColorScheme scheme = final ColorScheme scheme = ColorScheme.light(
ColorScheme.fromSeed(
seedColor: lightColors.primarySeed,
brightness: Brightness.light, brightness: Brightness.light,
).copyWith( primary: lightColors.headingColor,
primary: lightColors.primarySeed, onPrimary: lightColors.textOnDark,
secondary: lightColors.secondarySeed, secondary: lightColors.accentColor,
tertiary: const Color(0xFF2C85E1), onSecondary: lightColors.textOnDark,
surface: const Color(0xFFFFFBF7), surface: lightColors.shellSurface,
onSurface: lightColors.statusText, onSurface: lightColors.bodyText,
error: lightColors.dangerColor,
onError: lightColors.textOnDark,
); );
return ThemeData( return ThemeData(
@@ -77,16 +102,16 @@ class AppThemeBusinessObject {
} }
static ThemeData darkTheme() { static ThemeData darkTheme() {
final ColorScheme scheme = final ColorScheme scheme = ColorScheme.dark(
ColorScheme.fromSeed(
seedColor: darkColors.primarySeed,
brightness: Brightness.dark, brightness: Brightness.dark,
).copyWith( primary: darkColors.headingColor,
primary: darkColors.primarySeed, onPrimary: darkColors.textOnDark,
secondary: darkColors.secondarySeed, secondary: darkColors.accentColor,
tertiary: const Color(0xFF4E97E8), onSecondary: darkColors.textOnDark,
surface: const Color(0xFF111A27), surface: darkColors.shellSurface,
onSurface: darkColors.statusText, onSurface: darkColors.bodyText,
error: darkColors.dangerColor,
onError: darkColors.textOnDark,
); );
return ThemeData( return ThemeData(
+14 -6
View File
@@ -58,7 +58,7 @@ class LiveInfoPanel extends StatelessWidget {
children: [ children: [
Icon( Icon(
Icons.graphic_eq_rounded, Icons.graphic_eq_rounded,
color: themeColors.statusText, color: themeColors.textOnDarkSecondary,
size: 20, size: 20,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
@@ -67,7 +67,7 @@ class LiveInfoPanel extends StatelessWidget {
'Live Studio Feed', 'Live Studio Feed',
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
color: themeColors.statusText, color: themeColors.textOnDark,
), ),
), ),
), ),
@@ -94,7 +94,9 @@ class LiveInfoPanel extends StatelessWidget {
child: TrackCard( child: TrackCard(
label: 'Now Playing', label: 'Now Playing',
track: current, track: current,
titleColor: themeColors.statusText, titleColor: themeColors.textOnDark,
cardSurface: themeColors.cardSurface,
cardBorder: themeColors.cardBorder,
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
@@ -102,7 +104,9 @@ class LiveInfoPanel extends StatelessWidget {
child: TrackCard( child: TrackCard(
label: 'Coming Next', label: 'Coming Next',
track: next, track: next,
titleColor: themeColors.statusText, titleColor: themeColors.textOnDark,
cardSurface: themeColors.cardSurface,
cardBorder: themeColors.cardBorder,
), ),
), ),
], ],
@@ -113,14 +117,18 @@ class LiveInfoPanel extends StatelessWidget {
label: 'On Air Show', label: 'On Air Show',
show: currentShow, show: currentShow,
sourceTimezone: snapshot?.timezone, sourceTimezone: snapshot?.timezone,
titleColor: themeColors.statusText, titleColor: themeColors.textOnDarkSecondary,
cardSurface: themeColors.cardSurface,
cardBorder: themeColors.cardBorder,
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
ShowCard( ShowCard(
label: 'Up Next Show', label: 'Up Next Show',
show: nextShow, show: nextShow,
sourceTimezone: snapshot?.timezone, sourceTimezone: snapshot?.timezone,
titleColor: themeColors.statusText, titleColor: themeColors.textOnDarkSecondary,
cardSurface: themeColors.cardSurface,
cardBorder: themeColors.cardBorder,
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Text( Text(
+2 -2
View File
@@ -400,7 +400,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
style: theme.textTheme.headlineMedium?.copyWith( style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
letterSpacing: 0.4, letterSpacing: 0.4,
color: themeColors.statusText, color: themeColors.headingColor,
), ),
), ),
), ),
@@ -469,7 +469,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
_playbackStatusLabel(), _playbackStatusLabel(),
style: theme.textTheme.titleSmall?.copyWith( style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: themeColors.statusText, color: themeColors.textOnDark,
), ),
), ),
], ],
+13 -4
View File
@@ -10,12 +10,16 @@ class ShowCard extends StatelessWidget {
required this.show, required this.show,
required this.sourceTimezone, required this.sourceTimezone,
required this.titleColor, required this.titleColor,
required this.cardSurface,
required this.cardBorder,
}); });
final String label; final String label;
final LiveInfoShowInfo? show; final LiveInfoShowInfo? show;
final String? sourceTimezone; final String? sourceTimezone;
final Color titleColor; final Color titleColor;
final Color cardSurface;
final Color cardBorder;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -23,8 +27,8 @@ class ShowCard extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
color: Colors.black.withValues(alpha: 0.06), color: cardSurface,
border: Border.all(color: Colors.black.withValues(alpha: 0.08)), border: Border.all(color: cardBorder),
), ),
child: Row( child: Row(
children: [ children: [
@@ -37,7 +41,10 @@ class ShowCard extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of( style: Theme.of(
context, context,
).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), ).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
color: titleColor,
),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
@@ -46,7 +53,9 @@ class ShowCard extends StatelessWidget {
show?.starts, show?.starts,
sourceTimezone: sourceTimezone, sourceTimezone: sourceTimezone,
), ),
style: Theme.of(context).textTheme.labelSmall, style: Theme.of(
context,
).textTheme.labelSmall?.copyWith(color: titleColor),
), ),
], ],
), ),
+17 -5
View File
@@ -9,11 +9,15 @@ class TrackCard extends StatelessWidget {
required this.label, required this.label,
required this.track, required this.track,
required this.titleColor, required this.titleColor,
required this.cardSurface,
required this.cardBorder,
}); });
final String label; final String label;
final LiveInfoTrackInfo? track; final LiveInfoTrackInfo? track;
final Color titleColor; final Color titleColor;
final Color cardSurface;
final Color cardBorder;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -24,9 +28,9 @@ class TrackCard extends StatelessWidget {
return Container( return Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.08), color: cardSurface,
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
border: Border.all(color: Colors.black.withValues(alpha: 0.12)), border: Border.all(color: cardBorder),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -45,21 +49,29 @@ class TrackCard extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of( style: Theme.of(
context, context,
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w700), ).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w700,
color: titleColor,
),
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Text( Text(
subtitle.isEmpty ? 'No metadata provided' : subtitle, subtitle.isEmpty ? 'No metadata provided' : subtitle,
maxLines: 6, maxLines: 6,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(
context,
).textTheme.bodySmall?.copyWith(color: titleColor),
), ),
const Spacer(), const Spacer(),
Text( Text(
window, window,
style: Theme.of( style: Theme.of(
context, context,
).textTheme.labelMedium?.copyWith(fontWeight: FontWeight.w600), ).textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.w600,
color: titleColor,
),
), ),
], ],
), ),