Mobile-responsive table view with card list fallback #3

Open
opened 2026-03-07 06:38:32 +00:00 by adamksmith · 0 comments
Owner

Problem

The table view is unusable on mobile. It's a wide <table> with 11 columns (Slot, Device, Model, Serial, WWN, FW, Capacity, Pool, Temp, Hours, Health) that overflows horizontally, requiring awkward side-scrolling. Mono-spaced serials and WWNs make it even wider. The grid view works fine on mobile since the cells naturally reflow, but switching to table view on a phone is painful.

This matters because the whole point of a drive health dashboard is quick glances — often from your phone while standing in front of the rack.

Proposed Solution

Responsive table redesign with three tiers: desktop (full table), tablet (condensed table), and phone (card list). No separate mobile components — pure CSS breakpoints with progressive disclosure.

Implementation Details

1. Breakpoint Strategy

≥1024px  →  Full table (current layout, all 11 columns)
768–1023px  →  Condensed table (hide WWN, FW, collapse Pool into icon)
<768px  →  Card list (each drive = a stacked card)

2. Desktop (≥1024px) — No Changes

Current table layout works perfectly on wide screens. Keep as-is.

3. Tablet (768–1023px) — Condensed Table

Hide lower-priority columns to fit without horizontal scroll:

Keep Hide Rationale
Slot Always needed for physical identification
Device Quick reference
Model Truncate with text-overflow: ellipsis, max-width: 140px
Serial Primary identifier
WWN Available in detail modal on click
FW Rarely glanced at
Capacity
Pool Show as colored dot/icon instead of text label
Temp Critical at-a-glance metric
Hours Available in detail modal
Health Always visible

This brings it down to 7 visible columns which fits comfortably at 768px.

4. Mobile (<768px) — Drive Cards

Replace the table entirely with a vertical card list. Each drive renders as a compact card:

┌─────────────────────────────────────┐
│ ● sda                    Slot 0     │  ← health dot + device + slot
│ ST16000NM001G              16.0 TB  │  ← model + capacity
│ Serial: ZL210000A                   │  ← serial (mono, tappable to copy)
│ ┌────────┐ ┌────────┐ ┌──────────┐  │
│ │ 34°C   │ │ 2.1y   │ │ 🟢 tank  │  │  ← temp, hours, pool badge
│ └────────┘ └────────┘ └──────────┘  │
│                          Healthy ●  │  ← status pill
└─────────────────────────────────────┘

Design notes:

  • Cards use the same health-colored left border as table rows (3px solid with health dot color)
  • Tap a card to open the existing drive detail modal (same as table row click)
  • Serial is styled with user-select: all so a tap selects the full string for copying
  • Pool badge uses the same accent-colored style as the table view
  • Cards have a subtle gap: 8px between them for thumb-friendly scrolling
  • Empty slots are hidden in card view (no value showing empty bays on mobile)

5. Implementation Approach

Use a useMediaQuery hook or window.matchMedia to detect breakpoint and conditionally render:

function useBreakpoint() {
  const [bp, setBp] = useState("desktop");
  useEffect(() => {
    const check = () => {
      const w = window.innerWidth;
      setBp(w < 768 ? "mobile" : w < 1024 ? "tablet" : "desktop");
    };
    check();
    window.addEventListener("resize", check);
    return () => window.removeEventListener("resize", check);
  }, []);
  return bp;
}

Then in TableView:

function TableView({ enclosure, onSelect, selectedSerial, t }) {
  const bp = useBreakpoint();
  
  if (bp === "mobile") {
    return <CardListView enclosure={enclosure} onSelect={onSelect} t={t} />;
  }
  
  // Existing table with column visibility based on bp
  const showWwn = bp === "desktop";
  const showFw = bp === "desktop";
  const showHours = bp === "desktop";
  const compactPool = bp === "tablet";
  
  // ... render table with conditional columns
}

6. Grid View Mobile Adjustments

The grid view already works reasonably well on mobile since the cells resize, but make these tweaks:

  • Reduce column count on mobile: Override to 4 columns for 24-slot enclosures, 3 columns for 12-slot enclosures (instead of 6/9)
  • Increase touch target size: Minimum 44×44px per cell (iOS HIG / WCAG 2.5.5)
  • Dynamically scaled text already handles the rest via the existing useResizeScale hook
function GridView({ enclosure, onSelect, t }) {
  const bp = useBreakpoint();
  let cols;
  if (bp === "mobile") {
    cols = enclosure.total_slots <= 12 ? 3 : 4;
  } else {
    cols = enclosure.total_slots <= 12 ? 6 : enclosure.total_slots <= 24 ? 6 : 9;
  }
  // ...
}

7. Header / Controls Mobile Layout

  • Stack the view toggle (Grid/Table) and theme toggle below the title on mobile instead of inline
  • Stat cards: switch from 5-across flex to 2-column grid + 1 full-width "Overall" card on mobile

Acceptance Criteria

  • Table view renders without horizontal scroll at any viewport width
  • Mobile card list view activates below 768px with all critical drive info visible
  • Tablet condensed table hides WWN/FW/Hours columns, shows 7 columns
  • Tapping a card or row opens the drive detail modal
  • Serial number in card view supports tap-to-select for copying
  • Grid view reduces column count on mobile with minimum 44px touch targets
  • Stat cards reflow to 2-column grid on mobile
  • Header controls stack vertically on mobile
  • No horizontal scrolling at any breakpoint
  • Tested at 375px (iPhone SE), 390px (iPhone 15), 768px (iPad), 1024px+
