{ "project": "Portfolio — Responsive Scaling & Mobile Layout", "branchName": "ralph/responsive-scaling", "description": "Make the GP system dashboard scale proportionally on large screens (1440p/4K) via fluid root font-size, and fix mobile layout with a sidebar drawer, text wrapping, and D3 chart responsiveness. Dashboard phase only.", "userStories": [ { "id": "US-001", "title": "Skip boot/login sequence for dev iteration", "description": "As a developer, I want to skip the boot/ECG/login animation during this feature branch so I can iterate on the dashboard quickly without waiting 10+ seconds each reload.", "acceptanceCriteria": [ "In src/App.tsx, change the initial Phase state from 'boot' to 'pmr' so the app loads directly to the dashboard", "The boot, ECG, and login phases are still present in code — only the initial state changes", "App loads directly to the dashboard layout on refresh", "Typecheck passes" ], "priority": 1, "passes": true, "notes": "This is temporary — US-013 reverts this change." }, { "id": "US-002", "title": "Add fluid root font-size scaling and convert CSS custom properties to rem", "description": "As a user on a high-resolution display, I want the dashboard to scale proportionally so text and UI elements are comfortably sized without manual browser zoom.", "acceptanceCriteria": [ "In src/index.css, add a font-size rule on the `html` element using clamp() that: stays at 15px below 1920px, scales linearly from 15px at 1920px to ~22px at 3840px", "The clamp formula should be: clamp(15px, calc(10px + 0.26vw), 22px) or equivalent that hits ~15px at 1920vw and ~22px at 3840vw — tune the exact values to hit these targets", "Remove or update the existing `body { font-size: 15px }` to use rem or inherit from html", "Convert CSS custom property --topbar-height from 48px to 3.2rem", "Convert CSS custom property --sidebar-width from 272px to 18.133rem", "Convert CSS custom property --subnav-height from 36px to 2.4rem", "Any other layout CSS custom properties using px should be converted to rem (divide px by 15)", "At 1920px viewport, computed values should match current px values exactly", "Typecheck passes" ], "priority": 2, "passes": true, "notes": "rem base: 15px = 1rem. Conversion: px / 15 = rem. Tailwind classes already use rem so they will auto-scale. Only inline px styles and CSS custom properties need manual conversion." }, { "id": "US-003", "title": "Convert DashboardLayout, Card, SubNav, and DetailPanel to rem", "description": "As a developer, I need the layout skeleton components to use rem so they scale with the root font-size.", "acceptanceCriteria": [ "In DashboardLayout.tsx, convert all inline style px values (padding, margin, gap, width, height, calc expressions) to rem", "In Card.tsx, convert padding, border-radius if inline, and any other px values to rem", "In SubNav.tsx, convert all inline px values to rem", "In DetailPanel.tsx, convert all inline px values (width, padding, transforms) to rem", "Tailwind classes (p-4, gap-4, etc.) can stay as-is — they already use rem", "Layout looks identical at 1920px viewport width", "Typecheck passes", "Use Playwright MCP browser_snapshot at 1920px width to verify layout matches current appearance" ], "priority": 3, "passes": true, "notes": "Conversion: px / 15 = rem. e.g., 20px = 1.333rem, 16px = 1.067rem, 12px = 0.8rem. Border-radius can stay in px." }, { "id": "US-004", "title": "Convert TopBar and Sidebar to rem", "description": "As a developer, I need the navigation chrome (TopBar + Sidebar) to scale with root font-size.", "acceptanceCriteria": [ "In TopBar.tsx, convert all inline fontSize, padding, margin, gap, width, height, and minWidth/maxWidth px values to rem", "In Sidebar.tsx, convert all inline px values to rem", "Any child components rendered inline within Sidebar (PersonHeader section, Tags, Alerts) that use inline px styles should also be converted", "Search bar min-width/max-width converted to rem", "Layout looks identical at 1920px viewport width", "Typecheck passes", "Use Playwright MCP browser_snapshot at 1920px width to verify TopBar and Sidebar appearance" ], "priority": 4, "passes": true, "notes": "Conversion: px / 15 = rem. Border-radius can stay in px." }, { "id": "US-005", "title": "Convert PatientSummaryTile, LastConsultationTile, and CareerActivityTile to rem", "description": "As a developer, I need the full-width tile components to use rem for all inline px values so they scale with viewport.", "acceptanceCriteria": [ "In PatientSummaryTile.tsx, convert all inline fontSize, padding, margin, gap, and dimension px values to rem", "In LastConsultationTile.tsx, convert all inline px values to rem", "In CareerActivityTile.tsx, convert all inline px values to rem", "In ConsultationDetail.tsx (detail panel for career), convert all inline px values to rem", "Tiles look identical at 1920px viewport width", "Typecheck passes", "Use Playwright MCP browser_snapshot at 1920px width to verify tile appearance" ], "priority": 5, "passes": true, "notes": "Conversion: px / 15 = rem. Border-radius can stay in px. These are full-width tiles in the dashboard grid." }, { "id": "US-006", "title": "Convert CoreSkillsTile, LatestResultsTile, EducationTile, and ProjectsTile to rem", "description": "As a developer, I need the remaining tile components and their detail panels to use rem for all inline px values.", "acceptanceCriteria": [ "In CoreSkillsTile.tsx, convert all inline px values to rem", "In LatestResultsTile.tsx (KPI flip cards), convert all inline px values to rem", "In EducationTile.tsx, convert all inline px values to rem", "In ProjectsTile.tsx, convert all inline px values to rem", "In SkillDetail.tsx, SkillsAllDetail.tsx, KPIDetail.tsx, EducationDetail.tsx, and ProjectDetail.tsx — convert all inline px values to rem", "Tiles look identical at 1920px viewport width", "Typecheck passes", "Use Playwright MCP browser_snapshot at 1920px width to verify tile appearance" ], "priority": 6, "passes": true, "notes": "Conversion: px / 15 = rem. Border-radius can stay in px. This story covers 4 tiles + 5 detail components — the work is mechanical/repetitive." }, { "id": "US-007", "title": "Add mobile sidebar drawer with hamburger toggle", "description": "As a mobile user, I want to access the sidebar content (person details, tags, alerts) via a slide-out drawer triggered by a hamburger icon in the TopBar.", "acceptanceCriteria": [ "A hamburger menu icon (lucide-react Menu icon) appears in the TopBar on screens below lg breakpoint (1024px)", "The hamburger icon is hidden on lg+ screens (sidebar is inline on desktop)", "Tapping the hamburger icon opens the Sidebar as a slide-out drawer overlay from the left", "Drawer includes a semi-transparent backdrop that closes the drawer when tapped", "Drawer contains the full Sidebar content (PersonHeader, Tags, Alerts)", "Drawer has a close button (lucide-react X icon) in its top-right corner", "Pressing Escape closes the drawer", "Drawer animation: translateX(-100%) to translateX(0), 200ms ease-out", "Drawer respects prefers-reduced-motion (skip animation, show/hide instantly)", "Sidebar remains inline (not a drawer) on lg+ screens — no desktop behavior change", "Drawer state managed via useState in DashboardLayout.tsx", "Typecheck passes", "Use Playwright MCP to verify: at 375px width, hamburger visible, click opens drawer with sidebar content, click backdrop closes it" ], "priority": 7, "passes": true, "notes": "The drawer component can be built into DashboardLayout or as a separate MobileSidebarDrawer component. Keep it simple." }, { "id": "US-008", "title": "Fix text wrapping and overflow across all tiles", "description": "As a mobile user, I want all text content to wrap properly within containers so nothing is cut off or requires horizontal scrolling.", "acceptanceCriteria": [ "Add overflow-wrap: break-word to the main content area in DashboardLayout or index.css", "Add min-width: 0 to flex children in tile components where text could overflow (flex items don't shrink below content size by default)", "Long skill names in CoreSkillsTile truncate with text-overflow: ellipsis rather than breaking layout", "PatientSummaryTile stats grid reflows to fewer columns on narrow screens (minmax grid already handles this — verify)", "KPI cards in LatestResultsTile stack vertically on mobile (if using a grid/flex row, ensure it wraps)", "Career Activity, Education, and Project entries wrap text cleanly at narrow widths", "Add overflow-x: hidden on the main scrollable content area as a safety net", "No horizontal scrollbar at any viewport width from 320px to 3840px", "Typecheck passes", "Use Playwright MCP to verify at 375px width: no horizontal scrollbar, all text visible and wrapped" ], "priority": 8, "passes": true, "notes": "The key CSS properties: overflow-wrap: break-word, min-width: 0 on flex children, overflow-x: hidden on scroll container. Most wrapping issues come from flex items not shrinking." }, { "id": "US-009", "title": "TopBar mobile refinements", "description": "As a mobile user, I want the TopBar to remain functional and readable at narrow viewport widths without overflow.", "acceptanceCriteria": [ "Search trigger remains accessible on mobile — if the full search bar is hidden below md, ensure a search icon button is visible that opens the Command Palette", "Brand text, session info, and other TopBar content do not cause horizontal overflow at 320px width", "TopBar height scales with rem (already converted in US-004 — verify it works)", "All TopBar interactive elements have adequate touch targets (minimum 44px)", "Typecheck passes", "Use Playwright MCP to verify TopBar at 375px width: all content visible, no overflow, search accessible" ], "priority": 9, "passes": true, "notes": "TopBar already has responsive breakpoints (hidden sm:inline etc). This story fills gaps — mainly the search accessibility on mobile and verifying nothing overflows." }, { "id": "US-010", "title": "Career Constellation D3 chart — responsive container sizing", "description": "As a user on any device, I want the D3 career constellation chart to fit its container without overflowing at any viewport width.", "acceptanceCriteria": [ "Chart SVG width and height are derived from container dimensions, not hardcoded px values", "Chart re-renders or resizes when the container/viewport size changes (use ResizeObserver or window resize listener)", "D3 force simulation parameters (charge strength, link distance, node spacing) scale relative to available width", "On mobile (<768px), chart remains visible and nodes don't overlap excessively or overflow the container", "Node labels remain legible — on narrow viewports (<768px), consider hiding secondary/skill labels to reduce clutter", "Chart does not cause horizontal scrollbar at any viewport width", "Typecheck passes", "Use Playwright MCP to verify chart at 375px and 1920px: chart fits container, no overflow, labels readable" ], "priority": 10, "passes": true, "notes": "CareerConstellation.tsx uses D3 force simulation with SVG. The SVG likely has fixed dimensions — make them responsive to container size." }, { "id": "US-011", "title": "Career Constellation D3 chart — mobile touch interaction", "description": "As a mobile user, I want to interact with the career constellation chart using touch to explore roles and skills.", "acceptanceCriteria": [ "Nodes are tappable on touch devices with adequate touch target size (minimum 44px hit area)", "Tap on a role node triggers the same action as desktop click (opens role detail)", "Tap on a skill node triggers the same action as desktop click (opens skill detail)", "No hover-dependent information is inaccessible on touch — tooltips show on tap, or info is always visible", "Chart does not interfere with page scroll — vertical scroll works when touching the chart background (not a node)", "Typecheck passes", "Use Playwright MCP to verify at 375px: nodes tappable, page still scrollable when touching chart background" ], "priority": 11, "passes": true, "notes": "D3 click handlers likely already work for touch (click events fire on tap). Main concerns: touch target size and scroll interference." }, { "id": "US-012", "title": "Comprehensive viewport verification", "description": "As a developer, I need to verify the entire dashboard renders correctly at all target viewport sizes with no regressions.", "acceptanceCriteria": [ "Use Playwright MCP to resize browser to 375px wide (iPhone SE): single column layout, sidebar drawer works, all text wraps, no horizontal overflow, D3 chart fits", "Use Playwright MCP to resize browser to 768px wide (iPad portrait): single column layout, sidebar drawer works, comfortable spacing", "Use Playwright MCP to resize browser to 1024px wide (iPad landscape): sidebar inline, 2-column grid appears, no overflow", "Use Playwright MCP to resize browser to 1920px wide (1080p): layout visually matches pre-feature-branch appearance — no regression", "Use Playwright MCP to resize browser to 2560px wide (1440p): everything scaled ~25% larger, proportional layout, text clearly readable", "No horizontal scrollbar at any tested width", "All tiles visible and content readable at every width", "Fix any issues discovered during verification", "Typecheck passes" ], "priority": 12, "passes": true, "notes": "This is a verification and fix-up pass. Use Playwright MCP browser_resize + browser_snapshot at each viewport width. Fix any issues found inline." }, { "id": "US-013", "title": "Re-enable boot/login sequence", "description": "As a user, I want the full boot → ECG → login experience restored so the production app has the theatrical intro sequence.", "acceptanceCriteria": [ "In src/App.tsx, change the initial Phase state back from 'pmr' to 'boot'", "Boot → ECG → Login → Dashboard sequence works end to end", "No other changes to App.tsx beyond reverting the initial state", "Typecheck passes", "Use Playwright MCP to verify: app starts at boot sequence, progresses through ECG and login, arrives at dashboard" ], "priority": 13, "passes": true, "notes": "Reverts the change from US-001. Must be the final story." } ] }