feat: Update theme to match inou styleguide - light theme with amber accent

This commit is contained in:
Johan Jongsma 2026-02-01 08:38:12 +00:00
parent a6aca36522
commit 52db9d7ea6
2 changed files with 382 additions and 17 deletions

View File

@ -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)),
),
);
}

View File

@ -61,7 +61,9 @@ class _InouAppState extends State<InouApp> {
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(