## Problem The table view is unusable on mobile. It's a wide `<table>` with 11 columns (Slot, Device, Model, Serial, WWN, FW, Capacity, Pool, Temp, Hours, Health) that overflows horizontally, requiring awkward side-scrolling. Mono-spaced serials and WWNs make it even wider. The grid view works fine on mobile since the cells naturally reflow, but switching to table view on a phone is painful. This matters because the whole point of a drive health dashboard is quick glances — often from your phone while standing in front of the rack. ## Proposed Solution Responsive table redesign with three tiers: desktop (full table), tablet (condensed table), and phone (card list). No separate mobile components — pure CSS breakpoints with progressive disclosure. ### Implementation Details #### 1. Breakpoint Strategy ``` ≥1024px → Full table (current layout, all 11 columns) 768–1023px → Condensed table (hide WWN, FW, collapse Pool into icon) <768px → Card list (each drive = a stacked card) ``` #### 2. Desktop (≥1024px) — No Changes Current table layout works perfectly on wide screens. Keep as-is. #### 3. Tablet (768–1023px) — Condensed Table Hide lower-priority columns to fit without horizontal scroll: | Keep | Hide | Rationale | |---|---|---| | Slot | | Always needed for physical identification | | Device | | Quick reference | | Model | | Truncate with `text-overflow: ellipsis`, `max-width: 140px` | | Serial | | Primary identifier | | | ~~WWN~~ | Available in detail modal on click | | | ~~FW~~ | Rarely glanced at | | Capacity | | | | Pool | | Show as colored dot/icon instead of text label | | Temp | | Critical at-a-glance metric | | | ~~Hours~~ | Available in detail modal | | Health | | Always visible | This brings it down to 7 visible columns which fits comfortably at 768px. #### 4. Mobile (<768px) — Drive Cards Replace the table entirely with a vertical card list. Each drive renders as a compact card: ``` ┌─────────────────────────────────────┐ │ ● sda Slot 0 │ ← health dot + device + slot │ ST16000NM001G 16.0 TB │ ← model + capacity │ Serial: ZL210000A │ ← serial (mono, tappable to copy) │ ┌────────┐ ┌────────┐ ┌──────────┐ │ │ │ 34°C │ │ 2.1y │ │ 🟢 tank │ │ ← temp, hours, pool badge │ └────────┘ └────────┘ └──────────┘ │ │ Healthy ● │ ← status pill └─────────────────────────────────────┘ ``` Design notes: - Cards use the same health-colored left border as table rows (3px solid with health dot color) - Tap a card to open the existing drive detail modal (same as table row click) - Serial is styled with `user-select: all` so a tap selects the full string for copying - Pool badge uses the same accent-colored style as the table view - Cards have a subtle `gap: 8px` between them for thumb-friendly scrolling - Empty slots are hidden in card view (no value showing empty bays on mobile) #### 5. Implementation Approach Use a `useMediaQuery` hook or `window.matchMedia` to detect breakpoint and conditionally render: ```jsx function useBreakpoint() { const [bp, setBp] = useState("desktop"); useEffect(() => { const check = () => { const w = window.innerWidth; setBp(w < 768 ? "mobile" : w < 1024 ? "tablet" : "desktop"); }; check(); window.addEventListener("resize", check); return () => window.removeEventListener("resize", check); }, []); return bp; } ``` Then in `TableView`: ```jsx function TableView({ enclosure, onSelect, selectedSerial, t }) { const bp = useBreakpoint(); if (bp === "mobile") { return <CardListView enclosure={enclosure} onSelect={onSelect} t={t} />; } // Existing table with column visibility based on bp const showWwn = bp === "desktop"; const showFw = bp === "desktop"; const showHours = bp === "desktop"; const compactPool = bp === "tablet"; // ... render table with conditional columns } ``` #### 6. Grid View Mobile Adjustments The grid view already works reasonably well on mobile since the cells resize, but make these tweaks: - **Reduce column count on mobile**: Override to 4 columns for 24-slot enclosures, 3 columns for 12-slot enclosures (instead of 6/9) - **Increase touch target size**: Minimum 44×44px per cell (iOS HIG / WCAG 2.5.5) - **Dynamically scaled text already handles the rest** via the existing `useResizeScale` hook ```jsx function GridView({ enclosure, onSelect, t }) { const bp = useBreakpoint(); let cols; if (bp === "mobile") { cols = enclosure.total_slots <= 12 ? 3 : 4; } else { cols = enclosure.total_slots <= 12 ? 6 : enclosure.total_slots <= 24 ? 6 : 9; } // ... } ``` #### 7. Header / Controls Mobile Layout - Stack the view toggle (Grid/Table) and theme toggle below the title on mobile instead of inline - Stat cards: switch from 5-across flex to 2-column grid + 1 full-width "Overall" card on mobile ### Acceptance Criteria - [ ] Table view renders without horizontal scroll at any viewport width - [ ] Mobile card list view activates below 768px with all critical drive info visible - [ ] Tablet condensed table hides WWN/FW/Hours columns, shows 7 columns - [ ] Tapping a card or row opens the drive detail modal - [ ] Serial number in card view supports tap-to-select for copying - [ ] Grid view reduces column count on mobile with minimum 44px touch targets - [ ] Stat cards reflow to 2-column grid on mobile - [ ] Header controls stack vertically on mobile - [ ] No horizontal scrolling at any breakpoint - [ ] Tested at 375px (iPhone SE), 390px (iPhone 15), 768px (iPad), 1024px+
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adamksmith/jbod-monitor#3