- Overhaul theme system with Svrnty design and WCAG AAA compliance - Remove android, macos, and web platform files (iOS-only focus) - Update components with improved dark mode map and UI refinements - Enhance settings page with additional configuration options - Add theme system documentation in lib/theme/README.md - Update CLAUDE.md with comprehensive theme guidelines Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
9.2 KiB
Svrnty Theme System - Quick Reference
Standard Color Access Pattern
ALWAYS use:
final colorScheme = Theme.of(context).colorScheme;
// Primary UI
colorScheme.primary // Primary brand color (Crimson Red)
colorScheme.onPrimary // Text on primary (white/black depending on theme)
// Secondary UI
colorScheme.secondary // Secondary brand color (Slate Blue)
colorScheme.onSecondary // Text on secondary
// Text
colorScheme.onSurface // Primary text
colorScheme.onSurfaceVariant // Secondary text
// Backgrounds
colorScheme.surface // Page background
colorScheme.surfaceContainer // Card background
// Status (specialized)
StatusColorScheme.getStatusColor(status)
StatusColorScheme.getStatusColorFromTheme(status, colorScheme) // Theme-aware (preferred)
NEVER use:
Colors.white // FORBIDDEN - use colorScheme.onPrimary or onSurface
Colors.black // FORBIDDEN - use colorScheme.onSurface or scrim
Color(0xFFXXXXXX) // FORBIDDEN in components (except in theme files)
SvrntyColors.crimsonRed // FORBIDDEN - use colorScheme.primary instead
Theme Variants
The app provides 2 theme variants:
Light Theme
- Background: White (#FCFCFC)
- Primary: Crimson Red (#C91F37) - high contrast variant
- Secondary: Dark Slate (#2D3843) - high contrast variant
- Text: Very dark gray (#1A1C1E) - 16.5:1 contrast (WCAG AAA)
- Secondary Text: Dark gray (#3E4A56) - 7:1 contrast (WCAG AAA)
Dark Theme (Forest Green)
- Background: Dark Forest Green (#0C1410) - unique branding
- Primary: Bright Crimson (#FF5A6D) - optimized for dark backgrounds
- Secondary: Light Slate Gray (#A5B6C8)
- Text: Pure white (#FFFFFF) - 18.2:1 contrast (WCAG AAA)
- Secondary Text: Light gray-green (#E0E8E4) - 14.1:1 contrast (WCAG AAA)
All text colors are WCAG AAA compliant (minimum 7:1 contrast for normal text, 4.5:1 for large text).
Color Access Examples
Good Examples
// Text on colored background (e.g., status badge, avatar)
Container(
color: Theme.of(context).colorScheme.primary,
child: Text(
'Label',
style: TextStyle(color: Theme.of(context).colorScheme.onPrimary),
),
)
// Primary text on page background
Text(
'Hello',
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
)
// Secondary/muted text
Text(
'Description',
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
)
// Shadow color
BoxShadow(
color: Theme.of(context).colorScheme.scrim.withValues(alpha: 0.2),
)
Bad Examples (DON'T DO THIS)
// WRONG - hardcoded white
Text('Label', style: TextStyle(color: Colors.white))
// WRONG - hardcoded black
BoxShadow(color: Colors.black.withValues(alpha: 0.2))
// WRONG - hardcoded color value
Container(color: Color(0xFFFF9800))
// WRONG - using SvrntyColors directly for UI elements
Container(color: SvrntyColors.crimsonRed) // Use colorScheme.primary instead
Status Colors
For delivery status indicators, use the StatusColorScheme utility:
import '../theme/status_colors.dart';
// Get status color (hardcoded, consistent across themes)
final color = StatusColorScheme.getStatusColor('completed');
// Get status color from theme (preferred - adapts to theme)
final themeColor = StatusColorScheme.getStatusColorFromTheme(
'completed',
Theme.of(context).colorScheme,
);
// Get background and text colors
final bgColor = StatusColorScheme.getStatusBackground('completed');
final textColor = StatusColorScheme.getStatusText('completed');
// Use pre-built widgets
StatusBadgeWidget(status: 'completed')
StatusAccentBar(status: 'in_transit')
Supported Status Values:
pending- Amber (attention needed)in_transit,in_progress,processing- Slate blue (active)completed,delivered,done- Green (success)failed,error- Red (problem)cancelled,skipped,rejected- Gray (inactive)on_hold,paused,waiting- Slate (informational)
Progress Gradient Colors
For progress indicators (e.g., route completion):
import '../theme/color_system.dart';
// Progress colors (0-100%)
SvrntyColors.progressLow // Orange - 0-40%
SvrntyColors.progressMedium // Amber - 40-70%
SvrntyColors.progressHigh // Green - 70-100%
// Example: color interpolation
Color progressColor = Color.lerp(
SvrntyColors.progressLow,
SvrntyColors.progressMedium,
progressPercent,
)!;
Modifying Colors
Changing Brand Colors
Edit the ColorScheme definitions in /lib/theme.dart:
// Light theme
static ColorScheme lightScheme() {
return const ColorScheme(
brightness: Brightness.light,
primary: Color(0xffC91F37), // Change this for new primary color
secondary: Color(0xff2D3843), // Change this for new secondary color
// ... rest of colors
);
}
// Dark theme
static ColorScheme darkScheme() {
return const ColorScheme(
brightness: Brightness.dark,
primary: Color(0xffFF5A6D), // Change this for new primary color
secondary: Color(0xffA5B6C8), // Change this for new secondary color
// ... rest of colors
);
}
Adding New Semantic Colors
Add to /lib/theme/color_system.dart:
class SvrntyColors {
// ... existing colors
/// New semantic color
static const Color myNewColor = Color(0xFFXXXXXX);
}
Then use it in components:
Container(color: SvrntyColors.myNewColor)
Changing Status Colors
Edit /lib/theme/status_colors.dart:
class StatusColorScheme {
static const Color completed = Color(0xFF22C55E); // Change this
static const Color completedBackground = Color(0xFFD1FAE5); // Change this
static const Color completedText = Color(0xFF065F46); // Change this
// ... rest of colors
}
Text Theme
The app uses Montserrat font family for all text with explicit color assignments.
Font Weights:
- 300 (Light) - Unused in current design
- 400 (Regular) - Body text
- 500 (Medium) - Labels, buttons
- 600 (SemiBold) - Headings, titles
- 700 (Bold) - Display text, emphasis
Text Styles:
Theme.of(context).textTheme.displayLarge // 57px, bold, onSurface
Theme.of(context).textTheme.headlineMedium // 28px, semibold, onSurface
Theme.of(context).textTheme.titleLarge // 22px, semibold, onSurface
Theme.of(context).textTheme.bodyMedium // 14px, regular, onSurface
Theme.of(context).textTheme.labelSmall // 11px, medium, onSurfaceVariant
All text styles automatically adapt to light/dark themes with proper contrast.
Accessibility
All color combinations meet WCAG AAA standards (7:1 contrast for normal text, 4.5:1 for large text):
Light Theme:
- Primary text on background: 16.5:1 (WCAG AAA)
- Secondary text on background: 7:1 (WCAG AAA)
- Text on primary color: 6.2:1 (WCAG AA Large)
Dark Theme:
- Primary text on background: 18.2:1 (WCAG AAA)
- Secondary text on background: 14.1:1 (WCAG AAA)
- Text on primary color: 11.8:1 (WCAG AAA)
Component Themes
Component-specific theme configurations are in /lib/theme/component_themes.dart:
- CardTheme - Elevated cards with subtle shadows
- AppBarTheme - Navigation bars
- ButtonTheme - Filled, outlined, elevated buttons
- InputDecorationTheme - Text fields
- SnackBarTheme - Toast messages
- DialogTheme - Modal dialogs
- BottomNavigationBarTheme - Bottom navigation
- ChipTheme - Status chips
- ProgressIndicatorTheme - Loading indicators
- FloatingActionButtonTheme - FAB
- SliderTheme - Range inputs
All component themes use ColorScheme properties for consistency.
Migration Guide
If you need to migrate old hardcoded colors to the new theme system:
Step 1: Find Hardcoded Colors
# Search for hardcoded colors in your components
grep -r "Colors\.white\|Colors\.black\|Color(0x" lib/components/ lib/pages/
Step 2: Replace with Theme Colors
| Old Pattern | New Pattern |
|---|---|
Colors.white on colored bg |
colorScheme.onPrimary |
Colors.white on page |
colorScheme.surface |
Colors.black for text |
colorScheme.onSurface |
Colors.black for shadow |
colorScheme.scrim |
Color(0xFFXXXXXX) |
Use colorScheme or SvrntyColors |
Step 3: Test Both Themes
- Run app in light mode, verify all text is visible
- Run app in dark mode, verify all text is visible
- Check color contrast with browser dev tools
Troubleshooting
Text not visible in dark mode:
- Ensure you're using
colorScheme.onSurfaceoronSurfaceVariantfor text colors - Don't use hardcoded
Colors.blackor dark colors for text - Check that TextTheme is properly applied with
_buildTextTheme()
Colors not updating when switching themes:
- Use
Theme.of(context).colorSchemeinstead ofSvrntyColorsconstants - Ensure widget rebuilds when theme changes (use
ConsumerWidgetfor Riverpod) - Avoid caching ColorScheme outside of build method
Wrong colors in components:
- Verify component uses
colorSchemeparameter correctly - Check that custom theme is applied in MaterialApp
- Use
theme()method to generate ThemeData
Resources
- Material Design 3: https://m3.material.io/
- WCAG Contrast Checker: https://webaim.org/resources/contrastchecker/
- Flutter Theme Guide: https://docs.flutter.dev/cookbook/design/themes
- ColorScheme Docs: https://api.flutter.dev/flutter/material/ColorScheme-class.html