Skip to content

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

Open
Ovgodd wants to merge 1 commit intomainfrom
fix/separator-keyboard-interact
Open

♿️(frontend) fix sidebar resize handle for screen readers#2122
Ovgodd wants to merge 1 commit 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
@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

github-actions Bot commented Mar 24, 2026

Size Change: +2 B (0%)

Total Size: 4.27 MB

📦 View Changed
Filename Size Change
apps/impress/out/_next/static/77f01a61/_buildManifest.js 637 B +637 B (new file) 🆕
apps/impress/out/_next/static/d3c68130/_buildManifest.js 0 B -635 B (removed) 🏆

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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds localized accessibility for the Impress left-panel resize control. Introduces a stable RESIZE_HANDLE_ID, a width-bucket helper to produce aria-valuetext, and uses useTranslation() to build aria-label/aria-valuetext. Sets id and multiple 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 an effect that sets the handle’s DOM role to "slider" when open. Also updates CHANGELOG.md.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing sidebar resize handle accessibility for screen readers, which matches the core objective of the PR.
Description check ✅ Passed The description is directly related to the changeset, detailing the purpose and proposed changes for improving sidebar keyboard resize with screen readers active.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/separator-keyboard-interact

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

Copy link
Copy Markdown
Collaborator

@AntoLC AntoLC left a comment

Choose a reason for hiding this comment

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

Like pointed the rabbit, you should probably managed this case max === min.

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.

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

181-194: 🧹 Nitpick | 🔵 Trivial

Simplify the inline IIFE for aria-valuetext.

The three-branch IIFE adds visual noise inside JSX. Since getSidebarWidthLabel already returns a discriminated union ('narrow' | 'medium' | 'wide'), a simple object lookup keeps each t(...) call statically extractable while removing the IIFE wrapper. Alternatively, fold the t call into a small helper as suggested previously.

♻️ Proposed lookup
-          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 181 - 194, Replace the inline IIFE used for aria-valuetext with a
simple lookup using the discriminated union returned by getSidebarWidthLabel:
call getSidebarWidthLabel(panelSizePercent, minPanelSizePercent,
maxPanelSizePercent) once, then map its 'narrow'|'medium'|'wide' result to the
corresponding t('Sidebar width: ...') string (or use a tiny helper that wraps t)
and pass that value to aria-valuetext; update the ResizableLeftPanel component
where aria-valuetext is set to remove the IIFE and use the lookup/helper
instead.

21-37: 🧹 Nitpick | 🔵 Trivial

Move the max <= min guard before computing ratio.

The guard executes after ratio is already computed, meaning the function still divides by zero (producing NaN) on every degenerate-range call before bailing out. Functionally the early return saves the day, but readers naturally read the function top-to-bottom and the current order makes the guard look like dead code or an after-thought. Reordering also future-proofs against later edits that move logic between the two.

♻️ Proposed reorder
 const getSidebarWidthLabel = (
   current: number,
   min: number,
   max: number,
 ): 'narrow' | 'medium' | 'wide' => {
-  const ratio = (current - min) / (max - min);
   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 - 37, The function getSidebarWidthLabel computes ratio before
checking the degenerate range, which can produce a divide-by-zero NaN; reorder
the guard so the max <= min check occurs before calculating ratio (i.e., return
'medium' immediately if max <= min), then compute ratio = (current - min) / (max
- min) and proceed with the existing ratio checks for 'narrow'|'medium'|'wide'.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@src/frontend/apps/impress/src/features/left-panel/components/ResizableLeftPanel.tsx`:
- Around line 181-194: Replace the inline IIFE used for aria-valuetext with a
simple lookup using the discriminated union returned by getSidebarWidthLabel:
call getSidebarWidthLabel(panelSizePercent, minPanelSizePercent,
maxPanelSizePercent) once, then map its 'narrow'|'medium'|'wide' result to the
corresponding t('Sidebar width: ...') string (or use a tiny helper that wraps t)
and pass that value to aria-valuetext; update the ResizableLeftPanel component
where aria-valuetext is set to remove the IIFE and use the lookup/helper
instead.
- Around line 21-37: The function getSidebarWidthLabel computes ratio before
checking the degenerate range, which can produce a divide-by-zero NaN; reorder
the guard so the max <= min check occurs before calculating ratio (i.e., return
'medium' immediately if max <= min), then compute ratio = (current - min) / (max
- min) and proceed with the existing ratio checks for 'narrow'|'medium'|'wide'.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 800f5388-0105-4098-bc66-9caf1fe30bda

📥 Commits

Reviewing files that changed from the base of the PR and between ad2c384 and 4a0f3d4.

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

@Ovgodd Ovgodd force-pushed the fix/separator-keyboard-interact branch from a2eeb31 to 51e5853 Compare April 27, 2026 13:32
Expose the handle as a slider so arrow keys work with NVDA
@Ovgodd Ovgodd force-pushed the fix/separator-keyboard-interact branch from 51e5853 to b196700 Compare April 27, 2026 13:32
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