feat(tokens): add design tokens, type scale, push scaffold, and root nav state
This commit is contained in:
@@ -3,16 +3,21 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import 'pluriwave_motion.dart';
|
||||
import 'pluriwave_tokens.dart';
|
||||
import 'pluriwave_typography.dart';
|
||||
|
||||
abstract final class PluriWaveTheme {
|
||||
static ThemeData dark() {
|
||||
const tokens = PluriWaveTokens.dark;
|
||||
final baseTextTheme = GoogleFonts.plusJakartaSansTextTheme(
|
||||
ThemeData.dark().textTheme,
|
||||
);
|
||||
final typography = PluriWaveTypography.from(baseTextTheme);
|
||||
final colorScheme = const ColorScheme.dark().copyWith(
|
||||
primary: tokens.electricMagenta,
|
||||
secondary: const Color(0xFF7EE4C2),
|
||||
secondary: tokens.liveGreen,
|
||||
tertiary: tokens.warmCoral,
|
||||
surface: const Color(0xFF0D1B24),
|
||||
surfaceContainerLow: const Color(0xFF102532),
|
||||
surfaceContainerLow: tokens.listSurface,
|
||||
surfaceContainerHighest: const Color(0xFF1B3942),
|
||||
onSurface: const Color(0xFFF2F7FA),
|
||||
onPrimary: Colors.white,
|
||||
@@ -22,10 +27,12 @@ abstract final class PluriWaveTheme {
|
||||
useMaterial3: true,
|
||||
colorScheme: colorScheme,
|
||||
scaffoldBackgroundColor: tokens.deepViolet,
|
||||
textTheme: GoogleFonts.plusJakartaSansTextTheme(
|
||||
ThemeData.dark().textTheme,
|
||||
),
|
||||
extensions: const <ThemeExtension<dynamic>>[tokens, PluriWaveMotion.dark],
|
||||
textTheme: baseTextTheme,
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
tokens,
|
||||
PluriWaveMotion.dark,
|
||||
typography,
|
||||
],
|
||||
appBarTheme: const AppBarTheme(
|
||||
centerTitle: false,
|
||||
backgroundColor: Colors.transparent,
|
||||
@@ -69,4 +76,8 @@ extension PluriWaveThemeContextX on BuildContext {
|
||||
|
||||
PluriWaveMotion get pluriMotion =>
|
||||
Theme.of(this).extension<PluriWaveMotion>() ?? PluriWaveMotion.dark;
|
||||
|
||||
PluriWaveTypography get pluriType =>
|
||||
Theme.of(this).extension<PluriWaveTypography>() ??
|
||||
PluriWaveTypography.from(Theme.of(this).textTheme);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ class PluriWaveTokens extends ThemeExtension<PluriWaveTokens> {
|
||||
required this.glassSurface,
|
||||
required this.glassBorder,
|
||||
required this.glowColor,
|
||||
required this.listSurface,
|
||||
required this.liveGreen,
|
||||
required this.offlineAccent,
|
||||
required this.radiusSm,
|
||||
required this.radiusMd,
|
||||
required this.radiusLg,
|
||||
@@ -27,6 +30,21 @@ class PluriWaveTokens extends ThemeExtension<PluriWaveTokens> {
|
||||
final Color glassBorder;
|
||||
final Color glowColor;
|
||||
|
||||
/// Redesign additions (Design ADR-1). Row/card surface for the flat lists
|
||||
/// introduced by the redesign (Favoritos, Buscar results). De-literalised
|
||||
/// from `pluriwave_theme.dart`'s previous `surfaceContainerLow` literal —
|
||||
/// same value, now named — so rendered output stays byte-identical.
|
||||
final Color listSurface;
|
||||
|
||||
/// "EN DIRECTO" indicator and the Escuchar hero's waveform colour.
|
||||
/// De-literalised from `pluriwave_theme.dart`'s previous `secondary`
|
||||
/// literal — same value, now named.
|
||||
final Color liveGreen;
|
||||
|
||||
/// Offline/reconnect banner accent (WU16). No prior literal to promote —
|
||||
/// a genuinely new colour.
|
||||
final Color offlineAccent;
|
||||
|
||||
final double radiusSm;
|
||||
final double radiusMd;
|
||||
final double radiusLg;
|
||||
@@ -53,6 +71,9 @@ class PluriWaveTokens extends ThemeExtension<PluriWaveTokens> {
|
||||
glassSurface: Color(0x1FFFFFFF),
|
||||
glassBorder: Color(0x33FFFFFF),
|
||||
glowColor: Color(0x6621D4D9),
|
||||
listSurface: Color(0xFF102532),
|
||||
liveGreen: Color(0xFF7EE4C2),
|
||||
offlineAccent: Color(0xFF94A3B8),
|
||||
radiusSm: 14,
|
||||
radiusMd: 22,
|
||||
radiusLg: 30,
|
||||
@@ -70,6 +91,9 @@ class PluriWaveTokens extends ThemeExtension<PluriWaveTokens> {
|
||||
Color? glassSurface,
|
||||
Color? glassBorder,
|
||||
Color? glowColor,
|
||||
Color? listSurface,
|
||||
Color? liveGreen,
|
||||
Color? offlineAccent,
|
||||
double? radiusSm,
|
||||
double? radiusMd,
|
||||
double? radiusLg,
|
||||
@@ -85,6 +109,9 @@ class PluriWaveTokens extends ThemeExtension<PluriWaveTokens> {
|
||||
glassSurface: glassSurface ?? this.glassSurface,
|
||||
glassBorder: glassBorder ?? this.glassBorder,
|
||||
glowColor: glowColor ?? this.glowColor,
|
||||
listSurface: listSurface ?? this.listSurface,
|
||||
liveGreen: liveGreen ?? this.liveGreen,
|
||||
offlineAccent: offlineAccent ?? this.offlineAccent,
|
||||
radiusSm: radiusSm ?? this.radiusSm,
|
||||
radiusMd: radiusMd ?? this.radiusMd,
|
||||
radiusLg: radiusLg ?? this.radiusLg,
|
||||
@@ -111,6 +138,10 @@ class PluriWaveTokens extends ThemeExtension<PluriWaveTokens> {
|
||||
Color.lerp(glassSurface, other.glassSurface, t) ?? glassSurface,
|
||||
glassBorder: Color.lerp(glassBorder, other.glassBorder, t) ?? glassBorder,
|
||||
glowColor: Color.lerp(glowColor, other.glowColor, t) ?? glowColor,
|
||||
listSurface: Color.lerp(listSurface, other.listSurface, t) ?? listSurface,
|
||||
liveGreen: Color.lerp(liveGreen, other.liveGreen, t) ?? liveGreen,
|
||||
offlineAccent:
|
||||
Color.lerp(offlineAccent, other.offlineAccent, t) ?? offlineAccent,
|
||||
radiusSm: lerpDouble(radiusSm, other.radiusSm, t) ?? radiusSm,
|
||||
radiusMd: lerpDouble(radiusMd, other.radiusMd, t) ?? radiusMd,
|
||||
radiusLg: lerpDouble(radiusLg, other.radiusLg, t) ?? radiusLg,
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Design ADR-1: the named type scale, split out of [PluriWaveTokens] into
|
||||
/// its own ThemeExtension so screens stop copy-pasting
|
||||
/// `.copyWith(fontWeight: w900, letterSpacing: …)`. The set is closed at six
|
||||
/// styles — a seventh requires amending ADR-1.
|
||||
///
|
||||
/// [eyebrowLabel] never applies `toUpperCase()` — casing is a
|
||||
/// locale-hostile transform (Turkish dotless i, and scripts with no case at
|
||||
/// all). The style carries weight/letter-spacing only; each ARB value is
|
||||
/// authored in its own display form.
|
||||
@immutable
|
||||
class PluriWaveTypography extends ThemeExtension<PluriWaveTypography> {
|
||||
const PluriWaveTypography({
|
||||
required this.heroTime,
|
||||
required this.sectionTitle,
|
||||
required this.screenTitle,
|
||||
required this.cardTitle,
|
||||
required this.bodyStrong,
|
||||
required this.eyebrowLabel,
|
||||
});
|
||||
|
||||
/// 88 / w800 / height 1.0 / ls -2.0. Alarm ringing, inline time editor.
|
||||
/// Every call site must wrap this in `FittedBox(fit: BoxFit.scaleDown)` —
|
||||
/// it renders at 176px under a 2.0 text scaler.
|
||||
final TextStyle heroTime;
|
||||
|
||||
/// 23 / w800 / ls -0.6. Root screen section headings.
|
||||
final TextStyle sectionTitle;
|
||||
|
||||
/// 19 / w800 / ls -0.4. PluriPushScaffold header title.
|
||||
final TextStyle screenTitle;
|
||||
|
||||
/// 14.5 / w700. Station cards, settings rows, alarm cards.
|
||||
final TextStyle cardTitle;
|
||||
|
||||
/// 13 / w600. Card subtitles, meta lines.
|
||||
final TextStyle bodyStrong;
|
||||
|
||||
/// 11 / w800 / ls 0.8. "EN DIRECTO", "PROGRAMADOS", settings group
|
||||
/// headers.
|
||||
final TextStyle eyebrowLabel;
|
||||
|
||||
/// Builds the scale from [family] — the SAME resolved Google Fonts
|
||||
/// TextTheme instance [PluriWaveTheme.dark] already computes. Building the
|
||||
/// styles anywhere else would re-merge the font family, which is the
|
||||
/// copy-paste this extension exists to eliminate.
|
||||
factory PluriWaveTypography.from(TextTheme family) {
|
||||
final fallback = family.bodyMedium ?? const TextStyle();
|
||||
TextStyle style(
|
||||
double fontSize,
|
||||
FontWeight fontWeight, {
|
||||
double? letterSpacing,
|
||||
double? height,
|
||||
}) {
|
||||
return fallback.copyWith(
|
||||
fontSize: fontSize,
|
||||
fontWeight: fontWeight,
|
||||
letterSpacing: letterSpacing,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
|
||||
return PluriWaveTypography(
|
||||
heroTime: style(88, FontWeight.w800, letterSpacing: -2.0, height: 1.0),
|
||||
sectionTitle: style(23, FontWeight.w800, letterSpacing: -0.6),
|
||||
screenTitle: style(19, FontWeight.w800, letterSpacing: -0.4),
|
||||
cardTitle: style(14.5, FontWeight.w700),
|
||||
bodyStrong: style(13, FontWeight.w600),
|
||||
eyebrowLabel: style(11, FontWeight.w800, letterSpacing: 0.8),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
PluriWaveTypography copyWith({
|
||||
TextStyle? heroTime,
|
||||
TextStyle? sectionTitle,
|
||||
TextStyle? screenTitle,
|
||||
TextStyle? cardTitle,
|
||||
TextStyle? bodyStrong,
|
||||
TextStyle? eyebrowLabel,
|
||||
}) {
|
||||
return PluriWaveTypography(
|
||||
heroTime: heroTime ?? this.heroTime,
|
||||
sectionTitle: sectionTitle ?? this.sectionTitle,
|
||||
screenTitle: screenTitle ?? this.screenTitle,
|
||||
cardTitle: cardTitle ?? this.cardTitle,
|
||||
bodyStrong: bodyStrong ?? this.bodyStrong,
|
||||
eyebrowLabel: eyebrowLabel ?? this.eyebrowLabel,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
PluriWaveTypography lerp(
|
||||
covariant ThemeExtension<PluriWaveTypography>? other,
|
||||
double t,
|
||||
) {
|
||||
if (other is! PluriWaveTypography) return this;
|
||||
return PluriWaveTypography(
|
||||
heroTime: TextStyle.lerp(heroTime, other.heroTime, t) ?? heroTime,
|
||||
sectionTitle:
|
||||
TextStyle.lerp(sectionTitle, other.sectionTitle, t) ?? sectionTitle,
|
||||
screenTitle:
|
||||
TextStyle.lerp(screenTitle, other.screenTitle, t) ?? screenTitle,
|
||||
cardTitle: TextStyle.lerp(cardTitle, other.cardTitle, t) ?? cardTitle,
|
||||
bodyStrong: TextStyle.lerp(bodyStrong, other.bodyStrong, t) ?? bodyStrong,
|
||||
eyebrowLabel:
|
||||
TextStyle.lerp(eyebrowLabel, other.eyebrowLabel, t) ?? eyebrowLabel,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user