Skip to content

feat(client): responsive layout and cross-browser compatibility#48

Merged
steilerDev merged 2 commits into
mainfrom
feat/29-responsive-layout
Feb 8, 2026
Merged

feat(client): responsive layout and cross-browser compatibility#48
steilerDev merged 2 commits into
mainfrom
feat/29-responsive-layout

Conversation

@steilerDev
Copy link
Copy Markdown
Owner

Summary

  • Make the app shell responsive across 3 breakpoints: mobile (<768px), tablet (768-1024px), desktop (>1024px)
  • Sidebar collapses into a hamburger-toggled overlay on mobile/tablet, always visible on desktop
  • Overlay backdrop, Escape key dismiss, and auto-close on navigation
  • Touch targets meet 44px minimum on mobile/tablet
  • Cross-browser fixes: -webkit-text-size-adjust, overflow-x: hidden

Fixes #29

Test plan

  • 155 tests pass (14 new for responsive behavior)
  • Sidebar toggle open/close via hamburger menu
  • Overlay click closes sidebar
  • Escape key closes sidebar
  • Nav link click closes sidebar
  • Sidebar .open class applied correctly
  • All quality gates pass (lint, typecheck, format, test, build, audit)

🤖 Generated with Claude Code

…breakpoints

Implemented responsive layout for the app shell with three breakpoints:
- Mobile (<768px): Sidebar hidden by default, togglable via hamburger menu
- Tablet (768-1024px): Sidebar hidden by default, togglable via hamburger menu
- Desktop (>1024px): Sidebar always visible, menu button hidden

Changes:
- AppShell: Added sidebar open/close state management with useState
- AppShell: Added Escape key handler to close sidebar
- AppShell: Added overlay backdrop for mobile/tablet when sidebar is open
- Sidebar: Added isOpen and onClose props for responsive behavior
- Sidebar: Added CSS transitions for smooth slide-in/out animations
- Sidebar: Added onClick handlers to close sidebar after navigation on mobile/tablet
- Header: Hide menu button on desktop via CSS media query
- Global CSS: Added -webkit-text-size-adjust for cross-browser compatibility
- Global CSS: Added overflow-x: hidden to prevent horizontal scroll
- Touch targets: Ensured 44px minimum height for navLinks on mobile/tablet

Tests:
- Updated Sidebar.test.tsx to pass required isOpen and onClose props
- All 141 tests pass

Quality gates: lint, typecheck, format:check, test, build, audit all pass

Fixes #29

Co-Authored-By: Claude frontend-developer (Opus 4.6) <noreply@anthropic.com>
Added comprehensive test coverage for the new responsive sidebar behavior:

AppShell.test.tsx:
- Overlay visibility (not visible initially, appears on toggle)
- Clicking overlay closes sidebar
- Escape key closes sidebar
- Sidebar receives correct isOpen prop when toggled
- Multiple toggle cycles work correctly

Sidebar.test.tsx:
- .open class applied/removed based on isOpen prop
- onClose callback invoked for all nav link clicks

Test Results:
- 34 tests pass for AppShell, Sidebar, and Header
- 100% statement, function, and line coverage
- 95.65% branch coverage (exceeds 95% target)
- All 155 tests pass in full suite

Co-Authored-By: Claude qa-integration-tester (Opus 4.6) <noreply@anthropic.com>
Copy link
Copy Markdown
Owner Author

@steilerDev steilerDev left a comment

Choose a reason for hiding this comment

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

[product-architect] Architecture review for Story #29 (Responsive Layout & Cross-Browser Compatibility).

Verdict: Approved

This PR cleanly implements the responsive layout architecture I outlined for Issue #29. All six architectural requirements are satisfied and the implementation favors CSS-first responsive patterns without introducing unnecessary JavaScript-based viewport detection.

Architecture Compliance Checklist

