LearnNewsExamplesServices
Frontmatter
id18116
titleA rail reveal slides in but cuts out: dismissal applies display:none at once, so there is no exit motion
stateClosed
labels
bug
assigneesneo-fable
createdAtSep 2, 2026, 5:38 PM
updatedAtSep 2, 2026, 6:29 PM
githubUrlhttps://github.com/neomjs/neo/issues/18116
authorneo-fable
commentsCount0
parentIssue13158
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 2, 2026, 6:21 PM

A rail reveal slides in but cuts out: dismissal applies display:none at once, so there is no exit motion

neo-fable
neo-fable commented on Sep 2, 2026, 5:38 PM

Sub-issue of #13158 (dock layouts). Reported by the operator on the Workstation, 2026-09-02 15:35Z: "rails slide in animations are nice now, however, the team forgot about the opposite direction => slide out on focus leave". The entry motion is #18074 / PR #18079, which scoped this out explicitly ("Exit motion — the hidden state is display: none; a two-phase hide is its own leaf if wanted"). This is that leaf.

The Problem

Neo.dashboard.dock.interaction.RevealOverlay#syncSnapshot adds neo-dashboard-dock-reveal-overlay-hidden the moment visible turns false, and that class is display: none (resources/scss/src/dashboard/Container.scss). The reveal's content therefore emerges from its strip on entry and vanishes in one frame on dismissal — a dismissal by focus leave, Escape, an outside click, or the pointer leaving after dwell all cut. The asymmetry reads as a glitch next to the entry, and it is the more frequent of the two motions on a rail-heavy layout.

The Architectural Reality

  • RevealOverlay#syncRevealMotion opens a MotionSignal window on the hidden→visible boundary and closes it on visible→hidden; onMotionAnimationEnd closes it on the ROOT's own animationend (target identity; the children's ends bubble past). destroy() settles without an end event.
  • The entry keyframes (neo-dock-reveal-fade on the root, neo-dock-reveal-from-<edge> on the header and pane slot, 100cqw/100cqh travel against the root's size container) read --dock-transition-duration-reveal, which reduced motion collapses to 0ms at the token layer.
  • The iframe shield (:has(.neo-dashboard-dock-reveal-overlay:not(…-hidden))) and the rail's outside-pointer listener key on the hidden class and on the machine state respectively.

The Fix

A two-phase hide, owned by the overlay:

  1. On a visible→hidden boundary with a mounted DOM, the overlay stamps neo-dashboard-dock-reveal-overlay-leaving instead of -hidden and keeps the motion window open. The leaving rules run the reverse of the entry — root fades out, header and slot travel back to the edge — with animation-fill-mode: forwards, so the content holds its end state until the hidden class lands one round trip later, and pointer-events: none, so the departing panel never takes a click meant for what is under it.
  2. On the root's animationend while leaving, the overlay applies -hidden, drops -leaving, and settles the motion window — the same seam the entry already uses. Reduced motion collapses the duration to 0ms, which still fires the end event.
  3. A re-entry while leaving cancels the leave (drop -leaving, stay visible); teardown while leaving hides and settles without an end event, as today. A stuck exit (no end event — a consumer sheet that sets animation: none) hides after the motion signal's settle grace rather than never.
  4. The iframe shield excludes a leaving overlay, so a click into a frame during the exit lands in the frame.

Acceptance Criteria

  • AC-1 Unit (DockRevealOverlay.spec.mjs): a visible→hidden flip on a mounted overlay stamps -leaving, not -hidden, and keeps the motion window open; the root's animationend then stamps -hidden, drops -leaving, and settles; a hidden→visible flip while leaving cancels the leave; destroy while leaving settles. Red on dev for the first.
  • AC-2 Component (dock-rail-origin or dock-rail fixture): reveal, dismiss with Escape, and the overlay carries -leaving with a running animation on its pane slot before it becomes hidden; red on dev (hidden lands at once).
  • AC-3 The Workstation's rails, read by eye on the served app: the panel slides back under its strip on focus leave and on Escape, both neo themes.
  • AC-4 The existing reveal, rail, iframe-dismissal and motion-signal suites stay green (unit/dashboard, component/dashboard).

Out of Scope

  • A reverse slide-in from the current position on a re-entry during a leave. What happens instead, and it is load-bearing: dropping the leaving class changes animation-name back, so the browser restarts the ENTRY keyframes from the edge, and that restart's end event is what settles the still-open motion window (corrected after PR #18118's review — the original line claimed a snap-back with no restart).
  • Any change to the entry motion or its tokens.

Live latest-open sweep at 2026-09-02T15:36Z: no reveal-exit ticket (#18115 is the native-drop hold, #18074 is the entry, closed by PR #18079); A2A: no claim; Knowledge Base: the motion contract tickets (#13280, #14779, #14780), no leaf.

Authored by Mnemosyne (@neo-fable, Claude Fable 5.1, Claude Code) · session 37bbc610-f0cd-4abb-95c2-eb70336a86f3

tobiu referenced in commit 9360b3b - "feat(dock): a dismissed rail reveal slides back under its strip before it hides (#18116) (#18118) on Sep 2, 2026, 6:21 PM
tobiu closed this issue on Sep 2, 2026, 6:21 PM