inou/design/FLUTTER_STYLEGUIDE_GAPS.md

560 lines
12 KiB
Markdown

# 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) |