You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Animate AppShell navigation surfaces (TopNav header + inline SideNav) so they fade and slide in when they appear, instead of snapping in instantly. The motion reuses the same tokens MobileNav already uses (--duration-medium / --ease-standard) so all shell nav motion is coordinated.
MobileNav already slides via a transform + token-driven transition and honors prefers-reduced-motion. The other shell nav surfaces had no enter transition — showing a TopNav/SideNav popped in with no motion, which looked abrupt next to the drawer's slide. This brings the surface show under one motion language.
How
Replaced the unused, non-animatable styles.hidden = { display: 'none' } with two CSS-only enter animations built on @starting-style:
navSurfaceHeaderEnter — header/top bar fades + slides down (translateY(-100%) → 0).
navSurfaceSideEnter — inline SideNav fades + slides in from the inline-start edge (translateX(-100%) → 0, RTL-aware via :is([dir="rtl"] *)).
Both use --duration-medium / --ease-standard (no raw ms / cubic-bezier strings) and collapse to near-instant under @media (prefers-reduced-motion: reduce), matching MobileNav's pattern exactly.
Applied to the TopNav header wrapper, the sidenav-only auto mobile top bar, and the inline SideNav panel (fill mode) / sticky wrapper (auto mode).
No public API added — presence stays expressed by passing the slot or undefined; the animation is automatic.
StyleX only; verified the @starting-style (including the nested RTL transform) compiles to valid CSS via the build.
Accessibility
Nothing is unmounted or made non-focusable to express visible state — a shown surface is mounted and focusable the whole time, so focus and tab order are unaffected.
A hidden surface is fully unmounted (presence-based), so there is no off-screen focusable/tabbable nav.
prefers-reduced-motion is honored: the transition duration collapses to 0.01s so surfaces appear immediately with no motion.
Testing
pnpm build — passes (StyleX compiles the @starting-style rules, including the RTL variant, to valid CSS).
pnpm test — passes (AppShell: 38/38).
pnpm lint — passes.
Added a Toggle Nav Surfaces Storybook story that shows/hides the TopNav and SideNav so reviewers can see each surface animate in, and verify the reduced-motion behavior.
Because @starting-style fires on first mount, the surfaces also animate in on the shell's initial render — consistent with how Toast/Dialog/etc. already animate on mount in this codebase.
Icons from registry — n/a; no icons added or changed.
themeProps present — no new interactive elements. The animated wrappers are the existing header / sidenav panel containers, which already carry themeProps('app-shell-header' | 'app-shell-sidenav').
No wrapper divs — no new wrapper DOM. The enter animation is applied to existing elements (header wrapper, LayoutPanel via xstyle, and the pre-existing auto-mode sticky wrapper). No extra nodes introduced.
No CSS shorthands — used transitionProperty / transitionDuration / transitionTimingFunction individually (no transition shorthand); no flex shorthand touched.
ARIA still correct — no primitives swapped; DOM structure and roles unchanged. AppShell tests (38) pass, including nav semantics/labels.
Tests pass locally — packages/core/src/AppShell/AppShell.test.tsx 38/38 green; pnpm build green; pnpm test / pnpm lint run as gates.
Sandbox/stories updated — added a Toggle Nav Surfaces story so reviewers can see each surface animate in and verify reduced-motion. No props removed, so no callsite cleanup needed.
Popover background — n/a; no popover/layer involved.
Additional notes for reviewers
Motion tokens only — --duration-medium + --ease-standard (no raw ms / cubic-bezier), matching MobileNav. Reduced-motion collapses the transition to 0.01s via @media (prefers-reduced-motion: reduce), exactly like MobileNav.
StyleX capability check — the @starting-style enter transforms (including the RTL variant :is([dir="rtl"] *) nested inside @starting-style) compile to valid CSS; verified in dist/astryx.css. @starting-style + transform + reduced-motion are all listed as supported in the StyleX capabilities reference.
A11y — nothing is unmounted or made non-focusable to represent visible state; a shown surface is fully present/focusable. A hidden surface is presence-based (unmounted), so there is no off-screen focusable nav.
Scope / known limitation — this implements the enter animation. A true exit animation would require keeping a hidden surface mounted through a transition (mounted-through-exit) or React <ViewTransition> (feat: View Transitions for AppShell and collapsible side nav #334) — a larger, spec-sized change deliberately left out to keep this patch-level. Hiding is an instant unmount, which the Animation System exploration notes is acceptable. @starting-style also fires on the shell's first mount, so surfaces animate in on initial load — consistent with existing @starting-style usage across the codebase (Toast, Dialog, TopNavMegaMenu).
On the initial mount, we don't want the animation to fire. It should only fire when the navigation is hidden later. I think it could be helpful to add a storybook story where the top and sidenavs are hidden/shown. Potentially we need to work this into the design of the API as well, to make each area of the shell layout optionally hidden and inert.
Closing this in favor of the broader direction. The reviewer feedback here reframed nav enter/exit animation into a coordinated Focus Mode, which has grown into a general AppShell region-transform plugin system — tracked in #4255 and owned there. Closing to hand it over; #4174 stays open under that umbrella.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CLA SignedThis label is managed by the Meta Open Source bot.needs:design-reviewAffects visuals — Design should review
1 participant
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Animate AppShell navigation surfaces (TopNav header + inline SideNav) so they fade and slide in when they appear, instead of snapping in instantly. The motion reuses the same tokens MobileNav already uses (
--duration-medium/--ease-standard) so all shell nav motion is coordinated.Closes #4174.
Why
MobileNav already slides via a
transform+ token-driven transition and honorsprefers-reduced-motion. The other shell nav surfaces had no enter transition — showing a TopNav/SideNav popped in with no motion, which looked abrupt next to the drawer's slide. This brings the surface show under one motion language.How
styles.hidden = { display: 'none' }with two CSS-only enter animations built on@starting-style:navSurfaceHeaderEnter— header/top bar fades + slides down (translateY(-100%) → 0).navSurfaceSideEnter— inline SideNav fades + slides in from the inline-start edge (translateX(-100%) → 0, RTL-aware via:is([dir="rtl"] *)).--duration-medium/--ease-standard(no raw ms / cubic-bezier strings) and collapse to near-instant under@media (prefers-reduced-motion: reduce), matching MobileNav's pattern exactly.undefined; the animation is automatic.@starting-style(including the nested RTL transform) compiles to valid CSS via the build.Accessibility
prefers-reduced-motionis honored: the transition duration collapses to0.01sso surfaces appear immediately with no motion.Testing
pnpm build— passes (StyleX compiles the@starting-stylerules, including the RTL variant, to valid CSS).pnpm test— passes (AppShell: 38/38).pnpm lint— passes.Notes / scope
@starting-stylecovers enter cleanly; a full exit animation would require keeping a hidden surface mounted through a transition (the "mounted-through-exit dance") or React<ViewTransition>(feat: View Transitions for AppShell and collapsible side nav #334), which is a larger, spec-sized change. Hiding is currently an instant unmount — the design docs note instant removal is acceptable. Coordinating collapse-to-width (AppShell: support collapsing sidenav to zero width with animation #2331) and View Transitions (feat: View Transitions for AppShell and collapsible side nav #334) into one exit motion is left to those issues.@starting-stylefires on first mount, the surfaces also animate in on the shell's initial render — consistent with how Toast/Dialog/etc. already animate on mount in this codebase.