mobile-music/lib/theme/tokens.dart
2026-08-05 21:57:06 -04:00

109 lines
3.9 KiB
Dart

import 'package:flutter/widgets.dart';
/// A full color palette for one selectable app theme. Immutable; the app swaps
/// which instance is [TimbreColors._active] when the user changes theme.
class TimbrePalette {
const TimbrePalette({
required this.brightness,
required this.background,
required this.surface,
required this.foreground,
required this.dimmed,
required this.border,
required this.borderActive,
required this.accentDefault,
});
final Brightness brightness;
final Color background;
final Color surface;
final Color foreground;
final Color dimmed;
final Color border;
final Color borderActive;
final Color accentDefault;
/// The original near-black, low-contrast theme (the app's default).
static const TimbrePalette dark = TimbrePalette(
brightness: Brightness.dark,
background: Color(0xFF1A1A1A),
surface: Color(0xFF161616),
foreground: Color(0xFFD4D0C8),
dimmed: Color(0xFF5A5858),
border: Color(0xFF252525),
borderActive: Color(0xFF3A3A3A),
accentDefault: Color(0xFFFF8C00),
);
/// A light theme: lavender canvas, slightly-deeper-lavender panels, deep-indigo
/// text and accent. The accent is locked to [accentDefault] while this theme
/// is active (see `main.dart`).
static const TimbrePalette lavender = TimbrePalette(
brightness: Brightness.light,
background: Color(0xFFD3D3FF), // lavender canvas (primary)
surface: Color(0xFFC6C6F4), // slightly darker lavender panels
foreground: Color(0xFF2E2A45), // deep-indigo text
dimmed: Color(0xFF6A6688), // muted indigo-grey
border: Color(0xFFB9B9E8), // deeper-lavender hairline
borderActive: Color(0xFF2E2A45), // accent-toned active border
accentDefault: Color(0xFF2E2A45), // deep indigo (accent)
);
}
/// Design tokens mirroring Timbre's `dynamic`/`static` theme defaults
/// (see timbre `docs/sample-config.toml`, `theme.rs`, `color.rs`).
///
/// The palette is intentionally near-black and low-contrast in the default
/// theme; the *accent* is the one lively color and, in the `dynamic` theme, is
/// extracted live from the current album art. The base colors are no longer
/// compile-time constants — they resolve through [_active] so a user-selected
/// theme (see [TimbrePalette]) recolors the whole app. `applyPalette` is called
/// once per build by `TimbreApp` before the theme is built.
class TimbreColors {
const TimbreColors._();
static TimbrePalette _active = TimbrePalette.dark;
/// Swap the active palette. Called by `TimbreApp.build` from the persisted
/// theme setting; a re-keyed subtree remount then re-reads these getters.
static void applyPalette(TimbrePalette p) => _active = p;
/// Brightness of the active theme (drives `ThemeData`/`ColorScheme`).
static Brightness get brightness => _active.brightness;
/// App canvas.
static Color get background => _active.background;
/// Panel/surface fill (slightly offset from the canvas).
static Color get surface => _active.surface;
/// Primary text.
static Color get foreground => _active.foreground;
/// Secondary/muted text.
static Color get dimmed => _active.dimmed;
/// Hairline borders (inactive).
static Color get border => _active.border;
/// Hairline borders when a pane is focused.
static Color get borderActive => _active.borderActive;
/// Default accent when no art-derived accent applies.
static Color get accentDefault => _active.accentDefault;
}
/// Spacing scale — dense, "player as instrument" rather than airy mobile cards.
class TimbreSpacing {
const TimbreSpacing._();
static const double xs = 2;
static const double sm = 4;
static const double md = 8;
static const double lg = 12;
static const double xl = 16;
/// Minimum touch target per Apple/Material guidance — keep rows dense
/// visually but pad hit areas to at least this.
static const double minTouchTarget = 44;
}