Files
BadNote/lib/theme/app_theme.dart

111 lines
4.0 KiB
Dart
Raw Permalink Normal View History

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
/// BadNote design tokens — ink-desk academic aesthetic.
/// Warm paper field, graphite ink, single copper accent. No Inter / no purple.
abstract final class AppTokens {
static const Color paper = Color(0xFFF3EDE3);
static const Color paperDark = Color(0xFF1C1A17);
static const Color ink = Color(0xFF1F1B16);
static const Color inkMuted = Color(0xFF6B6358);
static const Color rule = Color(0xFFD9CFC0);
static const Color copper = Color(0xFFB45A2A);
static const Color copperSoft = Color(0xFFE8C4AE);
static const Color sticky = Color(0xFFF6E7A5);
static const double radiusSm = 6;
static const double radiusMd = 12;
static const double chromePad = 16;
}
abstract final class AppTheme {
static ThemeData light() {
final base = ColorScheme.fromSeed(
seedColor: AppTokens.copper,
brightness: Brightness.light,
surface: AppTokens.paper,
primary: AppTokens.copper,
onPrimary: Colors.white,
onSurface: AppTokens.ink,
secondary: AppTokens.inkMuted,
);
return _build(base, Brightness.light);
}
static ThemeData dark() {
final base = ColorScheme.fromSeed(
seedColor: AppTokens.copperSoft,
brightness: Brightness.dark,
surface: AppTokens.paperDark,
primary: AppTokens.copperSoft,
onSurface: AppTokens.paper,
);
return _build(base, Brightness.dark);
}
static ThemeData fromScheme(ColorScheme scheme) =>
_build(scheme, scheme.brightness);
static ThemeData _build(ColorScheme scheme, Brightness brightness) {
final display = GoogleFonts.sourceSerif4TextTheme(
ThemeData(brightness: brightness).textTheme,
);
final ui = GoogleFonts.ibmPlexSansTextTheme(
ThemeData(brightness: brightness).textTheme,
);
final merged = ui.copyWith(
displayLarge: display.displayLarge?.copyWith(fontWeight: FontWeight.w600),
displayMedium: display.displayMedium?.copyWith(fontWeight: FontWeight.w600),
displaySmall: display.displaySmall?.copyWith(fontWeight: FontWeight.w600),
headlineLarge: display.headlineLarge?.copyWith(fontWeight: FontWeight.w600),
headlineMedium: display.headlineMedium?.copyWith(fontWeight: FontWeight.w600),
headlineSmall: display.headlineSmall?.copyWith(fontWeight: FontWeight.w600),
titleLarge: display.titleLarge?.copyWith(fontWeight: FontWeight.w600),
);
return ThemeData(
useMaterial3: true,
colorScheme: scheme,
scaffoldBackgroundColor: scheme.surface,
textTheme: merged,
appBarTheme: AppBarTheme(
centerTitle: false,
backgroundColor: scheme.surface,
foregroundColor: scheme.onSurface,
elevation: 0,
scrolledUnderElevation: 0.5,
titleTextStyle: display.titleLarge?.copyWith(
color: scheme.onSurface,
fontWeight: FontWeight.w600,
),
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: scheme.surface,
indicatorColor: AppTokens.copper.withValues(alpha: 0.18),
labelTextStyle: WidgetStatePropertyAll(
ui.labelMedium?.copyWith(fontWeight: FontWeight.w600),
),
),
navigationRailTheme: NavigationRailThemeData(
backgroundColor: scheme.surface,
indicatorColor: AppTokens.copper.withValues(alpha: 0.18),
selectedIconTheme: IconThemeData(color: scheme.primary),
unselectedIconTheme: IconThemeData(color: scheme.onSurfaceVariant),
),
cardTheme: CardThemeData(
color: scheme.surface,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppTokens.radiusMd),
side: BorderSide(color: AppTokens.rule.withValues(alpha: 0.7)),
),
),
dividerColor: AppTokens.rule,
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: scheme.primary,
foregroundColor: scheme.onPrimary,
),
);
}
}