4.8 KiB
Responsive Shell (Phase 1 of "make the site responsive") — Design Spec
Context: this is a sub-project
The user's original ask was "ทำหน้าเว็บทั้งหมดให้ responsive" (make the whole site responsive). The app has no responsive/breakpoint infrastructure at all today, and spans hundreds of routes (navigator.ts is ~3,800 lines) built around wide, dense data tables (table-excel, horizontal drag-scroll) and multi-column filter forms. Doing "everything" in one pass isn't tractable, so this work is split into phases:
- Phase 1 (this spec): the global shell — sidebar, toolbar, main content container — since it's present on every page and is the highest-leverage, lowest-risk piece.
- Later phases (separate specs, not started): per-page table and form responsiveness. Explicitly out of scope here.
Goal for the whole initiative (confirmed with the user): usable on tablet and smaller screens. Not a full mobile-first redesign of every table — the priority is that the most-used chrome (menu, toolbar, forms) doesn't break, over making every dense table phone-perfect.
Problem (Phase 1 scope)
The sidebar built in the icon-rail feature (see 2026-07-07-sidebar-icon-rail-design.md) only toggles between full (260px) and rail (72px) width via a manual hamburger click — there's no automatic response to viewport size. On a tablet-width screen, a user has to remember to collapse it themselves; the shell doesn't adapt on its own.
Design
Behavior
- Below 1024px viewport width, the sidebar automatically switches to rail mode (72px), reusing the exact
isRailModestate +sidebar_rail_modelocalStorage key already built for the manual toggle — no new state, no new CSS classes. - The user can still manually re-expand to full width via the existing hamburger button at any point, including while the viewport is below 1024px. That manual choice is not immediately re-collapsed — the automatic behavior only fires again on an actual re-crossing of the 1024px boundary (shrinking past it again), not continuously while already narrow.
- Widening back above 1024px does not auto-expand the sidebar back to full — only auto-collapse on shrinking is automatic; expanding is always a manual, explicit action. This avoids undoing a user's deliberate manual collapse made while at desktop width.
- On initial page load, if the viewport is already narrower than 1024px, the sidebar starts in rail mode regardless of what was last stored in
localStoragefor that browser profile (e.g. a first-time tablet user gets the right starting state; a desktop user who previously storedfalseand then narrows their window gets correctly collapsed too).
Mechanism
- Angular CDK's
BreakpointObserver(@angular/cdk/layout) watches(max-width: 1024px). This emits once immediately on subscribe with the current match state, then again only when the match state changes (not on every resize tick) — which is exactly the "fires on crossing, not continuously" behavior described above, with no manual debouncing needed. MainLayoutComponent(which already ownsisRailModeand its localStorage persistence from the icon-rail feature) subscribes to this observable inngOnInit. Whenever it emitsmatches: true,isRailModeis set totrueand persisted — mirroring exactly whatonToggleRailMode(true)already does. Whenever it emitsmatches: false, nothing happens (no forced expand).- The subscription is torn down in
ngOnDestroy(a new lifecycle hook onMainLayoutComponent, which doesn't implementOnDestroytoday). LayoutModule(@angular/cdk/layout) is registered inapp.module.ts, the same wayOverlayModulewas added for the sidebar flyout.
What was checked and needs no change
tool-bar.component.scss's.user-display-namealready hasmax-width: 200px; overflow: hidden; text-overflow: ellipsis— a long display name already truncates gracefully rather than overflowing the toolbar at narrower widths.main-layout.component.html's content wrapper has no hardcodedmin-width, so it doesn't itself force horizontal overflow at 1024px and below.
Out of scope
- Any per-page table or form responsiveness (separate future phase/spec).
- Any breakpoint other than the single 1024px tablet threshold.
- Touch-specific interactions (this reuses the existing rail/flyout mouse-hover-and-click behavior as-is).
Testing
- No automated test coverage exists for the layout shell (consistent with the icon-rail feature that preceded this). Verification is manual: resize the browser (or use devtools device emulation) across the 1024px boundary and confirm the sidebar collapses/expands as described, confirm a fresh load at a narrow width starts collapsed, and confirm manually re-expanding while narrow sticks until the boundary is crossed again.