spec: mobile stat cards responsive design - two-column layout fix

- Design two-column flexbox layout (label left, number right)
- Responsive font sizing (sm/md/lg breakpoints)
- Label truncation for long text
- Applies to Inventory, Logs, and Admin pages
- Ready for implementation review
This commit is contained in:
Daniel Bedeleanu
2026-04-15 11:13:28 +03:00
parent a6af90ec48
commit bacc23cc23

View File

@@ -0,0 +1,262 @@
# Mobile Stat Cards Responsive Design
> **Goal:** Fix stat card content overflow on mobile iPhone screens by implementing a two-column flexbox layout (label left, number right) that adapts responsively across breakpoints.
> **Architecture:** Redesign stat card components to use `flex justify-between` with responsive font sizing and label truncation. Apply fix to Inventory, Logs, and Admin pages.
> **Tech Stack:** Tailwind CSS, React, Next.js 15, responsive breakpoints
---
## 1. Problem Statement
**Current Issue:** Stat cards display labels and numbers that overflow outside card boundaries on mobile screens (< 640px).
**Affected Components:**
- Inventory Page: "Categories 2", "Item Types 6", "Total Boxes 2"
- Logs Page: "Total Events [number]"
- Admin Page: Stat displays with label + number pattern
**Root Cause:** Cards use inline or block layout without proper space distribution, causing numbers to overflow when screen width is constrained.
---
## 2. Solution Overview
**Approach: Two-Column Flexbox Layout**
Redesign stat cards using CSS Flexbox with:
- **Label (Left):** Flexible width, can grow to fill space, truncates if too long
- **Number (Right):** Fixed width, never wraps, always visible
- **Responsive Sizing:** Font sizes reduce on mobile (`text-sm`) and increase on desktop (`text-base`/`text-xl`)
- **Gap:** 8px (`gap-2`) breathing room between label and number
**Layout Formula:**
```
[Label (flexible)] [gap] [Number (fixed)]
```
---
## 3. Component Specification
### 3.1 Stat Card Component
**Component Name:** `StatCard` (or enhance existing stat display)
**Props:**
```typescript
interface StatCard {
label: string; // e.g., "Categories", "Total Events"
value: number; // e.g., 2, 42
icon?: React.ReactNode; // Optional icon (Lucide)
}
```
**Markup Structure:**
```tsx
<div className="flex justify-between items-center gap-2 p-4 bg-slate-900 rounded-lg">
{/* Icon + Label Container */}
<div className="flex items-center gap-2 min-w-0">
{icon && <Icon className="w-5 h-5 text-primary" />}
<span className="text-sm md:text-base text-slate-400 truncate">
{label}
</span>
</div>
{/* Number (Right) */}
<span className="text-lg md:text-xl font-black text-white whitespace-nowrap">
{value}
</span>
</div>
```
**CSS Classes Breakdown:**
| Class | Purpose |
|-------|---------|
| `flex justify-between` | Label left, number right |
| `items-center` | Vertically center all items |
| `gap-2` | 8px spacing between label and number |
| `p-4` | Padding inside card (8px on mobile via Tailwind default) |
| `bg-slate-900` | Card background (dark theme) |
| `rounded-lg` | Rounded corners |
| `text-sm md:text-base` | Font size: 14px mobile, 16px desktop+ |
| `text-slate-400` | Label color (muted) |
| `truncate` | Label ellipsis if overflow (single line only) |
| `text-lg md:text-xl` | Number size: 18px mobile, 20px desktop+ |
| `font-black` | Number weight (bold) |
| `text-white` | Number color (high contrast) |
| `whitespace-nowrap` | Number never wraps to new line |
| `min-w-0` | Allow label container to shrink below content size (enables truncate) |
---
## 4. Responsive Breakpoints
**Mobile (< 640px):**
- Label: `text-sm` (14px)
- Number: `text-lg` (18px)
- Padding: `p-4` (16px all sides)
- Gap: `gap-2` (8px)
**Tablet (640px - 1024px):**
- Label: `text-base` (16px)
- Number: `text-xl` (20px)
- Padding: `p-5` (20px all sides)
**Desktop (> 1024px):**
- Label: `text-base` (16px)
- Number: `text-xl` (20px)
- Padding: `p-6` (24px all sides)
**Tailwind Breakpoint Syntax:**
```tsx
className="text-sm md:text-base lg:text-base" // Label
className="text-lg md:text-xl lg:text-xl" // Number
className="p-4 md:p-5 lg:p-6" // Padding
```
---
## 5. Affected Pages & Components
### 5.1 Inventory Page
**Location:** `frontend/app/inventory/page.tsx` (or relevant component)
**Stat Cards to Fix:**
- Categories [count]
- Item Types [count]
- Total Boxes [count]
**Current Implementation:** (likely)
```tsx
<div className="flex gap-4">
<div className="flex items-center gap-2">
<Layers className="text-primary" />
<div>
<p className="text-xs text-slate-500">Categories</p>
<p className="text-lg font-black">{categoriesCount}</p>
</div>
</div>
</div>
```
**Updated Implementation:**
```tsx
<div className="flex justify-between items-center gap-2 p-4 bg-slate-900 rounded-lg">
<div className="flex items-center gap-2 min-w-0">
<Layers className="w-5 h-5 text-primary" />
<span className="text-sm md:text-base text-slate-400 truncate">
Categories
</span>
</div>
<span className="text-lg md:text-xl font-black text-white whitespace-nowrap">
{categoriesCount}
</span>
</div>
```
### 5.2 Logs Page
**Location:** `frontend/app/logs/page.tsx` (or relevant component)
**Stat Card to Fix:**
- Total Events [count]
**Apply same two-column pattern.**
### 5.3 Admin Page
**Location:** `frontend/app/admin/page.tsx` (or relevant component)
**Stat Cards to Fix:**
- Any label + number displays
**Apply same two-column pattern.**
---
## 6. Edge Cases & Handling
### 6.1 Long Labels
**Problem:** "Total Events in System" might be too long on mobile
**Solution:** Use `truncate` class to show ellipsis:
```tsx
<span className="text-sm md:text-base text-slate-400 truncate">
Total Events in System
</span>
```
Result on mobile: "Total Events in S..." (with ellipsis)
### 6.2 Large Numbers (3+ digits)
**Problem:** "1234" might still overflow on very narrow screens
**Solution:**
- `whitespace-nowrap` prevents wrap
- `text-lg md:text-xl` scales appropriately
- If a number is > 999, consider abbreviating: "1.2K" instead of "1234"
### 6.3 Icon Presence/Absence
**Problem:** Some cards may have icons, some may not
**Solution:** Icon is optional, layout still works:
- With icon: [icon] [label] [gap] [number]
- Without icon: [label] [gap] [number]
Both center properly with `items-center` on the flex container.
---
## 7. Testing Strategy
### 7.1 Responsive Testing
- **iPhone SE (375px):** Verify no overflow, label truncates if needed
- **iPhone 12/13 (390px):** Verify alignment and spacing
- **iPhone 14 Pro Max (430px):** Verify layout stability
- **iPad (768px):** Verify desktop-like appearance with `md:` breakpoint
- **Desktop (1920px):** Verify `lg:` breakpoint works
### 7.2 Edge Cases
- Very long labels: "Total Events in System Very Long Name"
- Large numbers: 9999, 1234567
- No icon present
- Icon + label + number all together
### 7.3 Visual Regression
- Compare before/after on all three pages (Inventory, Logs, Admin)
- Verify card backgrounds, padding, and spacing remain consistent
- Verify typography hierarchy (label < number in weight/size)
---
## 8. Files to Modify
| File | Component(s) | Change |
|------|--------------|--------|
| `frontend/app/inventory/page.tsx` | Stat cards display | Apply two-column layout |
| `frontend/app/logs/page.tsx` | Stat cards display | Apply two-column layout |
| `frontend/app/admin/page.tsx` | Stat cards display | Apply two-column layout |
| `frontend/components/StatCard.tsx` | (Optional) New component | Create reusable StatCard component |
---
## 9. Success Criteria
✓ No content overflow on iPhone SE (375px) in portrait mode
✓ Labels and numbers both fully visible on mobile
✓ Labels truncate gracefully with ellipsis on very long text
✓ Numbers stay right-aligned without wrapping
✓ Responsive font sizes scale correctly across breakpoints (`sm`, `md`, `lg`)
✓ Visual consistency across Inventory, Logs, and Admin pages
✓ No regression on desktop/tablet layouts
---
## 10. Implementation Notes
- **Tailwind-first approach:** Use responsive utility classes, no custom CSS
- **Prefer composition:** Create reusable `StatCard` component if multiple pages share the pattern
- **Keep it DRY:** If stat cards are duplicated across pages, extract to shared component
- **Accessibility:** Ensure label and number have sufficient color contrast (WCAG AA)
---