# inou Flutter Styleguide Gap Analysis **Generated:** 2025-01-28 **Purpose:** Document gaps between the original web styleguide and the current Flutter implementation to achieve closer visual parity. --- ## Executive Summary The Flutter app has a good foundation with `InouTheme` properly implementing the design tokens. However, there are several gaps in how those tokens are applied consistently across the app. The main issues are: 1. **Typography inconsistencies** - font weights and sizes don't always match the web 2. **Missing CSS-specific styling** - dashed borders, hover states, transitions 3. **Component styling gaps** - buttons, inputs, and badges not pixel-perfect 4. **Layout structure** - page layout and spacing inconsistencies --- ## 1. Typography Gaps ### Current vs Original | Style | Web CSS | Flutter Implementation | Gap | |-------|---------|------------------------|-----| | Page Title (h1Large) | 2.5rem (37.5px), 700 | 37.5px, w700 | ✅ Matches | | h1 | 2.25rem (33.75px), 300, -0.03em | 33.75px, w300, -0.03em | ✅ Matches | | h2 Section Title | 1.4rem (21px), 600 | 21px, w600 | ✅ Matches | | h3 Subsection | 1.1rem (16.5px), 600 | 16.5px, w600 | ✅ Matches | | Intro text | 1.15rem (17.25px), 300, line-height 1.8 | 17.25px, w300, height 1.8 | ✅ Matches | | Body light | 1rem (15px), 300, line-height 1.8 | Missing `bodyLight` in some places | ⚠️ Inconsistent use | | Body regular | 1rem (15px), 400 | 15px, w400 | ✅ Matches | | Label/Category | 0.75rem (11.25px), 600, uppercase, 0.1em | 11.25px, w600, 0.1em spacing | ⚠️ `toUpperCase()` not always called | | Mono | SF Mono, 0.85rem (12.75px) | SF Mono, 12.75px | ⚠️ Fallback fonts missing | ### Fixes Needed ```dart // In InouTheme, add missing style variants: static TextStyle get bodyLight => GoogleFonts.sora( fontSize: 15.0, fontWeight: FontWeight.w300, // Light, for long-form content color: textMuted, height: 1.8, ); // Mono font needs fallbacks static TextStyle get mono => TextStyle( fontFamily: 'SF Mono', fontFamilyFallback: const ['Monaco', 'Consolas', 'monospace'], fontSize: 12.75, fontWeight: FontWeight.w400, color: text, ); ``` --- ## 2. Color Gaps ### Theme Colors ✅ All colors match the design tokens exactly: - `bg: #F8F7F6` ✅ - `bgCard: #FFFFFF` ✅ - `border: #E5E2DE` ✅ - `text: #1C1917` ✅ - `textMuted: #78716C` ✅ - `textSubtle: #A8A29E` ✅ - `accent: #B45309` ✅ - All indicator colors ✅ ### Missing Usage Patterns **Issue:** The web uses `opacity: 0.6` for "coming soon" and disabled states. Flutter uses hardcoded opacity. ```dart // Web CSS: .data-card.coming-soon { opacity: 0.6; } .btn-disabled { opacity: 0.6; } // Flutter should use consistent disabled opacity: static const double disabledOpacity = 0.6; ``` --- ## 3. Spacing Gaps ### Spacing Scale ✅ All spacing tokens match: - xs: 4px ✅ - sm: 8px ✅ - md: 12px ✅ - lg: 16px ✅ - xl: 24px ✅ - xxl: 32px ✅ - xxxl: 48px ✅ ### Usage Inconsistencies **Issue:** The dossier page uses hardcoded padding values instead of theme constants. ```dart // Current (in dossier_page.dart): padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 48), // Should be: padding: const EdgeInsets.symmetric( horizontal: InouTheme.spaceXl, vertical: InouTheme.spaceXxxl, ), ``` **Issue:** Card margins not consistent with web. ```dart // Web CSS: .data-card { margin-bottom: 16px; } // Flutter InouCard uses spaceLg (16px) ✅ but some pages override ``` --- ## 4. Border Radius Gaps ### Radius Values ✅ All match the design tokens. ### Missing Application **Issue:** Some components don't use the theme radius: ```dart // Current (in various places): borderRadius: BorderRadius.circular(4), // Should use: borderRadius: InouTheme.borderRadiusSm, ``` --- ## 5. Component Gaps ### 5.1 Buttons **Web CSS:** ```css .btn { padding: 10px 18px; font-size: 1rem; font-weight: 500; border-radius: 6px; transition: all 0.15s; } .btn-small { padding: 6px 12px; font-size: 1rem; /* Same font size as regular */ } ``` **Flutter Current:** ```dart final padding = isSmall ? const EdgeInsets.symmetric(horizontal: 12, vertical: 6) : const EdgeInsets.symmetric(horizontal: 18, vertical: 10); // Font size differs: small uses 14px, regular uses 15px ``` **Fix:** ```dart // Both sizes should use 15px (1rem) font textStyle: InouTheme.labelLarge.copyWith( fontSize: 15, // Always 15px, not 14 for small ), ``` ### 5.2 Badges **Web CSS:** ```css .badge { padding: 2px 8px; font-size: 1rem; /* 15px */ font-weight: 500; border-radius: 4px; background: var(--accent-light); color: var(--accent); } .badge-care { background: var(--success-light); color: var(--success); } ``` **Flutter Current:** (inou_badge.dart) - Padding: 4h, 8v (should be 2v, 8h) ❌ - Font size: uses bodySmall (12.75px) instead of 15px ❌ **Fix:** ```dart // InouBadge needs updating: static TextStyle get badgeText => GoogleFonts.sora( fontSize: 15.0, fontWeight: FontWeight.w500, color: text, ); // Padding should be: padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), ``` ### 5.3 Data Cards **Web CSS:** ```css .data-card-header { display: flex; align-items: center; padding: 16px; gap: 12px; } .data-card-indicator { width: 4px; height: 32px; border-radius: 2px; } .section-heading { font-size: 0.75rem; /* 11.25px */ font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; } ``` **Flutter Current:** The `_DataCard` in dossier_page.dart mostly matches, but: - Letter spacing is 0.8 instead of 0.08em (0.9px) ❌ - Not using `InouTheme.labelSmall` which has correct specs **Fix:** ```dart // In _DataCard header: Text( title, // Don't call toUpperCase() here style: InouTheme.labelSmall, // Already has correct specs + uppercase handling ), ``` ### 5.4 Data Rows **Web CSS:** ```css .data-row { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px dashed var(--border); /* DASHED! */ } .data-row:last-child { border-bottom: none; } .data-row.child { padding-left: 48px; } ``` **Flutter Current:** - Uses solid borders instead of dashed ❌ - Child row padding is 32px instead of 48px ❌ **Fix:** ```dart // Flutter can't do dashed borders easily, but can simulate with DashedLine widget // or use a dotted pattern via CustomPaint // For child padding: padding: const EdgeInsets.only(left: 48, right: 16, top: 12, bottom: 12), ``` ### 5.5 Form Inputs **Web CSS:** ```css .form-group input { width: 100%; padding: 10px 12px; font-size: 1rem; border: 1px solid var(--border); border-radius: 6px; transition: border-color 0.15s, box-shadow 0.15s; } .form-group input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); } ``` **Flutter Current:** The theme's `InputDecorationTheme` uses 2px border on focus, not 1px + shadow. **Fix:** ```dart // In InputDecorationTheme: focusedBorder: OutlineInputBorder( borderRadius: borderRadiusMd, borderSide: BorderSide(color: accent, width: 1), // 1px, not 2px ), // Add focus shadow via Container wrapper or InputDecoration ``` ### 5.6 Messages (Error/Info/Success) **Web CSS:** ```css .error { background: var(--danger-light); border: 1px solid #FECACA; color: var(--danger); padding: 10px 14px; border-radius: 6px; } .info { background: var(--accent-light); border: 1px solid #FDE68A; color: var(--accent); } .success { background: var(--success-light); border: 1px solid #A7F3D0; color: var(--success); } ``` **Flutter Current:** `InouMessage` widget exists but check border colors. **Add specific border colors:** ```dart static const Color errorBorder = Color(0xFFFECACA); static const Color infoBorder = Color(0xFFFDE68A); static const Color successBorder = Color(0xFFA7F3D0); ``` --- ## 6. Layout Gaps ### 6.1 Page Container **Web CSS:** ```css .container { max-width: 800px; /* maxWidthNarrow */ margin: 0 auto; padding: 40px 20px; } .container-narrow { max-width: 360px; /* maxWidthForm */ padding: 60px 20px 40px; } ``` **Flutter:** Uses `InouPage` and `InouAuthFlowPage` which are close but: - Auth pages should use `maxWidthForm` (360px) ✅ - Dossier uses `maxWidth` (1200px) but should probably use `maxWidthNarrow` (800px) ❌ ### 6.2 Nav Bar **Web CSS:** ```css .nav { padding: 12px 24px; max-width: 1200px; border-bottom: 1px solid var(--border); } .logo { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; text-transform: lowercase; } ``` **Flutter Current:** - Logo font size uses `h3` style (~18px) instead of 1.75rem (26.25px) ❌ - Missing letter-spacing: -0.02em ❌ **Fix:** ```dart // In InouHeader _buildLogo(): Text( 'inou', style: GoogleFonts.sora( fontSize: 26.25, // 1.75rem fontWeight: FontWeight.w700, color: InouTheme.accent, letterSpacing: -0.02 * 26.25, // -0.02em ), ), ``` ### 6.3 Footer **Web CSS:** ```css .footer { margin-top: 40px; padding-top: 12px; border-top: 1px solid var(--border); } ``` **Flutter:** Check `InouFooter` for consistency. --- ## 7. Missing Features ### 7.1 Dashed Borders The web uses `border-bottom: 1px dashed var(--border)` for data rows. Flutter doesn't support dashed borders natively. **Solutions:** 1. Use `dotted_border` package 2. Custom `CustomPainter` for dashed lines 3. Accept solid borders (simpler but less accurate) ### 7.2 Hover States Web has hover effects everywhere. Flutter web supports hover but mobile doesn't. **Solutions:** 1. Use `InkWell` with splash colors 2. On web, implement `MouseRegion` for hover states 3. Accept that mobile won't have hover ### 7.3 Transitions Web CSS has `transition: all 0.15s` on interactive elements. **Solution:** Add `AnimatedContainer` or `AnimatedDefaultTextStyle` where appropriate. --- ## 8. Priority Fixes ### High Priority (Visual Impact) 1. **Badge font size and padding** - very noticeable 2. **Logo size in header** - brand consistency 3. **Button small font size** - should match regular 4. **Data card title letter-spacing** - subtle but important ### Medium Priority 5. **Child row padding** (32px → 48px) 6. **Dossier page max-width** (1200px → 800px) 7. **Input focus style** (2px → 1px + shadow) 8. **Message border colors** - semantic colors ### Low Priority (Polish) 9. Dashed borders (requires package or custom paint) 10. Hover states (platform-specific) 11. Transitions (nice to have) --- ## 9. Recommended Actions ### Immediate (Theme File Updates) ```dart // Add to InouTheme: // 1. Badge text style static TextStyle get badgeText => GoogleFonts.sora( fontSize: 15.0, fontWeight: FontWeight.w500, ); // 2. Logo style static TextStyle get logo => GoogleFonts.sora( fontSize: 26.25, fontWeight: FontWeight.w700, letterSpacing: -0.02 * 26.25, ); // 3. Message border colors static const Color errorBorder = Color(0xFFFECACA); static const Color infoBorder = Color(0xFFFDE68A); static const Color successBorder = Color(0xFFA7F3D0); // 4. Consistent disabled opacity static const double disabledOpacity = 0.6; ``` ### Widget Updates 1. **InouBadge** - Fix padding (2v, 8h) and font size (15px) 2. **InouButton** - Use 15px for small buttons too 3. **InouHeader** - Use new logo style 4. **InouCard** - Ensure labelSmall is used correctly ### Page Updates 1. **DossierPage** - Change max-width to 800px 2. **All pages** - Use InouTheme spacing constants instead of hardcoded values --- ## 10. Testing Checklist After applying fixes, verify: - [ ] Landing page hero text matches web - [ ] Login form looks identical to web - [ ] Dashboard profile cards match - [ ] Dossier data cards and rows match - [ ] Badges render at correct size - [ ] Buttons (both sizes) match - [ ] Header logo size and spacing correct - [ ] Footer styling matches - [ ] Messages (error/info/success) match - [ ] Form inputs match (esp. focus state) --- ## Appendix: Quick Reference ### Font Sizes (Base 15px) | Name | rem | px | |------|-----|-----| | h1Large | 2.5 | 37.5 | | h1 | 2.25 | 33.75 | | h2 | 1.4 | 21 | | h3 | 1.1 | 16.5 | | intro | 1.15 | 17.25 | | body | 1.0 | 15 | | small | 0.85 | 12.75 | | label | 0.75 | 11.25 | | logo | 1.75 | 26.25 | ### Key Spacing | Use Case | Value | |----------|-------| | Card margin-bottom | 16px | | Card padding | 16px | | Header padding | 12px 24px | | Container padding | 40px 20px | | Button padding | 10px 18px | | Button small padding | 6px 12px | | Badge padding | 2px 8px | ### Border Radius | Use Case | Value | |----------|-------| | Buttons | 6px (radiusMd) | | Cards | 8px (radiusLg) | | Badges | 4px (radiusSm) | | Inputs | 6px (radiusMd) |