/// Mindful Values Architecture theme — warm surfaces with high-contrast type. library; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; /// Builds [ThemeData] tuned for readable contrast on warm paper surfaces. abstract final class AppTheme { /// Page canvas — light paper, clearly lighter than elevated cards. static const Color surface = Color(0xFFFFFBF5); /// Elevated cards / sheets — distinct from the canvas. static const Color surfaceContainerLow = Color(0xFFFFFFFF); static const Color surfaceContainer = Color(0xFFF3EDE4); static const Color surfaceContainerHigh = Color(0xFFE9E2D7); static const Color surfaceContainerHighest = Color(0xFFDED6C9); /// Near-black espresso for primary copy (≈16:1 on surface). static const Color onSurface = Color(0xFF1A1612); /// Secondary copy — still strong on cream (≈9:1). static const Color onSurfaceVariant = Color(0xFF3F3832); /// Icons / meta — darker than the old stone grey. static const Color outline = Color(0xFF5C534B); static const Color outlineVariant = Color(0xFFC4B8AA); static const Color primary = Color(0xFF4F4032); static const Color primaryContainer = Color(0xFF6B5848); static const Color onPrimary = Color(0xFFFFFBF5); static const Color secondary = Color(0xFF8A3F1C); static const Color secondaryContainer = Color(0xFFFFD4BC); static const Color tertiary = Color(0xFF3A5238); static const Color inverseSurface = Color(0xFF25211D); static ThemeData light() { const colorScheme = ColorScheme( brightness: Brightness.light, primary: primary, onPrimary: onPrimary, primaryContainer: primaryContainer, onPrimaryContainer: Color(0xFFFFF3E8), secondary: secondary, onSecondary: onPrimary, secondaryContainer: secondaryContainer, onSecondaryContainer: Color(0xFF4A1E08), tertiary: tertiary, onTertiary: onPrimary, tertiaryContainer: Color(0xFF5A7358), onTertiaryContainer: Color(0xFFEAF6E7), error: Color(0xFFB3261E), onError: onPrimary, errorContainer: Color(0xFFFFDAD6), onErrorContainer: Color(0xFF410002), surface: surface, onSurface: onSurface, surfaceContainerHighest: surfaceContainerHighest, surfaceContainerHigh: surfaceContainerHigh, surfaceContainer: surfaceContainer, surfaceContainerLow: surfaceContainerLow, surfaceContainerLowest: Color(0xFFFFFFFF), onSurfaceVariant: onSurfaceVariant, outline: outline, outlineVariant: outlineVariant, inverseSurface: inverseSurface, onInverseSurface: Color(0xFFF7F1E8), inversePrimary: Color(0xFFDCC4A8), shadow: Color(0xFF1A1612), scrim: Color(0xFF1A1612), ); final textTheme = _textTheme(colorScheme); return ThemeData( useMaterial3: true, colorScheme: colorScheme, textTheme: textTheme, scaffoldBackgroundColor: surface, appBarTheme: AppBarTheme( centerTitle: false, elevation: 0, scrolledUnderElevation: 0, backgroundColor: surface, foregroundColor: onSurface, titleTextStyle: textTheme.titleLarge, ), cardTheme: CardThemeData( elevation: 0, color: surfaceContainerLow, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), side: const BorderSide(color: outlineVariant, width: 1.2), ), clipBehavior: Clip.antiAlias, margin: EdgeInsets.zero, ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: Colors.white, hintStyle: textTheme.bodyMedium?.copyWith( color: outline.withValues(alpha: 0.85), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: outlineVariant, width: 1.2), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: outlineVariant, width: 1.2), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: const BorderSide(color: primary, width: 1.8), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( minimumSize: const Size.fromHeight(52), backgroundColor: primary, foregroundColor: onPrimary, shape: const StadiumBorder(), textStyle: textTheme.labelLarge?.copyWith(color: onPrimary), elevation: 0, ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: onSurface, textStyle: textTheme.labelMedium?.copyWith( color: onSurface, fontWeight: FontWeight.w600, ), shape: const StadiumBorder(), ), ), chipTheme: ChipThemeData( backgroundColor: Colors.white, selectedColor: surfaceContainerHigh, side: const BorderSide(color: outlineVariant, width: 1.2), shape: const StadiumBorder(), labelStyle: textTheme.labelMedium?.copyWith(color: onSurface), secondaryLabelStyle: textTheme.labelMedium?.copyWith(color: onSurface), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2), ), bottomSheetTheme: const BottomSheetThemeData( backgroundColor: surfaceContainerLow, surfaceTintColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(32)), ), showDragHandle: true, ), dividerTheme: const DividerThemeData( color: outlineVariant, space: 1, thickness: 1, ), listTileTheme: const ListTileThemeData( textColor: onSurface, iconColor: onSurfaceVariant, ), snackBarTheme: SnackBarThemeData( behavior: SnackBarBehavior.floating, backgroundColor: inverseSurface, contentTextStyle: textTheme.bodyMedium?.copyWith( color: const Color(0xFFF7F1E8), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), ); } /// Soft dark mirror — elevated cards stay darker than text for contrast. static ThemeData dark() { const darkScheme = ColorScheme( brightness: Brightness.dark, primary: Color(0xFFE2CDB6), onPrimary: Color(0xFF2A2118), primaryContainer: Color(0xFF6B5848), onPrimaryContainer: Color(0xFFFFF3E8), secondary: Color(0xFFFFB691), onSecondary: Color(0xFF4A1E08), secondaryContainer: Color(0xFF6B3014), onSecondaryContainer: Color(0xFFFFD4BC), tertiary: Color(0xFFB5CDB0), onTertiary: Color(0xFF1A2A1A), tertiaryContainer: Color(0xFF3A5238), onTertiaryContainer: Color(0xFFEAF6E7), error: Color(0xFFFFB4AB), onError: Color(0xFF690005), errorContainer: Color(0xFF93000A), onErrorContainer: Color(0xFFFFDAD6), surface: Color(0xFF161310), onSurface: Color(0xFFF5EFE6), surfaceContainerHighest: Color(0xFF3A342E), surfaceContainerHigh: Color(0xFF302B26), surfaceContainer: Color(0xFF26221E), surfaceContainerLow: Color(0xFF221E1A), surfaceContainerLowest: Color(0xFF0E0C0A), onSurfaceVariant: Color(0xFFD9CEC2), outline: Color(0xFFB0A397), outlineVariant: Color(0xFF4A433C), inverseSurface: Color(0xFFF5EFE6), onInverseSurface: Color(0xFF1C1916), inversePrimary: Color(0xFF4F4032), shadow: Color(0xFF000000), scrim: Color(0xFF000000), ); final textTheme = _textTheme(darkScheme); return ThemeData( useMaterial3: true, colorScheme: darkScheme, textTheme: textTheme, scaffoldBackgroundColor: darkScheme.surface, appBarTheme: AppBarTheme( centerTitle: false, elevation: 0, scrolledUnderElevation: 0, backgroundColor: darkScheme.surface, foregroundColor: darkScheme.onSurface, titleTextStyle: textTheme.titleLarge, ), cardTheme: CardThemeData( elevation: 0, color: darkScheme.surfaceContainerLow, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), side: BorderSide(color: darkScheme.outlineVariant, width: 1.2), ), ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: darkScheme.surfaceContainer, hintStyle: textTheme.bodyMedium?.copyWith( color: darkScheme.outline.withValues(alpha: 0.9), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide(color: darkScheme.outlineVariant, width: 1.2), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide(color: darkScheme.outlineVariant, width: 1.2), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide(color: darkScheme.primary, width: 1.8), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( minimumSize: const Size.fromHeight(52), backgroundColor: darkScheme.primary, foregroundColor: darkScheme.onPrimary, shape: const StadiumBorder(), textStyle: textTheme.labelLarge?.copyWith(color: darkScheme.onPrimary), elevation: 0, ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: darkScheme.onSurface, textStyle: textTheme.labelMedium?.copyWith( color: darkScheme.onSurface, fontWeight: FontWeight.w600, ), shape: const StadiumBorder(), ), ), chipTheme: ChipThemeData( backgroundColor: darkScheme.surfaceContainer, selectedColor: darkScheme.surfaceContainerHigh, side: BorderSide(color: darkScheme.outlineVariant, width: 1.2), shape: const StadiumBorder(), labelStyle: textTheme.labelMedium?.copyWith(color: darkScheme.onSurface), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2), ), bottomSheetTheme: BottomSheetThemeData( backgroundColor: darkScheme.surfaceContainerLow, surfaceTintColor: Colors.transparent, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(32)), ), showDragHandle: true, ), listTileTheme: ListTileThemeData( textColor: darkScheme.onSurface, iconColor: darkScheme.onSurfaceVariant, ), ); } static TextTheme _textTheme(ColorScheme colors) { final newsreader = GoogleFonts.newsreaderTextTheme(); final jakarta = GoogleFonts.plusJakartaSansTextTheme(); return TextTheme( displaySmall: newsreader.displaySmall?.copyWith( fontSize: 32, height: 40 / 32, fontWeight: FontWeight.w500, letterSpacing: -0.32, color: colors.onSurface, ), headlineMedium: newsreader.headlineMedium?.copyWith( fontSize: 26, height: 34 / 26, fontWeight: FontWeight.w500, color: colors.onSurface, ), headlineSmall: newsreader.headlineSmall?.copyWith( fontSize: 22, height: 30 / 22, fontWeight: FontWeight.w500, color: colors.onSurface, ), titleLarge: newsreader.titleLarge?.copyWith( fontSize: 22, height: 30 / 22, fontWeight: FontWeight.w500, color: colors.onSurface, ), titleMedium: jakarta.titleMedium?.copyWith( fontSize: 16, fontWeight: FontWeight.w700, color: colors.onSurface, ), bodyLarge: jakarta.bodyLarge?.copyWith( fontSize: 16, height: 24 / 16, fontWeight: FontWeight.w500, color: colors.onSurface, ), bodyMedium: jakarta.bodyMedium?.copyWith( fontSize: 14, height: 20 / 14, fontWeight: FontWeight.w500, color: colors.onSurfaceVariant, ), bodySmall: jakarta.bodySmall?.copyWith( fontSize: 12, height: 16 / 12, fontWeight: FontWeight.w500, color: colors.onSurfaceVariant, ), labelLarge: jakarta.labelLarge?.copyWith( fontSize: 15, height: 20 / 15, fontWeight: FontWeight.w700, letterSpacing: 0.15, color: colors.onPrimary, ), labelMedium: jakarta.labelMedium?.copyWith( fontSize: 13, height: 18 / 13, fontWeight: FontWeight.w600, letterSpacing: 0.2, color: colors.onSurfaceVariant, ), labelSmall: jakarta.labelSmall?.copyWith( fontSize: 11, height: 16 / 11, fontWeight: FontWeight.w700, letterSpacing: 0.4, color: colors.onSurfaceVariant, ), ); } }