inou/.claude/worktrees/vibrant-nash/design/FLUTTER_STYLEGUIDE_GAPS.md

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:

  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

// 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.labelSmall which 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 use maxWidthNarrow (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 h3 style (~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
  ),
),

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:

  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

  1. Child row padding (32px → 48px)
  2. Dossier page max-width (1200px → 800px)
  3. Input focus style (2px → 1px + shadow)
  4. Message border colors - semantic colors

Low Priority (Polish)

  1. Dashed borders (requires package or custom paint)
  2. Hover states (platform-specific)
  3. Transitions (nice to have)

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

  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)