Cold-review findings:
- #1 (CONFIRMED): deep-linking/reloading into a route inside a default-collapsed
labeled group (e.g. /app/settings, /app/libraries) left the expanded sidebar
with no active indicator, violating "active route marked in both states". Now
the group that contains the active route is force-shown (and its header shows
expanded) WITHOUT persisting — navigating away reverts to the stored preference.
Regression test added.
- #2 (mobile): a persisted-collapsed state hid the brand logo+wordmark while the
toggle is also hidden at <=980px, leaving an empty header. Restore the brand in
the mobile query (collapse is desktop-only).
Verified live (Playwright): /app/settings deep-link shows Settings active +
aria-current, System auto-revealed, Media stays collapsed, nothing persisted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add two independent, persisted collapse states to the shell sidebar:
- a header toggle that collapses it to a 60px icon rail
- collapsible accordions per labeled nav group (Media, System);
the unlabeled Primary group is always open, default-collapsed groups
State + persistence live in web/src/app/sidebarState.ts (useSidebarState);
AppShell stamps ctv-app-shell-collapsed on the shell root and the rail look
is CSS-driven. Two namespaced localStorage keys (ctv-sidebar-collapsed,
ctv-sidebar-groups) per the persisted-UI-state convention. In the rail,
accordions are ignored (all items shown icon-only, label kept in the a11y
tree + surfaced as a title tooltip, badges as a corner dot); active-route
indicator works in both states; grid-column transition respects
prefers-reduced-motion.
Tests: colocated sidebarState.test.ts + a new describe in App.test.tsx
(default-collapsed, accordion toggle+persist, rail, reload persistence).
Docs: spa-conventions §13 + decisions.md 2026-07-18.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>