Skip to content

feat(web): make the right-panel resize handle keyboard operable - #4581

Closed
andyon2 wants to merge 1 commit into
pingdotgg:mainfrom
andyon2:resize-handle-keyboard
Closed

andyon2 wants to merge 1 commit into
pingdotgg:mainfrom
andyon2:resize-handle-keyboard

Conversation

@andyon2

@andyon2 andyon2 commented Jul 26, 2026 •

Copy link
Copy Markdown

What changed

RightPanelResizeHandle was pointer-only: role="separator" with pointer handlers, no tabIndex, no name, no value. Panel width was the one part of the layout a keyboard user could not reach.

  • Arrow keys nudge the handle by 8px, Shift makes the step 48px, Home/End park it at either extreme.
  • The handle is focusable and announces itself as a window splitter — aria-label, aria-valuenow, aria-valuemin, aria-valuemax — instead of an unnamed separator.
  • The 1px line lights up on keyboard focus too (group-focus-visible:bg-primary), so focus never sits on something invisible.

Keys are spatial: ArrowLeft always moves the handle left, which widens or narrows depending on which edge the panel is anchored to. That rule lives in a pure function, nextWidthForKey, so the direction and step logic is testable without a DOM.

The new props on RightPanelResizeHandle are optional, so existing call sites keep compiling; PreviewPanelShell passes them.

Why it should exist

useResizableWidth already owns clamping and persistence — the keyboard path is ~40 lines on top of machinery that exists. Without it the panel has a visible, draggable affordance that simply does not exist for anyone not using a pointer.

Before / after

The change is only visible while the handle has focus, because before this patch the handle could not receive focus at all.

before (handle blurred — and unreachable) after (handle focused via Tab)

Interaction, walked through in a real browser on this branch: Tab reaches the handle right after the composer's send button; it reports Resize preview panel, valuenow 540, min 360, max 714. Shift+ArrowLeft ×3 widens the panel 540 → 684, End parks it at 360, ArrowLeft ×8 brings it to 424. Focus ring and line colour verified in the dark theme.

Test

apps/web/src/hooks/useResizableWidth.test.ts covers nextWidthForKey: both edges, both step sizes, Home/End, and the keys it must ignore.

vp test run --project unit apps/web/src/hooks/useResizableWidth.test.ts → 6 passed.

Notes

Read CONTRIBUTING first — happy for this to be closed or deferred. It is small, self-contained, and does not change any existing behaviour for pointer users.


Note

Low Risk
Localized UI/a11y change to panel resizing; pointer behavior unchanged and width still clamped and persisted through existing hook logic.

Overview
Makes the preview panel resize handle keyboard-accessible and properly announced to assistive tech, without changing pointer drag behavior.

useResizableWidth now exposes onKeyDown and a shared commit path so arrow keys (8px), Shift+arrows (48px), and Home/End resize the panel with the same clamping and localStorage persistence as drag-end. Direction is edge-aware via exported pure helper nextWidthForKey, covered by new unit tests.

RightPanelResizeHandle becomes focusable (tabIndex={0}) with aria-label / value min-max, and shows focus on the 1px indicator. PreviewPanelShell passes width bounds and the label "Resize preview panel".

Reviewed by Cursor Bugbot for commit 7ed9174. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Make the right-panel resize handle keyboard operable

  • The RightPanelResizeHandle component gains tabIndex=0, ARIA attributes (aria-label, aria-valuenow, aria-valuemin, aria-valuemax), and a focus-visible highlight on the indicator line.
  • useResizableWidth gains an onKeyDown handler: Arrow keys move the panel width by a small step, Shift+Arrow moves by a coarse step, and Home/End jump to the min/max bounds. The new width is clamped and immediately persisted to localStorage.
  • A pure helper nextWidthForKey is exported from useResizableWidth.ts to compute the next width from key input, aware of which edge the handle sits on.
  • PreviewPanelShell now passes label, minWidth, maxWidth, and current width to the handle to populate the ARIA value semantics correctly.
📊 Macroscope summarized 7ed9174. 3 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

The handle was pointer-only, so panel width was the one part of the layout a
keyboard user could not reach.

- Arrow keys nudge it by 8px, Shift makes the step 48px, Home/End park it at
  either extreme. Keys are spatial: ArrowLeft always moves the handle left,
  which widens or narrows depending on which edge it is anchored to.
- The handle is focusable and announces itself as a window splitter
  (aria-label / valuenow / valuemin / valuemax) instead of a bare separator.
- The 1px line lights up on keyboard focus too — otherwise focus would sit on
  something invisible.

The direction and step rules are a pure function (`nextWidthForKey`) with unit
tests; the new props are optional so existing call sites keep compiling.

Co-Authored-By: Claude Opus 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 36ca0503-abea-4364-b27c-cca6b939394c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched Author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 26, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7ed9174. Configure here.

case "Home":
return widerToTheLeft ? maxWidth : minWidth;
case "End":
return widerToTheLeft ? minWidth : maxWidth;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Home/End invert announced range

Medium Severity

The Home and End keys in useResizableWidth set the panel width to maxWidth and minWidth respectively when edge is "left". This behavior is inverted from typical expectations for range controls and conflicts with the aria-valuemin/aria-valuemax values, making it confusing for assistive technologies.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7ed9174. Configure here.

if (next === null) return;
event.preventDefault();
commit(next);
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keyboard resize fights active drag

Low Severity

onKeyDown always commits even when dragStateRef is active. A key press mid-drag writes localStorage immediately, while pointer move still derives width from the original startWidth, and onPointerCancel reverts the UI to startWidth without undoing that persist, so displayed and stored width can diverge.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7ed9174. Configure here.

@macroscopeapp

macroscopeapp Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Unresolved review comment identifies a medium-severity accessibility issue where Home/End key behavior inverts relative to the announced ARIA value range, potentially confusing assistive technology users. This bug in an accessibility-focused PR warrants human review.

You can customize Macroscope's approvability policy. Learn more.

@andyon2

andyon2 commented Jul 26, 2026

Copy link
Copy Markdown
Author

Closing this — opened in error on my side; these changes were meant to stay in my own fork, not to go upstream. Sorry for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant