LearnNewsExamplesServices
Frontmatter
id18074
titleA rail reveal enters with an 8px nudge, not a slide from its own edge
stateClosed
labels
enhancementdesignai
assigneesneo-fable-clio
createdAtSep 2, 2026, 1:42 PM
updatedAtSep 2, 2026, 4:14 PM
githubUrlhttps://github.com/neomjs/neo/issues/18074
authorneo-fable-clio
commentsCount0
parentIssue13158
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 2, 2026, 4:14 PM

A rail reveal enters with an 8px nudge, not a slide from its own edge

Closed v13.2.0/archive-v13-2-0-chunk-18 enhancementdesignai
neo-fable-clio
neo-fable-clio commented on Sep 2, 2026, 1:42 PM

Sub-issue of #13158 (dock layouts). The second of the dock-polish leaves the demo plan named (L2 — reveal motion); siblings: #18070 (L3, the reveal's geometry on a padded host), #18071 (L4, header-action tooltips).

Context

Clicking a rail tab reveals the auto-hidden pane as an overlay next to the strip. Today the overlay appears: it fades from 40% opacity and moves 8px, over the shared dock duration. The operator's design read (2026-09-02, the demo rehearsal): a reveal should slide in from the edge that owns it — a right rail slides right-to-left while the overlay's opacity rises, in the order of 300ms — so the eye reads where the pane came from and where it will go back to.

The Problem

The motion exists but is a nudge, not a slide. Measured on dev@5ae9358063:

  • resources/scss/src/dashboard/Container.scss:150-170 — .neo-dashboard-dock-reveal-overlay { animation: neo-dock-reveal-fade var(--dock-transition-duration) var(--dock-transition-easing) } with the per-edge names the rail stamps (-left/-right/-top/-bottom); the keyframes start at opacity: 0.4 and translateX(±8px) / translateY(±8px).
  • Container.scss:14/20 — --dock-transition-duration: var(--motion-panel, 280ms), --dock-transition-duration-fast: var(--motion-fast, 120ms); :136-140 collapses both to 0ms under prefers-reduced-motion: reduce (the token layer owns reduced motion, per #14779).
  • src/dashboard/dock/interaction/RevealOverlay.mjs:192-214 — the overlay listens to its own animationend, holding one MotionSignal.enter/leave entry while the reveal animates (revealMotionActive); teardown settles the producer without an end event (:234).
  • The hidden state is display: none (Container.scss:472-474): there is no exit motion, and none is asked for here.

An 8px travel from 40% opacity over 280ms reads as a flicker at pane scale (the overlay is 12rem+ wide); the pane's origin is not legible.

The Architectural Reality

  • #14779 established the dock motion contract: durations come from ONE token set (--dock-transition-*), never literals at call sites; motion is observable (dock-animating / animationend-driven completion); reduced motion collapses at the token layer. The reveal already rides that contract through the overlay's animationend → MotionSignal pair — a longer or different keyframe keeps it as long as the animation still ends on the overlay element.
  • The per-edge cls (neo-dashboard-dock-reveal-overlay-<edge>) is stamped by the rail (RevealOverlay.mjs:291-294), so the direction is already known to CSS; the change is confined to keyframes and one token.
  • ADR 0029 §2.6 (auto-hide interaction layer, #13280): the reveal is runtime-only geometry; motion does not touch the document.
  • Not this leaf: where the overlay starts (touching the strip vs. covering it) is #18070's geometry; a reveal inside an iframe pane is #18067.

The Fix

resources/scss/src/dashboard/Container.scss:

  1. One new token in the existing set: --dock-transition-duration-reveal: var(--motion-reveal, 300ms), collapsed to 0ms in the reduced-motion block beside its siblings.
  2. The overlay's animation reads that token (easing stays --dock-transition-easing, the soft ease-out).
  3. The four per-edge keyframes start fully outside the overlay's own box and transparent: from { opacity: 0; transform: translateX(100%) } for -right (-100% for -left, translateY(±100%) for -bottom / -top); the edge-less fallback keeps a fade from 0.
  4. overflow on the reveal's host must clip the travel so the overlay never paints over the strip mid-flight — read the host rule before adding one; if the rail host already clips, no change.

No JavaScript changes are expected; if MotionSignal needs the animation name (it should not — it listens to animationend on the overlay), that is a finding to record, not to work around.

Contract Ledger Matrix

Target surface Source of authority Proposed behavior Fallback Docs Evidence
--dock-transition-duration-reveal (new token, Container.scss token block) #14779 (one token set, no literals) 300ms via var(--motion-reveal, 300ms); 0ms under reduced motion absent token → the var() default ADR 0029 §2.6 one line component arm reads animation-duration
@keyframes neo-dock-reveal-from-{left,right,top,bottom} (Container.scss:167-170) this ticket start at opacity: 0, translate*(±100%) — same line component arm samples the transform at t≈0 and settled
.neo-dashboard-dock-reveal-overlay animation (Container.scss:153) #14779 duration from the new token — — same arm
RevealOverlay animationend → MotionSignal (RevealOverlay.mjs:192-214) #14779 / #14929 unchanged; must still settle after the longer animation — — existing unit arm + the component arm's settle wait

Decision Record impact: aligned-with ADR 0029 (§2.6 runtime-only reveal); amends nothing.

Acceptance Criteria

  • After a rail-tab click in a real browser, the overlay's computed animation-name is the owning edge's keyframe and animation-duration is 0.3s; sampled within the first frame its transform carries the edge-ward offset, settled it is the identity — a component spec under test/playwright/component/dashboard/ on an existing dock-rail fixture, one arm per horizontal edge at least, red on dev (today's transform at t≈0 is ≤ 8px).
  • With reducedMotion: 'reduce' emulated, the same read is 0s (the token layer collapses it; no per-site check).
  • The overlay still settles: MotionSignal leaves after the animation and the existing rail / overlay unit specs stay green (test/playwright/unit/dashboard/).
  • Visual read on the FM cockpit and on apps/workstation (right rail and left rail), both themes: the pane slides in from its strip and never paints over the strip mid-flight; screenshots in the PR, design-read.
  • No duration literal at the call site (review-checkable, #14779 AC).

Out of Scope

  • Exit motion (the hidden state is display: none; a two-phase hide is its own leaf if wanted).
  • The overlay's start geometry on padded hosts (#18070), iframe dismissal (#18067), header-action tooltips (#18071).
  • Pin / restore transitions (FLIP layer, #14929).

Related

Parent: #13158. Motion contract: #14779; FLIP layer: #14929; auto-hide interaction layer: #13280 / ADR 0029 §2.6. Siblings: #18070, #18067, #18071. Consumer read: neomjs/neo-agent-institution (FM cockpit) and apps/workstation.

Live latest-open sweep: checked the latest 20 open issues at 2026-09-02T11:38Z (none on reveal motion; #18070 is geometry, #18067 dismissal, #18027 the maximize FLIP flash); A2A claims of the last hour: L3 (Ada), L4 (Mnemosyne), #18067 (Vega) — none on motion; ask_knowledge_base (ticket) surfaced #14779 / #13280 as the contract, no equivalent leaf; the local ticket corpus has no reveal-motion ticket.

Origin Session ID: 91f83b9c-df95-4f72-a68f-d33f470792ac

Retrieval Hint: "dock reveal overlay slide from edge keyframes --dock-transition-duration-reveal MotionSignal"

📜 Clio