AppShell: animate nav surface enter/exit (SideNav/TopNav hide-show, coordinate MobileNav slide)
- Dominant language
- TypeScript
- Stars
- 13k
- Forks
- 1.1k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 690
Description
## Summary
Add coordinated enter/exit **animations for AppShell navigation surfaces** — when the SideNav or TopNav is hidden/shown, and (consistency pass) for the MobileNav slide. Today nav-surface visibility toggles snap instantly, which looks abrupt next to the rest of the shell.
## Current state
- **MobileNav already animates well** — it slides via `transform: translateX(-100%) → translateX(0)` with a `--duration-medium` / `--ease-standard` transition and honors `prefers-reduced-motion` (`MobileNav.tsx`). Good baseline to match.
- **SideNav / TopNav hide is a hard snap.** AppShell toggles these surfaces with `styles.hidden = { display: 'none' }` — `display` isn't animatable, so hiding/showing a nav pops with no transition. There's no enter/exit for the surface appearing or leaving.
So the gap isn't "no animation anywhere" — it's that **surface show/hide snaps** while the drawer slides. We want the shell to animate consistently when nav chrome comes and goes.
## Desired behavior
- **SideNav hide/show:** animate the surface out/in (slide + fade, or width collapse) instead of `display: none` popping. Needs an exit animation before unmount (can't animate `display`), so the surface should stay mounted through the transition, then unmount / become inert.
- **TopNav hide/show:** same — slide/fade the bar out of the header row rather than snapping.
- **MobileNav:** already slides; fold it into the same motion tokens/pattern so all shell nav motion is coordinated (durations, easing, reduced-motion) rather than each surface doing its own thing.
- **Reduced motion:** all of the above respect `prefers-reduced-motion` (MobileNav's pattern is the reference).
- **Layout reflow:** when a surface hides, the main content area should reflow smoothly, not jump.
## Design notes / relationship to existing issues
This is the **surface enter/exit** slice of shell motion. It overlaps with, but is distinct from, existing issues — worth deciding whether to unify:
- **#334** — View Transitions for AppShell + collapsible sidenav (React 19 ``). A candidate *mechanism* for this animation. If we adopt View Transitions, this issue is a concrete target for it.
- **#2331** — collapse sidenav to *zero width* with an animated width transition. Adjacent: that's collapse-to-hidden via width; this is the general show/hide of the surface (and TopNav, and coordinating MobileNav).
- **#1903** — explore React View Transitions generally.
Suggested framing: keep this as the "shell nav surface enter/exit motion" issue; use #334's View Transitions (or CSS transitions with a mounted-through-exit pattern) as the implementation, and align width-collapse (#2331) so collapse and hide share one motion language.
## Open questions (for spec)
- **Mount lifecycle:** since `display` can't animate, hidden surfaces must stay mounted (and made inert / removed from tab order) during exit, or use View Transitions to animate the removal. Pick one pattern and apply it to SideNav, TopNav, and MobileNav.
- **Mechanism:** View Transitions (#334) vs. CSS transform/opacity transitions with an exit-phase state. VT is cleaner for reflow but has browser-support fallbacks; CSS is universally supported but needs the mounted-through-exit dance.
- **Tokens:** which motion tokens (`--duration-*`, `--ease-*`) — reuse MobileNav's so all shell motion matches.
- **API:** does hiding a nav stay purely presence-based (`sideNav={cond ? nav : undefined}`), and the animation is automatic? Or is there an explicit transition opt-in like #2331 proposes (`sideNavTransition`)?
## Acceptance criteria
- [ ] Hiding/showing the SideNav animates (no `display: none` snap); content reflows smoothly.
- [ ] Hiding/showing the TopNav animates consistently with the SideNav.
- [ ] MobileNav slide uses the same shared motion tokens/pattern as the above.
- [ ] All nav-surface motion respects `prefers-reduced-motion`.
- [ ] Hidden surfaces are inert / out of tab order once fully hidden (no focusable off-screen nav).
- [ ] Works across themes + light/dark.
## Notes
- Shell/motion system work — likely a small system spec (motion for shell surfaces) before build, per Component Lifecycle. Coordinate scope with #334 and #2331 so we don't ship three different collapse/hide motions.
Contributor guide
Assessment
This issue has not been assessed yet.