Skip to content

feat: mobile responsive layout#71

Merged
GeneralJerel merged 4 commits intomainfrom
feat/mobile-responsive
Mar 27, 2026
Merged

feat: mobile responsive layout#71
GeneralJerel merged 4 commits intomainfrom
feat/mobile-responsive

Conversation

@GeneralJerel
Copy link
Copy Markdown
Collaborator

Summary

  • Header banner adapts to mobile: smaller padding/text, subtitle hidden on narrow screens, compact buttons
  • Demo gallery drawer becomes a full-screen bottom sheet on mobile (< 640px), keeps side drawer on desktop
  • Brand shell uses tighter padding and smaller border-radius on small viewports
  • Adds viewport meta tag for proper mobile scaling

Addresses #69 — this is the responsive approach (vs the desktop-only gate in #70)

Test plan

  • Resize browser to < 640px — demo gallery opens as full-screen sheet from bottom
  • Resize to 640px+ — demo gallery slides in from right as before
  • Header looks clean at 320px, 375px, 768px, and 1440px widths
  • Category filter scrolls horizontally on mobile
  • Test on Chrome DevTools mobile emulator (iPhone SE, Pixel 7)

🤖 Generated with Claude Code

- Header banner: smaller padding, text, and buttons on mobile; hide
  subtitle and "Demos" label on narrow screens
- Demo gallery: full-screen bottom sheet on mobile, side drawer on sm+
- Brand shell: tighter padding and smaller border-radius on small screens
- Add viewport meta tag for proper mobile scaling
@GeneralJerel
Copy link
Copy Markdown
Collaborator Author

Works well for most cases but the output might not be mobile friendly need to have a modal warning instead best view on desktop

@GeneralJerel
Copy link
Copy Markdown
Collaborator Author

Sample

image

Shows a dismissible glassmorphic modal on viewports <= 768px warning
that interactive visualizations work best on larger screens. Dismissed
state persists for the session via sessionStorage.
…p modal

Replace useEffect+useState with useSyncExternalStore for sessionStorage
subscription, and use CSS md:hidden instead of JS matchMedia for the
mobile viewport gate.
Copy link
Copy Markdown
Collaborator Author

@GeneralJerel GeneralJerel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: Mobile Responsive Layout

Overall: Solid mobile responsive work. The drawer-to-bottom-sheet pattern and responsive header are well-executed. A few issues worth addressing:

Issues

1. maximum-scale=1 blocks user zoom (accessibility)apps/app/src/app/layout.tsx:13

maximum-scale=1 prevents pinch-to-zoom, violating WCAG 2.1 SC 1.4.4. This was historically used to prevent iOS double-tap zoom glitches, but those are fixed in modern Safari. Remove it:

-<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />

2. sessionStorage access can throwapps/app/src/components/desktop-tip-modal.tsx:20

In privacy-restricted contexts (e.g., Safari private browsing on older versions, some embedded webviews), sessionStorage.getItem() and .setItem() can throw. The useSyncExternalStore client snapshot and dismiss function should wrap these in try/catch:

() => {
  try { return !sessionStorage.getItem(DISMISSED_KEY); }
  catch { return false; }
},

3. Desktop tip modal has no Escape-to-dismissapps/app/src/components/desktop-tip-modal.tsx

The DemoGallery already supports Escape key (demo-gallery/index.tsx:27-29). The desktop tip modal should be consistent — users expect Escape to dismiss overlays. A simple useEffect with a keydown listener would do it.

4. Breakpoint mismatch between responsive layout and modal visibilityapps/app/src/components/desktop-tip-modal.tsx:34

The responsive layout uses sm: (640px) as its breakpoint, but the modal uses md:hidden (768px). This means users between 640–768px get the desktop layout but still see the "best viewed on desktop" warning. Either:

  • Change the modal to sm:hidden to match the layout breakpoint, or
  • Accept this intentionally (tablets may still benefit from the tip)

If intentional, a comment would help clarify.

Minor Nits

  • textAlign: "center" as const (desktop-tip-modal.tsx:53) — The as const cast shouldn't be needed with React 19's types. Can likely be removed.
  • Heavy inline styles on the modal — The modal uses ~40 lines of inline styles while the rest of the PR moves styles into CSS (the drawer refactor). For consistency, these could live in globals.css alongside the other glassmorphic patterns. Not blocking, but would improve maintainability.

What looks good

  • Moving drawer positioning from inline styles to CSS classes (demo-gallery-drawer) is a clean improvement
  • Mobile-first CSS for the drawer (default: bottom sheet, @media (min-width: 640px): side panel) is the right approach
  • overscroll-contain on the scrollable card list prevents scroll chaining — nice touch
  • Larger touch target on the close button (p-2 -mr-1) is a good mobile UX detail
  • useSyncExternalStore is the correct React pattern for subscribing to sessionStorage without the lint error
  • Responsive header typography/spacing degrades gracefully

- Remove maximum-scale=1 from viewport meta to allow pinch-to-zoom (WCAG 2.1)
- Wrap sessionStorage access in try/catch for privacy-restricted contexts
- Add Escape key to dismiss desktop tip modal (consistent with demo gallery)
- Change tip modal breakpoint from md:hidden to sm:hidden to match layout
@GeneralJerel
Copy link
Copy Markdown
Collaborator Author

Self-Review Findings

Verdict: No merge blockers (P0=0, P1=0)

Findings

ID Sev Category File:Line Issue Suggested Fix
F1 P2 Bug desktop-tip-modal.tsx:33 useEffect missing dependency array — keydown listener re-registered every render Add [notDismissed] as deps; wrap dismiss in useCallback or inline it
F2 P2 A11y desktop-tip-modal.tsx:45 Modal lacks role="dialog", aria-modal="true", aria-labelledby Add ARIA attributes to the overlay div
F3 P2 A11y desktop-tip-modal.tsx:44 No focus trap — keyboard users can Tab behind the modal Auto-focus "Continue anyway" button; constrain Tab within modal
F4 P3 UX desktop-tip-modal.tsx:47 sm:hidden (640px) may fire on small tablets where app is still usable Consider md:hidden (768px) or accept 640px
F5 P3 UX globals.css:613 Mobile drawer missing safe-area-inset for notched devices Add env(safe-area-inset-*) padding
F6 P3 Maint globals.css:309 max-width: 768px overlaps Tailwind's md at exactly 768px Use max-width: 767px for consistency

Testing Gaps

  • No tests for DesktopTipModal (dismiss, sessionStorage, Escape key)
  • No visual regression tests for responsive breakpoints
  • No automated accessibility testing
  • Mobile drawer untested on notched devices

Open Questions

  • sessionStorage vs localStorage — tip reappears every new tab; intentional?
  • Swipe-to-close gesture for the mobile bottom-sheet drawer?

🤖 Generated with Claude Code

@GeneralJerel GeneralJerel merged commit e0db59e into main Mar 27, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant