Skip to content

♿️(frontend) fix sidebar resize handle for screen readers#2122

Open
Ovgodd wants to merge 4 commits intomainfrom
fix/separator-keyboard-interact
Open

♿️(frontend) fix sidebar resize handle for screen readers#2122
Ovgodd wants to merge 4 commits intomainfrom
fix/separator-keyboard-interact

Conversation

@Ovgodd
Copy link
Copy Markdown
Collaborator

@Ovgodd Ovgodd commented Mar 24, 2026

Purpose

Fix left sidebar keyboard resize with a screen reader on, and improve SR output plus step size.

Proposal

  • Replace the default separator role with slider so that, when the handle is focused, screen readers pass Left/Right to the app instead of using them for reading (known issue with separator in NVDA).
  • from aria-valuenow into aria-valuetext (width in px).
  • Use keyboardResizeBy={1} for smaller steps.

@Ovgodd Ovgodd requested a review from AntoLC March 24, 2026 15:00
@Ovgodd Ovgodd self-assigned this Mar 24, 2026
@Ovgodd Ovgodd force-pushed the fix/separator-keyboard-interact branch from aa9cacf to c22927b Compare March 24, 2026 15:01
@Ovgodd Ovgodd marked this pull request as ready for review March 24, 2026 15:03
@Ovgodd Ovgodd moved this from Backlog to In review in LaSuite Docs A11y Mar 24, 2026
@Ovgodd Ovgodd linked an issue Mar 24, 2026 that may be closed by this pull request
Expose the handle as a slider so arrow keys work with NVDA
@Ovgodd Ovgodd force-pushed the fix/separator-keyboard-interact branch from c22927b to b7d0ba7 Compare March 24, 2026 15:55
@github-actions
Copy link
Copy Markdown

Size Change: +214 B (+0.01%)

Total Size: 4.23 MB

Filename Size Change
apps/impress/out/_next/static/3574adc1/_buildManifest.js 0 B -905 B (removed) 🏆
apps/impress/out/_next/static/f3d1265a/_buildManifest.js 904 B +904 B (new file) 🆕

compressed-size-action

@AntoLC
Copy link
Copy Markdown
Collaborator

AntoLC commented Apr 21, 2026

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Walkthrough

Adds localized accessibility for the Impress left-panel resize handle. Introduces RESIZE_HANDLE_ID and getSidebarWidthLabel(current, min, max), uses useTranslation to build aria-label and aria-valuetext, sets id and aria-* attributes (aria-label, aria-orientation, aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext) on PanelResizeHandle, enables keyboard resizing via keyboardResizeBy={1} on PanelGroup, and adds a useEffect that sets the handle element’s role to "slider" when the panel is open. Also updates CHANGELOG.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • AntoLC
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing the sidebar resize handle for screen readers with an accessibility focus.
Description check ✅ Passed The description clearly outlines the purpose, proposed changes, and implementation details related to the accessibility improvements in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/separator-keyboard-interact

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Around line 11-12: The second changelog line ("♿️(frontend) fix sidebar resize
handle for screen readers `#2122`") is missing a list marker and is treated as a
continuation of the previous bullet; add a list marker (e.g., a leading "-" or
"•") before that line so it becomes its own list item under the "Changed"
section, ensuring the two entries ("💄(frontend) improve comments highlights
`#1961`" and "♿️(frontend) fix sidebar resize handle for screen readers `#2122`")
are separate bullets.

In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`:
- Around line 120-131: The current updateValueText function hardcodes the unit
"pixels" inside the translation key passed to t(...) (see updateValueText,
handle.setAttribute('aria-valuetext')), which prevents translators from
controlling unit formatting or order; change the call to t to pass the numeric
width as a variable only (e.g., t('Sidebar width', { widthPx })) and move the
unit text into the translation string itself (or use ICU/pluralization patterns)
so translators can provide the full localized phrase (including "px" or
localized unit) in the translation files.
- Line 154: Remove the unsupported keyboardResizeBy prop from the PanelGroup
usage in ResizableLeftPanel (the JSX element PanelGroup in
ResizableLeftPanel.tsx); update the JSX to omit keyboardResizeBy entirely and
rely on the library's Separator components for keyboard accessibility per
react-resizable-panels API, ensuring there are no leftover references to
keyboardResizeBy elsewhere in this component.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0326f0b8-0ef0-4330-9795-3d271f0727c6

📥 Commits

Reviewing files that changed from the base of the PR and between 0d09f76 and b7d0ba7.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx

Comment thread CHANGELOG.md Outdated
@Ovgodd Ovgodd force-pushed the fix/separator-keyboard-interact branch from a257fa6 to bfeeba5 Compare April 22, 2026 12:09
@Ovgodd Ovgodd requested a review from AntoLC April 22, 2026 12:09
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`:
- Around line 172-186: The aria-orientation on the slider is incorrect
(currently "vertical") and must be changed to "horizontal" to match the actual
keyboard interaction and role="slider" behavior; locate the element using
RESIZE_HANDLE_ID in ResizableLeftPanel (the same block that sets
aria-valuemin/aria-valuemax/aria-valuenow/aria-valuetext and references
getSidebarWidthLabel) and update aria-orientation="vertical" to
aria-orientation="horizontal" so it aligns with keyboardResizeBy={1} on
PanelGroup and W3C APG expectations for a horizontal width slider.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 41a277df-55a9-41f6-85c2-713da769dd62

📥 Commits

Reviewing files that changed from the base of the PR and between b7d0ba7 and bfeeba5.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`:
- Around line 178-185: The aria-valuetext currently nests t() around
getSidebarWidthLabel which hides full sentences from extraction; replace the
nested translation with a single static translation key per full sentence: map
the result of getSidebarWidthLabel(panelSizePercent, minPanelSizePercent,
maxPanelSizePercent) to a static key (e.g. 'leftPanel.sidebarWidth.narrow' /
'.medium' / '.wide') and call t(key) directly in the aria-valuetext (update
ResizableLeftPanel.tsx where aria-valuetext is set) so each full sentence is a
separate, extractable translation string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7781cf7e-98cf-4285-bcc1-427de244d7d3

📥 Commits

Reviewing files that changed from the base of the PR and between bfeeba5 and 0bdb668.

📒 Files selected for processing (1)
  • src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx (1)

178-191: 🧹 Nitpick | 🔵 Trivial

Simplify the aria-valuetext IIFE with a lookup map.

The inline IIFE with chained ifs is harder to scan than a plain object lookup keyed by the label. Functionally equivalent and keeps all three translation keys statically analyzable for extraction.

♻️ Proposed refactor
-          aria-valuetext={(() => {
-            const label = getSidebarWidthLabel(
-              panelSizePercent,
-              minPanelSizePercent,
-              maxPanelSizePercent,
-            );
-            if (label === 'narrow') {
-              return t('Sidebar width: narrow');
-            }
-            if (label === 'medium') {
-              return t('Sidebar width: medium');
-            }
-            return t('Sidebar width: wide');
-          })()}
+          aria-valuetext={
+            {
+              narrow: t('Sidebar width: narrow'),
+              medium: t('Sidebar width: medium'),
+              wide: t('Sidebar width: wide'),
+            }[
+              getSidebarWidthLabel(
+                panelSizePercent,
+                minPanelSizePercent,
+                maxPanelSizePercent,
+              )
+            ]
+          }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`
around lines 178 - 191, Replace the IIFE used for aria-valuetext with a simple
lookup map keyed by the result of getSidebarWidthLabel(panelSizePercent,
minPanelSizePercent, maxPanelSizePercent); compute const label =
getSidebarWidthLabel(...) then map { narrow: t('Sidebar width: narrow'), medium:
t('Sidebar width: medium'), wide: t('Sidebar width: wide') }[label] ||
t('Sidebar width: wide') and use that value for aria-valuetext — this keeps the
translation keys statically visible, preserves current behavior, and removes the
chained ifs in ResizableLeftPanel.tsx.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`:
- Around line 21-34: getSidebarWidthLabel can produce NaN when max === min
(division by zero) and then incorrectly falls through to 'wide'; update the
function (getSidebarWidthLabel) to guard the degenerate case by checking if (max
=== min || max - min === 0) and return a sensible label (e.g., return 'narrow'
or return current <= min ? 'narrow' : 'wide') before computing ratio, so no
division by zero occurs and screen-reader labels remain correct.

---

Duplicate comments:
In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`:
- Around line 178-191: Replace the IIFE used for aria-valuetext with a simple
lookup map keyed by the result of getSidebarWidthLabel(panelSizePercent,
minPanelSizePercent, maxPanelSizePercent); compute const label =
getSidebarWidthLabel(...) then map { narrow: t('Sidebar width: narrow'), medium:
t('Sidebar width: medium'), wide: t('Sidebar width: wide') }[label] ||
t('Sidebar width: wide') and use that value for aria-valuetext — this keeps the
translation keys statically visible, preserves current behavior, and removes the
chained ifs in ResizableLeftPanel.tsx.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ff32f95c-d995-4f64-a5c3-904caba2abc9

📥 Commits

Reviewing files that changed from the base of the PR and between 0bdb668 and ad2c384.

📒 Files selected for processing (1)
  • src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx

Comment on lines +21 to +34
const getSidebarWidthLabel = (
current: number,
min: number,
max: number,
): 'narrow' | 'medium' | 'wide' => {
const ratio = (current - min) / (max - min);
if (ratio < 1 / 3) {
return 'narrow';
}
if (ratio < 2 / 3) {
return 'medium';
}
return 'wide';
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard against max === min to avoid NaN in the width ratio.

When the viewport is narrow enough that Math.min(pxToPercent(maxPanelSizePx), 40) collapses to minPanelSizePercent (e.g., maxPanelSizePx=450, minPanelSizePx=300 at viewport width ≈ 750px: 40% = 300px = min), (current - min) / (max - min) becomes 0 / 0 = NaN. All ordering comparisons against NaN are false, so the function silently falls through to 'wide', which is misleading for SR users. Consider handling the degenerate range explicitly.

🛡️ Proposed guard
 const getSidebarWidthLabel = (
   current: number,
   min: number,
   max: number,
 ): 'narrow' | 'medium' | 'wide' => {
+  if (max <= min) {
+    return 'medium';
+  }
   const ratio = (current - min) / (max - min);
   if (ratio < 1 / 3) {
     return 'narrow';
   }
   if (ratio < 2 / 3) {
     return 'medium';
   }
   return 'wide';
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`
around lines 21 - 34, getSidebarWidthLabel can produce NaN when max === min
(division by zero) and then incorrectly falls through to 'wide'; update the
function (getSidebarWidthLabel) to guard the degenerate case by checking if (max
=== min || max - min === 0) and return a sensible label (e.g., return 'narrow'
or return current <= min ? 'narrow' : 'wide') before computing ratio, so no
division by zero occurs and screen-reader labels remain correct.

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

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

Resizable separator: inactive with keyboard

2 participants