Requirement Status Notes
AppShell owns sidebar state (useState) Pass useState(false) in AppShell, passed down as isOpen/onClose props
CSS-first responsive strategy with @media queries Pass Three CSS Modules files use media queries at the 1024px breakpoint; no JS viewport detection
translateX sidebar animation with position: fixed Pass Sidebar.module.css uses transform: translateX(-100%) / translateX(0) with transition: transform 0.3s ease
Overlay backdrop for closing sidebar Pass Conditionally rendered div.overlay in AppShell with onClick={handleCloseSidebar}
Escape key handler in useEffect Pass Correct keydown listener with cleanup in useEffect, guarded by isSidebarOpen
Touch targets 44px minimum Pass .navLink gets min-height: 44px on mobile/tablet; .menuButton already had min-width/min-height: 44px
No new dependencies Pass No package.json changes

Code Quality Notes

Well done:

  • Prop interface for Sidebar is properly typed (SidebarProps with isOpen: boolean and onClose: () => void).
  • useCallback correctly applied to handleToggleSidebar and handleCloseSidebar to stabilize references.
  • Escape handler properly checks isSidebarOpen before closing, avoiding unnecessary state updates.
  • CSS z-index layering is correct: sidebar at z-index: 100, overlay at z-index: 50 -- sidebar renders above the overlay.
  • Desktop media query hides the menu button (display: none) and resets the sidebar to position: static -- clean separation of responsive behavior.
  • Nav link clicks call onClose to auto-close sidebar on navigation, good UX on mobile.
  • Cross-browser additions (-webkit-text-size-adjust: 100%, overflow-x: hidden) are appropriate.
  • aria-hidden="true" on overlay is correct since it is a decorative/interactive backdrop, not content.

Two minor observations (non-blocking):

  1. Trailing space in className when sidebar is closed. Line 10 of Sidebar.tsx produces "sidebar " (with a trailing space) when isOpen is false due to the template literal `${styles.sidebar} ${isOpen ? styles.open : ''}`. This is functionally harmless since browsers ignore extra whitespace in class attributes, but for cleanliness you could use [styles.sidebar, isOpen && styles.open].filter(Boolean).join(' ') or a simple conditional. Not blocking since it has zero runtime impact.

  2. Overlay renders on all viewports when sidebar is open, but CSS hides it on desktop. The overlay div is conditionally rendered in React whenever isSidebarOpen is true, but the CSS sets display: none by default and only shows it on max-width: 1024px. On desktop the menu button is hidden so isSidebarOpen should never be true, but if it somehow were (e.g., programmatic state change, window resize from mobile to desktop while sidebar is open), the overlay DOM node would exist but be invisible. This is fine -- no visual or functional impact. A future improvement could reset isSidebarOpen to false on a matchMedia listener when crossing the desktop breakpoint, but that is out of scope for this story.

Test Coverage

14 new tests covering toggle, overlay click-to-close, Escape key dismiss, nav link auto-close, and .open class application. The tests correctly use userEvent (not fireEvent) for realistic interaction simulation. Test structure is clean with proper setup/teardown patterns.

Summary

The implementation is architecturally sound. CSS Modules handle all responsive behavior through media queries. State ownership is correctly centralized in AppShell. No new dependencies. CI passes. This PR is ready to merge from an architecture perspective.

Copy link
Copy Markdown
Owner Author

@steilerDev steilerDev left a comment

Choose a reason for hiding this comment

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

[product-owner]

PR #48 Review: Story #29 -- Responsive Layout & Cross-Browser Compatibility

Acceptance Criteria Verification

