Skip to content

Slide the sidebar and Chat for their keyboard shortcuts - #539

Merged
MaggieAppleton merged 4 commits into
mainfrom
design-keyboard-toggle-motion
Oct 13, 2026
Merged

MaggieAppleton merged 4 commits into
mainfrom
design-keyboard-toggle-motion

Conversation

@MaggieAppleton

@MaggieAppleton MaggieAppleton commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Opening or closing the Projects sidebar (⌘) or Chat (⌘.) with a shortcut snapped, while a click slid. They now slide the same way for both, with the same duration and easing.

Why it snapped

motionImmediately() (apps/web/src/motion-input.ts, added in ee4bb35 Wire purposeful motion into app surfaces) settles every presence transition at once when the last input was a key. That rule fits menus and view swaps. But the sidebar and Chat tracks resize the workspace, and a snap hides where the space went.

Change

  • navigation-shell.tsx: the sidebar presence settles at once only when the layout mode changes. It no longer settles just because a key was pressed.
  • workspace.tsx: split Chat's presence never takes the keyboard gate. It still settles at once when the layout mode changes, so widening from compact to split doesn't slide Chat in. Compact and phone keep their current behaviour.
  • Fixes an older focus loss: pressing Enter on Show sidebar used to leave focus on the page body, because the button disappears. Focus now moves to Hide sidebar in the opened inline sidebar.
  • Reduced motion is unchanged. useTransitionPresence still settles at once under prefers-reduced-motion, and CSS transition: none still applies.
  • First load and restore are unchanged: presence starts in its final state.
  • Focus is unchanged. Opening Chat moves focus to its heading with preventScroll. Chat's track clips with overflow: clip, so focus can't scroll it. Closing either pane returns focus to the opener only when focus was inside the pane.

Still instant for keyboard input (on purpose): menus, popovers, dialogs, Document/Decisions/Build view swaps, compact/phone pane swaps, the scroll back to top on a phone tab, and count/icon/alert feedback (data-motion-input="pointer" CSS).

Docs: apps/web/DESIGN.md gains a Do rule. docs/architecture.md notes that the two tracks don't count as content swaps.

Frame strips (keyboard shortcut, 1280×720, frames about 20–40ms apart)

Before: every frame already shows the final state. After: the pane moves across the frames.

Chat close via ⌘.
Chat open via ⌘.

Tests

  • New e2e tests in e2e/keyboard-shortcuts.e2e.ts:
    • The chords fire a transitionrun on the width of .motion-sidebar or .workspace-chat-panel, once per toggle. On the old code this test fails (0 runs).
    • Under reduced motion they fire none.
  • bun run e2e e2e/keyboard-shortcuts.e2e.ts e2e/paper-layout.e2e.ts: 19 passed. bun run ci and bun run types are green. bun run ci was run without the git-ignored local design-jig files.
  • A separate commit renews the design-contract hash for workspace.tsx and navigation-shell.tsx. The class expressions didn't change; only the presence settle argument did.

🤖 Generated with Claude Code

Sidebar close via shortcut

Sidebar open via shortcut

Landing validation

The motion layer is rebased onto the merged loading shell and phone header. Its four prepared patches are unchanged, including the reviewed hashes for the combined workspace. CI, types, production build, 20 focused units, 38 workspace browsers, and eight child-phone cases passed locally. Prior GitHub CI was green; administrator bypass avoids another full cycle for this source-preserving refresh.

@MaggieAppleton

Copy link
Copy Markdown
Collaborator Author

Review: Looks good

Checked on a local fake-GitHub server (PR build, 1440×900, 1100, 900, 390×844) by sampling pane widths every animation frame.

What works

  • ⌘\ and ⌘. now animate with the same timing as a click. Sidebar: 22 frames to close (~235 ms) and 30 to open (~258 ms) for both key and click. Chat: 27 frames to close and 33 to open for both.
  • Pressing the shortcut repeatedly mid-animation reverses smoothly from the current width (sidebar ×5 at 60 ms, Chat ×4 at 70 ms). Both end in the correct state, with nothing stuck half-open.
  • Focus is right. ⌘. from the editor keeps the editor focused on close and moves it to the Chat H2 on open; the panel doesn't scroll sideways. Closing from inside Chat or the sidebar returns focus to "Show chat" or "Show sidebar".
  • Reduced motion settles instantly (one-frame change) for both keys and clicks. On first load and reload with the sidebar collapsed, nothing animates. Crossing the 1198 px inline/drawer boundary snaps the sidebar via modeChanged.
  • Phone/compact and the drawer (<1198 px) are unchanged. The shortcuts dialog still opens instantly by keyboard.
  • Hash renewal data flow: only the settle argument of useTransitionPresence changed in navigation-shell.tsx and workspace.tsx. Class expressions are unchanged, so the renewal is safe.
  • oxlint is clean on the changed files. The new e2e tests are a good regression check: they count transitionrun events and include a reduced-motion case.

Non-blocking notes

  1. apps/web/src/workspace.tsx:318: split Chat now always passes false, so a layout change from compact to split (e.g. widening 700→1440) slides Chat in, even right after keyboard input. The sidebar snaps in the same situation because of modeChanged. Before this PR that case only slid after pointer input, so it isn't a new kind of behaviour. For symmetry, consider a workspace-mode-changed guard like the sidebar's: mode === "split" ? workspaceModeChanged : immediately.
  2. Drawer mode (<1198 px): ⌘\ still opens the drawer instantly while a click slides it. That fits the stated reasoning (an overlay doesn't move space around), but the DESIGN.md rule says "Projects sidebar". Consider "inline Projects sidebar" so it's clear the drawer is excluded.
  3. Pre-existing, not from this PR: pressing Enter on "Show sidebar" leaves focus on <body>, because the button unmounts as the sidebar opens.
  4. PR body: the first two images use relative paths (./strip-keyboard-sidebar-*.png) and don't render. The attachments at the bottom duplicate them, so the broken links can be removed.

CI: all jobs green. format, lint, types, tests first failed on an unrelated timing-flaky test ("the grace period stops the agent on time while a heartbeat hangs in flight", 5061 ms vs <1500) and passed on rerun. e2e, container, chopin CLI and chopin CLI (Windows) all pass.

MaggieAppleton and others added 4 commits October 13, 2026 12:16
The sidebar and split Chat tracks settled at once whenever the last input was
a key, so Cmd-\ and Cmd-. snapped while a click slid. Their presence now
settles only on a layout change (sidebar) or reduced motion, matching pointer
timing. Menus, dialogs, document views, compact panes and count feedback keep
settling immediately for keyboard input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The workspace Chat panel and Projects sidebar class expressions are unchanged;
only their presence hooks' settle arguments changed. Their classes still come
from the finite presence states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Widening from compact to split no longer slides Chat in. Opening the inline
sidebar from Show sidebar moves focus to Hide sidebar instead of dropping it
to the page. DESIGN.md now names the inline sidebar, so the drawer is clearly
excluded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Class expressions in workspace.tsx and navigation-shell.tsx are unchanged; the
edits add a layout-change settle flag and a focus hand-off only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaggieAppleton
MaggieAppleton force-pushed the design-keyboard-toggle-motion branch from 59485b3 to 5040318 Compare October 13, 2026 11:17
@MaggieAppleton
MaggieAppleton merged commit 4f7635a into main Oct 13, 2026
3 of 4 checks passed
@MaggieAppleton
MaggieAppleton deleted the design-keyboard-toggle-motion branch October 13, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant