Files
rmutr/docs/superpowers/specs/2026-07-07-responsive-shell-phase1-design.md
2026-07-07 00:55:14 +07:00

46 lines
4.8 KiB
Markdown

# 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 `isRailMode` state + `sidebar_rail_mode` localStorage 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 `localStorage` for that browser profile (e.g. a first-time tablet user gets the right starting state; a desktop user who previously stored `false` and 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 owns `isRailMode` and its localStorage persistence from the icon-rail feature) subscribes to this observable in `ngOnInit`. Whenever it emits `matches: true`, `isRailMode` is set to `true` and persisted — mirroring exactly what `onToggleRailMode(true)` already does. Whenever it emits `matches: false`, nothing happens (no forced expand).
- The subscription is torn down in `ngOnDestroy` (a new lifecycle hook on `MainLayoutComponent`, which doesn't implement `OnDestroy` today).
- `LayoutModule` (`@angular/cdk/layout`) is registered in `app.module.ts`, the same way `OverlayModule` was added for the sidebar flyout.
### What was checked and needs no change
- `tool-bar.component.scss`'s `.user-display-name` already has `max-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 hardcoded `min-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.