12 KiB
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:
- Typography inconsistencies - font weights and sizes don't always match the web
- Missing CSS-specific styling - dashed borders, hover states, transitions
- Component styling gaps - buttons, inputs, and badges not pixel-perfect
- 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
// 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.
// 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.
// 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.
// 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:
// Current (in various places):
borderRadius: BorderRadius.circular(4),
// Should use:
borderRadius: InouTheme.borderRadiusSm,
5. Component Gaps
5.1 Buttons
Web 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:
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:
// Both sizes should use 15px (1rem) font
textStyle: InouTheme.labelLarge.copyWith(
fontSize: 15, // Always 15px, not 14 for small
),
5.2 Badges
Web 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:
// 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:
.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.labelSmallwhich has correct specs
Fix:
// 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:
.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:
// 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:
.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:
// 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:
.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:
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:
.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 usemaxWidthNarrow(800px) ❌
6.2 Nav Bar
Web 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
h3style (~18px) instead of 1.75rem (26.25px) ❌ - Missing letter-spacing: -0.02em ❌
Fix:
// 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:
.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:
- Use
dotted_borderpackage - Custom
CustomPainterfor dashed lines - 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:
- Use
InkWellwith splash colors - On web, implement
MouseRegionfor hover states - 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)
- Badge font size and padding - very noticeable
- Logo size in header - brand consistency
- Button small font size - should match regular
- Data card title letter-spacing - subtle but important
Medium Priority
- Child row padding (32px → 48px)
- Dossier page max-width (1200px → 800px)
- Input focus style (2px → 1px + shadow)
- Message border colors - semantic colors
Low Priority (Polish)
- Dashed borders (requires package or custom paint)
- Hover states (platform-specific)
- Transitions (nice to have)
9. Recommended Actions
Immediate (Theme File Updates)
// 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
- InouBadge - Fix padding (2v, 8h) and font size (15px)
- InouButton - Use 15px for small buttons too
- InouHeader - Use new logo style
- InouCard - Ensure labelSmall is used correctly
Page Updates
- DossierPage - Change max-width to 800px
- 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) |