From 52db9d7ea60387ff3f8d204fd5f81bea7db00083 Mon Sep 17 00:00:00 2001 From: Johan Jongsma Date: Sun, 1 Feb 2026 08:38:12 +0000 Subject: [PATCH] feat: Update theme to match inou styleguide - light theme with amber accent --- lib/core/theme.dart | 395 ++++++++++++++++++++++++++++++++++++++++++-- lib/main.dart | 4 +- 2 files changed, 382 insertions(+), 17 deletions(-) diff --git a/lib/core/theme.dart b/lib/core/theme.dart index 6816043..b522b5c 100644 --- a/lib/core/theme.dart +++ b/lib/core/theme.dart @@ -1,31 +1,394 @@ import 'package:flutter/material.dart'; -/// App theme configuration -class AppTheme { - static const Color primaryColor = Color(0xFF6366F1); // Indigo - static const Color secondaryColor = Color(0xFF8B5CF6); // Violet - static const Color backgroundColor = Color(0xFF0F172A); // Slate 900 - static const Color surfaceColor = Color(0xFF1E293B); // Slate 800 - static const Color textColor = Color(0xFFF8FAFC); // Slate 50 +/// inou brand colors from styleguide +class InouColors { + // Primary brand colors + static const Color accent = Color(0xFFB45309); // Amber - primary accent + static const Color accentLight = Color(0xFFD97706); // Lighter amber for hover states + // Text colors + static const Color textPrimary = Color(0xFF1C1917); // Stone 900 + static const Color textMuted = Color(0xFF78716C); // Stone 500 + static const Color textOnAccent = Color(0xFFFFFFFF); // White on accent + + // Background colors (light theme) + static const Color background = Color(0xFFF8F7F6); // Warm off-white + static const Color surface = Color(0xFFFFFFFF); // Pure white for cards + static const Color surfaceElevated = Color(0xFFFAFAF9); // Slightly elevated + + // Background colors (dark theme) + static const Color backgroundDark = Color(0xFF1C1917); + static const Color surfaceDark = Color(0xFF292524); + static const Color textPrimaryDark = Color(0xFFF5F5F4); + static const Color textMutedDark = Color(0xFFA8A29E); + + // Semantic colors + static const Color success = Color(0xFF059669); // Emerald + static const Color danger = Color(0xFFDC2626); // Red + static const Color info = Color(0xFF3B82F6); // Blue + static const Color warning = Color(0xFFF59E0B); // Amber + + // Status colors + static const Color care = Color(0xFF8B5CF6); // Violet - care status + static const Color processing = Color(0xFF6366F1); // Indigo - processing +} + +/// App theme configuration following inou styleguide +class AppTheme { + // Expose colors for direct access + static const Color primaryColor = InouColors.accent; + static const Color backgroundColor = InouColors.background; + static const Color surfaceColor = InouColors.surface; + static const Color textColor = InouColors.textPrimary; + + // Dark theme colors + static const Color backgroundColorDark = InouColors.backgroundDark; + static const Color surfaceColorDark = InouColors.surfaceDark; + static const Color textColorDark = InouColors.textPrimaryDark; + + /// Light theme (default, matches inou.com) + static ThemeData get lightTheme => ThemeData( + useMaterial3: true, + brightness: Brightness.light, + colorScheme: ColorScheme.light( + primary: InouColors.accent, + secondary: InouColors.accentLight, + surface: InouColors.surface, + error: InouColors.danger, + ), + scaffoldBackgroundColor: InouColors.background, + appBarTheme: const AppBarTheme( + backgroundColor: InouColors.background, + foregroundColor: InouColors.textPrimary, + elevation: 0, + centerTitle: false, + titleTextStyle: TextStyle( + color: InouColors.textPrimary, + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + bottomNavigationBarTheme: const BottomNavigationBarThemeData( + backgroundColor: InouColors.surface, + selectedItemColor: InouColors.accent, + unselectedItemColor: InouColors.textMuted, + elevation: 8, + ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: InouColors.surface, + indicatorColor: InouColors.accent.withOpacity(0.15), + labelTextStyle: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const TextStyle( + color: InouColors.accent, + fontSize: 12, + fontWeight: FontWeight.w600, + ); + } + return const TextStyle( + color: InouColors.textMuted, + fontSize: 12, + fontWeight: FontWeight.w500, + ); + }), + iconTheme: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const IconThemeData(color: InouColors.accent); + } + return const IconThemeData(color: InouColors.textMuted); + }), + ), + cardTheme: CardTheme( + color: InouColors.surface, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: BorderSide(color: InouColors.textMuted.withOpacity(0.1)), + ), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: InouColors.surface, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: InouColors.textMuted.withOpacity(0.2)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: InouColors.textMuted.withOpacity(0.2)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: InouColors.accent, width: 2), + ), + labelStyle: const TextStyle(color: InouColors.textMuted), + hintStyle: TextStyle(color: InouColors.textMuted.withOpacity(0.7)), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: InouColors.accent, + foregroundColor: InouColors.textOnAccent, + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + textStyle: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: InouColors.accent, + textStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + textTheme: const TextTheme( + displayLarge: TextStyle( + fontSize: 40, + fontWeight: FontWeight.w700, + color: InouColors.textPrimary, + ), + headlineLarge: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w700, + color: InouColors.textPrimary, + ), + headlineMedium: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w600, + color: InouColors.textPrimary, + ), + headlineSmall: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: InouColors.textPrimary, + ), + titleLarge: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: InouColors.textPrimary, + ), + titleMedium: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: InouColors.textPrimary, + ), + bodyLarge: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: InouColors.textPrimary, + ), + bodyMedium: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: InouColors.textPrimary, + ), + bodySmall: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + color: InouColors.textMuted, + ), + labelLarge: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: InouColors.textPrimary, + letterSpacing: 0.5, + ), + labelSmall: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: InouColors.textMuted, + letterSpacing: 1.0, + ), + ), + dividerTheme: DividerThemeData( + color: InouColors.textMuted.withOpacity(0.1), + thickness: 1, + ), + snackBarTheme: SnackBarThemeData( + backgroundColor: InouColors.textPrimary, + contentTextStyle: const TextStyle(color: Colors.white), + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + ); + + /// Dark theme static ThemeData get darkTheme => ThemeData( useMaterial3: true, brightness: Brightness.dark, colorScheme: ColorScheme.dark( - primary: primaryColor, - secondary: secondaryColor, - surface: surfaceColor, + primary: InouColors.accent, + secondary: InouColors.accentLight, + surface: InouColors.surfaceDark, + error: InouColors.danger, ), - scaffoldBackgroundColor: backgroundColor, + scaffoldBackgroundColor: InouColors.backgroundDark, appBarTheme: const AppBarTheme( - backgroundColor: backgroundColor, - foregroundColor: textColor, + backgroundColor: InouColors.backgroundDark, + foregroundColor: InouColors.textPrimaryDark, elevation: 0, + centerTitle: false, + titleTextStyle: TextStyle( + color: InouColors.textPrimaryDark, + fontSize: 20, + fontWeight: FontWeight.w600, + ), ), bottomNavigationBarTheme: const BottomNavigationBarThemeData( - backgroundColor: surfaceColor, - selectedItemColor: primaryColor, - unselectedItemColor: Colors.grey, + backgroundColor: InouColors.surfaceDark, + selectedItemColor: InouColors.accent, + unselectedItemColor: InouColors.textMutedDark, + elevation: 8, + ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: InouColors.surfaceDark, + indicatorColor: InouColors.accent.withOpacity(0.2), + labelTextStyle: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const TextStyle( + color: InouColors.accent, + fontSize: 12, + fontWeight: FontWeight.w600, + ); + } + return const TextStyle( + color: InouColors.textMutedDark, + fontSize: 12, + fontWeight: FontWeight.w500, + ); + }), + iconTheme: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const IconThemeData(color: InouColors.accent); + } + return const IconThemeData(color: InouColors.textMutedDark); + }), + ), + cardTheme: CardTheme( + color: InouColors.surfaceDark, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: BorderSide(color: InouColors.textMutedDark.withOpacity(0.15)), + ), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: InouColors.surfaceDark, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: InouColors.textMutedDark.withOpacity(0.3)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: InouColors.textMutedDark.withOpacity(0.3)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: InouColors.accent, width: 2), + ), + labelStyle: const TextStyle(color: InouColors.textMutedDark), + hintStyle: TextStyle(color: InouColors.textMutedDark.withOpacity(0.7)), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: InouColors.accent, + foregroundColor: InouColors.textOnAccent, + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + textStyle: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: InouColors.accent, + textStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + textTheme: const TextTheme( + displayLarge: TextStyle( + fontSize: 40, + fontWeight: FontWeight.w700, + color: InouColors.textPrimaryDark, + ), + headlineLarge: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w700, + color: InouColors.textPrimaryDark, + ), + headlineMedium: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w600, + color: InouColors.textPrimaryDark, + ), + headlineSmall: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: InouColors.textPrimaryDark, + ), + titleLarge: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: InouColors.textPrimaryDark, + ), + titleMedium: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: InouColors.textPrimaryDark, + ), + bodyLarge: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: InouColors.textPrimaryDark, + ), + bodyMedium: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: InouColors.textPrimaryDark, + ), + bodySmall: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + color: InouColors.textMutedDark, + ), + labelLarge: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: InouColors.textPrimaryDark, + letterSpacing: 0.5, + ), + labelSmall: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: InouColors.textMutedDark, + letterSpacing: 1.0, + ), + ), + dividerTheme: DividerThemeData( + color: InouColors.textMutedDark.withOpacity(0.15), + thickness: 1, + ), + snackBarTheme: SnackBarThemeData( + backgroundColor: InouColors.surfaceDark, + contentTextStyle: const TextStyle(color: InouColors.textPrimaryDark), + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), ); } diff --git a/lib/main.dart b/lib/main.dart index 31ccf49..0370b77 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -61,7 +61,9 @@ class _InouAppState extends State { return MaterialApp( navigatorKey: InouApp.navigatorKey, title: AppConfig.appName, - theme: AppTheme.darkTheme, + theme: AppTheme.lightTheme, + darkTheme: AppTheme.darkTheme, + themeMode: ThemeMode.light, // Use light theme to match inou.com debugShowCheckedModeBanner: false, // TODO: Re-enable AuthGate for production // home: const AuthGate(