ionic-planb-logistic-app-fl.../lib/theme
Mathias Beaulieu-Duncan edb106a7fd Refactor theme system and remove unused platforms
- 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>
2026-01-26 14:47:51 -05:00
..
animation_system.dart Add delivery order badges and optimize list performance 2025-11-17 10:29:17 -05:00
border_system.dart Implement premium UI/UX refinements for Apple-like polish 2025-11-15 14:41:32 -05:00
color_system.dart Refactor theme system and remove unused platforms 2026-01-26 14:47:51 -05:00
component_themes.dart Fix linting issues and code quality improvements 2025-11-16 01:39:35 -05:00
gradient_system.dart Fix linting issues and code quality improvements 2025-11-16 01:39:35 -05:00
README.md Refactor theme system and remove unused platforms 2026-01-26 14:47:51 -05:00
shadow_system.dart Implement premium UI/UX refinements for Apple-like polish 2025-11-15 14:41:32 -05:00
size_system.dart Fix linting issues and code quality improvements 2025-11-16 01:39:35 -05:00
spacing_system.dart Implement premium UI/UX refinements for Apple-like polish 2025-11-15 14:41:32 -05:00
status_colors.dart Refactor theme system and remove unused platforms 2026-01-26 14:47:51 -05:00
typography_system.dart Optimize contrast and readability with enhanced UI sizing 2025-11-17 10:04:00 -05:00

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.onSurface or onSurfaceVariant for text colors
  • Don't use hardcoded Colors.black or dark colors for text
  • Check that TextTheme is properly applied with _buildTextTheme()

Colors not updating when switching themes:

  • Use Theme.of(context).colorScheme instead of SvrntyColors constants
  • Ensure widget rebuilds when theme changes (use ConsumerWidget for Riverpod)
  • Avoid caching ColorScheme outside of build method

Wrong colors in components:

  • Verify component uses colorScheme parameter correctly
  • Check that custom theme is applied in MaterialApp
  • Use theme() method to generate ThemeData

Resources