--- name: krow-mobile-design-system description: KROW mobile design system usage rules covering colors, typography, icons, spacing, and UI component patterns. Use this when implementing UI in KROW mobile features, matching POC designs to production, creating themed widgets, enforcing visual consistency, or reviewing UI code compliance. Prevents hardcoded values and ensures brand consistency across staff and client apps. Critical for maintaining immutable design tokens. --- # KROW Mobile Design System Usage This skill defines mandatory standards for UI implementation using the shared `apps/mobile/packages/design_system`. All UI must consume design system tokens exclusively. ## When to Use This Skill - Implementing any UI in mobile features - Migrating POC/prototype designs to production - Creating new themed widgets or components - Reviewing UI code for design system compliance - Matching colors and typography from designs - Adding icons, spacing, or layout elements - Setting up theme configuration in apps - Refactoring UI code with hardcoded values ## Core Principle **Design tokens (colors, typography, spacing) are IMMUTABLE and defined centrally.** Features consume tokens but NEVER modify them. The design system maintains visual coherence across all apps. ## 1. Design System Ownership ### Centralized Authority - `apps/mobile/packages/design_system` owns: - All brand assets - Colors and semantic color mappings - Typography and font configurations - Core UI components - Icons and images - Spacing, radius, elevation constants ### No Local Overrides **✅ CORRECT:** ```dart // Feature uses design system import 'package:design_system/design_system.dart'; Container( color: UiColors.background, padding: EdgeInsets.all(UiConstants.spacingL), child: Text( 'Hello', style: UiTypography.display1m, ), ) ``` **❌ FORBIDDEN:** ```dart // ❌ Custom colors in feature const myBlue = Color(0xFF1A2234); // ❌ Custom text styles in feature const myStyle = TextStyle(fontSize: 24, fontWeight: FontWeight.bold); // ❌ Theme overrides in feature Theme( data: ThemeData(primaryColor: Colors.blue), child: MyWidget(), ) ``` ### Extension Policy If a required style is missing: 1. **FIRST:** Add it to `design_system` following existing patterns 2. **THEN:** Use it in your feature **DO NOT** create temporary workarounds with hardcoded values. ## 2. Package Structure ``` apps/mobile/packages/design_system/ ├── lib/ │ ├── src/ │ │ ├── ui_colors.dart # Color tokens │ │ ├── ui_typography.dart # Text styles │ │ ├── ui_icons.dart # Icon exports │ │ ├── ui_constants.dart # Spacing, radius, elevation │ │ ├── ui_theme.dart # ThemeData factory │ │ └── widgets/ # Shared UI components │ │ ├── custom_button.dart │ │ └── custom_app_bar.dart │ └── design_system.dart # Public exports ├── assets/ │ ├── icons/ │ ├── images/ │ └── fonts/ └── pubspec.yaml ``` ## 3. Colors Usage Rules ### Strict Protocol **✅ DO:** ```dart // Use UiColors for all color needs Container(color: UiColors.background) Text('Hello', style: TextStyle(color: UiColors.foreground)) Icon(Icons.home, color: UiColors.primary) ``` **❌ DON'T:** ```dart // ❌ Hardcoded hex colors Container(color: Color(0xFF1A2234)) // ❌ Material color constants Container(color: Colors.blue) // ❌ Opacity on hardcoded colors Container(color: Color(0xFF1A2234).withOpacity(0.5)) ``` ### Available Color Categories **Brand Colors:** - `UiColors.primary` - Main brand color - `UiColors.secondary` - Secondary brand color - `UiColors.accent` - Accent highlights **Semantic Colors:** - `UiColors.background` - Page background - `UiColors.foreground` - Primary text color - `UiColors.card` - Card/container background - `UiColors.border` - Border colors - `UiColors.mutedForeground` - Secondary text **Status Colors:** - `UiColors.success` - Success states - `UiColors.warning` - Warning states - `UiColors.error` - Error states - `UiColors.info` - Information states ### Color Matching from POCs When migrating POC designs: 1. **Find closest match** in `UiColors` 2. **Use existing color** even if slightly different 3. **DO NOT add new colors** without design team approval **Example Process:** ```dart // POC has: Color(0xFF2C3E50) // Find closest: UiColors.background or UiColors.card // Use: UiColors.card // POC has: Color(0xFF27AE60) // Find closest: UiColors.success // Use: UiColors.success ``` ### Theme Access Colors can also be accessed via theme: ```dart // Both are valid: Container(color: UiColors.primary) Container(color: Theme.of(context).colorScheme.primary) ``` ## 4. Typography Usage Rules ### Strict Protocol **✅ DO:** ```dart // Use UiTypography for all text Text('Title', style: UiTypography.display1m) Text('Body', style: UiTypography.body1r) Text('Label', style: UiTypography.caption1m) ``` **❌ DON'T:** ```dart // ❌ Custom TextStyle Text('Title', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, )) // ❌ Manual font configuration Text('Body', style: TextStyle( fontFamily: 'Inter', fontSize: 16, )) // ❌ Modifying existing styles inline Text('Title', style: UiTypography.display1m.copyWith( fontSize: 28, // ← Don't override size )) ``` ### Available Typography Styles **Display Styles (Large Headers):** - `UiTypography.display1m` - Display Medium - `UiTypography.display1sb` - Display Semi-Bold - `UiTypography.display1b` - Display Bold **Heading Styles:** - `UiTypography.heading1m` - H1 Medium - `UiTypography.heading1sb` - H1 Semi-Bold - `UiTypography.heading1b` - H1 Bold - `UiTypography.heading2m` - H2 Medium - `UiTypography.heading2sb` - H2 Semi-Bold **Body Styles:** - `UiTypography.body1r` - Body Regular - `UiTypography.body1m` - Body Medium - `UiTypography.body1sb` - Body Semi-Bold - `UiTypography.body2r` - Body 2 Regular **Caption/Label Styles:** - `UiTypography.caption1m` - Caption Medium - `UiTypography.caption1sb` - Caption Semi-Bold - `UiTypography.label1m` - Label Medium ### Allowed Customizations **✅ ALLOWED (Color Only):** ```dart // You MAY change color Text( 'Title', style: UiTypography.display1m.copyWith( color: UiColors.error, // ← OK ), ) ``` **❌ FORBIDDEN (Size, Weight, Family):** ```dart // ❌ Don't change size Text( 'Title', style: UiTypography.display1m.copyWith(fontSize: 28), ) // ❌ Don't change weight Text( 'Title', style: UiTypography.display1m.copyWith(fontWeight: FontWeight.w900), ) // ❌ Don't change family Text( 'Title', style: UiTypography.display1m.copyWith(fontFamily: 'Roboto'), ) ``` ### Typography Matching from POCs When migrating: 1. Identify text role (heading, body, caption) 2. Find closest matching style in `UiTypography` 3. Use existing style even if size/weight differs slightly ## 5. Icons Usage Rules ### Strict Protocol **✅ DO:** ```dart // Use UiIcons Icon(UiIcons.home) Icon(UiIcons.profile) Icon(UiIcons.chevronLeft) ``` **❌ DON'T:** ```dart // ❌ Direct icon library imports import 'package:lucide_icons/lucide_icons.dart'; Icon(LucideIcons.home) // ❌ Font Awesome direct import 'package:font_awesome_flutter/font_awesome_flutter.dart'; FaIcon(FontAwesomeIcons.house) ``` ### Why Centralize Icons? 1. **Consistency:** Same icon for same action everywhere 2. **Branding:** Unified icon set with consistent stroke weight 3. **Swappability:** Change icon library in one place ### Icon Libraries Design system uses: - `typedef _IconLib = LucideIcons;` (primary) - `typedef _IconLib2 = FontAwesomeIcons;` (secondary) **Features MUST NOT import these directly.** ### Adding New Icons If icon missing: 1. Add to `ui_icons.dart`: ```dart class UiIcons { static const home = _IconLib.home; static const newIcon = _IconLib.newIcon; // Add here } ``` 2. Use in feature: ```dart Icon(UiIcons.newIcon) ``` ## 6. Spacing & Layout Constants ### Strict Protocol **✅ DO:** ```dart // Use UiConstants for spacing Padding(padding: EdgeInsets.all(UiConstants.spacingL)) SizedBox(height: UiConstants.spacingM) Container( padding: EdgeInsets.symmetric( horizontal: UiConstants.spacingL, vertical: UiConstants.spacingM, ), ) // Use UiConstants for radius Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(UiConstants.radiusM), ), ) // Use UiConstants for elevation elevation: UiConstants.elevationLow ``` **❌ DON'T:** ```dart // ❌ Magic numbers Padding(padding: EdgeInsets.all(16.0)) SizedBox(height: 24.0) BorderRadius.circular(8.0) elevation: 2.0 ``` ### Available Constants **Spacing:** ```dart UiConstants.spacingXs // Extra small UiConstants.spacingS // Small UiConstants.spacingM // Medium UiConstants.spacingL // Large UiConstants.spacingXl // Extra large UiConstants.spacing2xl // 2x Extra large ``` **Border Radius:** ```dart UiConstants.radiusS // Small UiConstants.radiusM // Medium UiConstants.radiusL // Large UiConstants.radiusXl // Extra large UiConstants.radiusFull // Fully rounded ``` **Elevation:** ```dart UiConstants.elevationNone UiConstants.elevationLow UiConstants.elevationMedium UiConstants.elevationHigh ``` ## 7. Smart Widgets Usage ### When to Use - **Prefer standard Flutter Material widgets** styled via theme - **Use design system widgets** for non-standard patterns - **Create new widgets** in design system if reused >3 features ### Navigation in Widgets Widgets with navigation MUST use safe methods: **✅ CORRECT:** ```dart // In UiAppBar back button: import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_core/krow_core.dart'; IconButton( icon: Icon(UiIcons.chevronLeft), onPressed: () => Modular.to.popSafe(), // ← Safe pop ) ``` **❌ FORBIDDEN:** ```dart // ❌ Direct Navigator IconButton( icon: Icon(UiIcons.chevronLeft), onPressed: () => Navigator.pop(context), ) // ❌ Unsafe Modular IconButton( icon: Icon(UiIcons.chevronLeft), onPressed: () => Modular.to.pop(), // Can crash ) ``` ### Composition Over Inheritance **✅ CORRECT:** ```dart // Compose standard widgets Container( padding: EdgeInsets.all(UiConstants.spacingL), decoration: BoxDecoration( color: UiColors.card, borderRadius: BorderRadius.circular(UiConstants.radiusM), ), child: Column( children: [ Text('Title', style: UiTypography.heading1sb), SizedBox(height: UiConstants.spacingM), Text('Body', style: UiTypography.body1r), ], ), ) ``` **❌ AVOID:** ```dart // ❌ Deep custom widget hierarchies class CustomCard extends StatelessWidget { // Complex custom implementation } ``` ## 8. Theme Configuration ### App Setup Apps initialize theme ONCE in root MaterialApp: **✅ CORRECT:** ```dart // apps/mobile/apps/staff/lib/app_widget.dart import 'package:design_system/design_system.dart'; class StaffApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp.router( theme: StaffTheme.light, // ← Design system theme darkTheme: StaffTheme.dark, // ← Optional dark mode themeMode: ThemeMode.system, // ... ); } } ``` **❌ FORBIDDEN:** ```dart // ❌ Custom theme in app MaterialApp.router( theme: ThemeData( primaryColor: Colors.blue, // ← NO! ), ) // ❌ Theme override in feature Theme( data: ThemeData(...), child: MyFeatureWidget(), ) ``` ### Accessing Theme **Both methods valid:** ```dart // Method 1: Direct design system import import 'package:design_system/design_system.dart'; Text('Hello', style: UiTypography.body1r) // Method 2: Via theme context Text('Hello', style: Theme.of(context).textTheme.bodyMedium) ``` **Prefer Method 1** for explicit type safety. ## 9. POC → Production Workflow ### Step 1: Implement Structure (POC Matching) Implement UI layout exactly matching POC: ```dart // Temporary: Match POC visually Container( color: Color(0xFF1A2234), // ← POC color padding: EdgeInsets.all(16.0), // ← POC spacing child: Text( 'Title', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), // ← POC style ), ) ``` **Purpose:** Ensure visual parity with POC before refactoring. ### Step 2: Architecture Refactor Move to Clean Architecture: - Extract business logic to use cases - Move state management to BLoCs - Implement repository pattern - Use dependency injection ### Step 3: Design System Integration Replace hardcoded values: ```dart // Production: Design system tokens Container( color: UiColors.background, // ← Found closest match padding: EdgeInsets.all(UiConstants.spacingL), // ← Used constant child: Text( 'Title', style: UiTypography.heading1sb, // ← Matched typography ), ) ``` **Color Matching:** - POC `#1A2234` → `UiColors.background` - POC `#3498DB` → `UiColors.primary` - POC `#27AE60` → `UiColors.success` **Typography Matching:** - POC `24px bold` → `UiTypography.heading1sb` - POC `16px regular` → `UiTypography.body1r` - POC `14px medium` → `UiTypography.caption1m` **Spacing Matching:** - POC `16px` → `UiConstants.spacingL` - POC `8px` → `UiConstants.spacingM` - POC `4px` → `UiConstants.spacingS` ## 10. Anti-Patterns & Common Mistakes ### ❌ Magic Numbers ```dart // BAD EdgeInsets.all(12.0) SizedBox(height: 24.0) BorderRadius.circular(8.0) // GOOD EdgeInsets.all(UiConstants.spacingM) SizedBox(height: UiConstants.spacingL) BorderRadius.circular(UiConstants.radiusM) ``` ### ❌ Local Themes ```dart // BAD Theme( data: ThemeData(primaryColor: Colors.blue), child: MyWidget(), ) // GOOD // Use global theme defined in app ``` ### ❌ Hex Hunting ```dart // BAD: Copy-paste from Figma Container(color: Color(0xFF3498DB)) // GOOD: Find matching design system color Container(color: UiColors.primary) ``` ### ❌ Direct Icon Library ```dart // BAD import 'package:lucide_icons/lucide_icons.dart'; Icon(LucideIcons.home) // GOOD Icon(UiIcons.home) ``` ### ❌ Custom Text Styles ```dart // BAD Text('Title', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, fontFamily: 'Inter', )) // GOOD Text('Title', style: UiTypography.heading1sb) ``` ## 11. Design System Review Checklist Before merging UI code: ### ✅ Design System Compliance - [ ] No hardcoded `Color(...)` or `0xFF...` hex values - [ ] No custom `TextStyle(...)` definitions - [ ] All spacing uses `UiConstants.spacing*` - [ ] All radius uses `UiConstants.radius*` - [ ] All elevation uses `UiConstants.elevation*` - [ ] All icons from `UiIcons`, not direct library imports - [ ] Theme consumed from design system, no local overrides - [ ] Layout matches POC intent using design system primitives ### ✅ Architecture Compliance - [ ] No business logic in widgets - [ ] State managed by BLoCs - [ ] Navigation uses Modular safe extensions - [ ] Localization used for all text (no hardcoded strings) - [ ] No direct Data Connect queries in widgets ### ✅ Code Quality - [ ] Widget build methods concise (<50 lines) - [ ] Complex widgets extracted to separate files - [ ] Meaningful widget names - [ ] Doc comments on reusable widgets ## 12. When to Extend Design System ### Add New Color **When:** New brand color approved by design team **Process:** 1. Add to `ui_colors.dart`: ```dart class UiColors { static const myNewColor = Color(0xFF123456); } ``` 2. Update theme if needed 3. Use in features ### Add New Typography Style **When:** New text style pattern emerges across multiple features **Process:** 1. Add to `ui_typography.dart`: ```dart class UiTypography { static const myNewStyle = TextStyle( fontSize: 20, fontWeight: FontWeight.w600, fontFamily: _fontFamily, ); } ``` 2. Use in features ### Add Shared Widget **When:** Widget reused in 3+ features **Process:** 1. Create in `lib/src/widgets/`: ```dart // my_widget.dart class MyWidget extends StatelessWidget { // Implementation using design system tokens } ``` 2. Export from `design_system.dart` 3. Use across features ## Summary **Core Rules:** 1. **All colors from `UiColors`** - Zero hex codes in features 2. **All typography from `UiTypography`** - Zero custom TextStyle 3. **All spacing/radius/elevation from `UiConstants`** - Zero magic numbers 4. **All icons from `UiIcons`** - Zero direct library imports 5. **Theme defined once** in app entry point 6. **POC → Production** requires design system integration step **The Golden Rule:** Design system is immutable. Features adapt to the system, not the other way around. When implementing UI: 1. Import `package:design_system/design_system.dart` 2. Use design system tokens exclusively 3. Match POC intent with available tokens 4. Request new tokens only when truly necessary 5. Never create temporary hardcoded workarounds Visual consistency is non-negotiable. Every pixel must come from the design system.