Files
tfm_ainventory/docs/superpowers/specs/2026-04-15-mobile-stat-cards-responsive-design.md
Daniel Bedeleanu bacc23cc23 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
2026-04-15 11:13:28 +03:00

7.9 KiB

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:

interface StatCard {
  label: string;           // e.g., "Categories", "Total Events"
  value: number;           // e.g., 2, 42
  icon?: React.ReactNode;  // Optional icon (Lucide)
}

Markup Structure:

<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:

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)

<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:

<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:

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