AC Description Verdict Evidence
1 Three breakpoints: mobile (<768px), tablet (768-1024px), desktop (>1024px) PASS CSS media queries at max-width: 1024px (mobile+tablet) and min-width: 1025px (desktop). Mobile and tablet share the same toggle behavior, which is consistent with AC2 and AC3 both describing hamburger-based toggling. The breakpoint boundary is correctly at 1024px.
2 On mobile, sidebar collapses into a hamburger menu PASS At max-width: 1024px, the sidebar uses position: fixed + transform: translateX(-100%) to hide off-screen. The hamburger button in the Header toggles it via isSidebarOpen state. Overlay backdrop appears when open.
3 On tablet, sidebar can be toggled open/closed PASS Same toggle mechanism applies at tablet widths (768-1024px). Toggle via hamburger button, close via overlay click, Escape key, or nav link click. All close paths are implemented and tested.
4 On desktop, sidebar is always visible PASS At min-width: 1025px, sidebar has position: static (always visible in flow). The hamburger menu button is hidden via display: none. The overlay also has display: none on desktop.
5 Touch targets at least 44x44px on mobile/tablet PASS Menu button: min-width: 44px; min-height: 44px in Header.module.css. Nav links: min-height: 44px with display: flex; align-items: center in Sidebar.module.css at max-width: 1024px.
6 Text readable without horizontal scrolling PASS overflow-x: hidden on body prevents horizontal scroll. Flexible layout with flex: 1 on main content area. No fixed widths on content areas.
7 Renders correctly in Chrome, Firefox, Safari, Edge PASS (with caveat) Cannot verify in automated tests, but the CSS techniques used are standard and well-supported: flexbox, CSS transitions, transform: translateX, position: fixed, media queries, -webkit-text-size-adjust: 100%. No vendor-specific or experimental features used. Reasonable cross-browser assurance.

Result: 7/7 acceptance criteria PASS.

Agent Responsibility Verification

  • Implementation by frontend-developer: PASS -- commit d0d62cb authored by Claude frontend-developer (Opus 4.6)
  • Tests by qa-integration-tester: PASS -- commit 8b41c1e has Co-Authored-By: Claude qa-integration-tester (Opus 4.6) trailer, confirming QA wrote the tests
  • Test coverage: PASS -- 100% statement/function/line coverage, 95.65% branch coverage (exceeds 95% target)
  • UAT scenarios: PASS -- uat-validator posted scenarios on issue #29, qa-integration-tester reviewed for testability
  • Architecture review: PASS -- product-architect approved the implementation plan on issue #29
  • CI checks: PASS -- Quality Gates and Docker build both passing

Scope Check

All 8 modified files are directly related to the responsive layout story:

  • AppShell.tsx / AppShell.module.css -- sidebar state management and overlay
  • Sidebar.tsx / Sidebar.module.css -- responsive sidebar with isOpen/onClose props
  • Header.module.css -- desktop media query to hide menu button
  • index.css -- cross-browser fixes
  • AppShell.test.tsx / Sidebar.test.tsx -- test updates

No scope creep detected. No unrelated files modified.

UX Quality Notes

Good implementation choices:

  • Escape key dismissal is a nice accessibility touch
  • Overlay backdrop provides clear visual context on mobile/tablet
  • CSS transitions (0.3s ease) give smooth slide-in/out animation
  • Nav links auto-close sidebar after navigation (prevents orphaned open state)
  • :focus-visible styles present on both menu button and nav links (WCAG 2.1 AA)

Decision

APPROVED. All 7 acceptance criteria are met. All agent responsibilities fulfilled. Tests exceed coverage target. No scope creep. CI is green. This PR is ready to merge.

@steilerDev steilerDev merged commit def3af8 into main Feb 8, 2026
3 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 8, 2026

🎉 This PR is included in version 1.5.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

steilerDev pushed a commit that referenced this pull request Feb 8, 2026
- Fix trailing space in Sidebar className when closed (PR #48 review)
- Wrap splitChunks in isProduction conditional (PR #49 review)

Co-Authored-By: Claude frontend-developer (Opus 4.6) <noreply@anthropic.com>
steilerDev added a commit that referenced this pull request Feb 8, 2026
…50)

- Fix trailing space in Sidebar className when closed (PR #48 review)
- Wrap splitChunks in isProduction conditional (PR #49 review)

Co-authored-by: Claude frontend-developer (Opus 4.6) <noreply@anthropic.com>
@steilerDev steilerDev deleted the feat/29-responsive-layout branch February 19, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2.5: Responsive Layout & Cross-Browser Compatibility

2 participants