fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(ui): route Select keyboard and ARIA through floating-ui hooks - #9179

Merged
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard
Jul 16, 2026
Merged

fix(ui): route Select keyboard and ARIA through floating-ui hooks#9179
alexcarpenter merged 1 commit into
mainfrom
select-floating-ui-keyboard

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Stacked on top of #9176 (esc-closes-checkout-drawer). Reworks Select so its keyboard handling goes through floating-ui's useListNavigation / useInteractions instead of a hand-rolled onKeyDown: pressing ArrowUp/ArrowDown on a focused, closed Select now opens the listbox, and navigation is a single source of truth. Options are wired up with getItemProps (so hover follows the keyboard highlight), and the active option is announced via aria-activedescendant. ARIA is now correct per mode — the plain variant keeps its listbox semantics, and the searchable variant (e.g. the PhoneInput country picker) exposes a proper combobox: the input is role="combobox" with aria-controls, aria-autocomplete="list", and aria-activedescendant.

To test: focus a Select trigger and press Arrow keys to open it; navigate with the arrows and confirm the highlighted option is exposed via aria-activedescendant (on the listbox for a plain select, on the search input for the searchable PhoneInput picker).

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Accessibility
    • Improved Select keyboard navigation: pressing Arrow Up/Down on a focused trigger now opens the listbox, and Enter selects the active option.
    • Enhanced screen-reader support by announcing the active option via aria-activedescendant.
    • For searchable Select variants, updated combobox semantics (including correct ARIA attributes) so assistive technologies read the input/listbox state properly.
  • Tests
    • Added coverage for keyboard and ARIA behavior in both listbox and searchable combobox modes.

@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 16, 2026 5:00pm
swingsetReadyReadyPreview, CommentJul 16, 2026 5:00pm

Request Review

@changeset-bot

changeset-botBot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 311ca4a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/uiPatch
@clerk/chrome-extensionPatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 60c36b03-6914-43ef-9976-6dc720536acf

📥 Commits

Reviewing files that changed from the base of the PR and between e317f53 and 311ca4a.

📒 Files selected for processing (4)
  • .changeset/select-floating-ui-keyboard.md
  • packages/ui/bundlewatch.config.json
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/elements/tests/Select.test.tsx
  • packages/ui/src/elements/Select.tsx

📝 Walkthrough

Walkthrough

Select now uses floating-ui list navigation and interaction props for keyboard movement, option registration, trigger wiring, and active-descendant accessibility behavior across listbox and searchable combobox modes.

Changes

Select navigation and accessibility

Layer / File(s)Summary
Floating interaction wiring
packages/ui/src/elements/Select.tsx
Select exposes floating-ui interaction getters and navigation state, while the trigger and popover consume the updated interaction props.
Option rendering and ARIA semantics
packages/ui/src/elements/Select.tsx
Options register through useListItem and FloatingList; active indexes and mode-specific aria-activedescendant attributes are applied.
Keyboard and ARIA validation
packages/ui/src/elements/__tests__/Select.test.tsx, .changeset/select-floating-ui-keyboard.md
Tests cover arrow-key opening, keyboard selection, and listbox/combobox ARIA attributes; the changeset documents the update.
Bundle size support
packages/ui/bundlewatch.config.json
The common UI bundle size threshold is increased from 130KB to 132KB.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant SelectButton
participant Select
participant useListNavigation
participant SelectOptionList
participant SelectOption
SelectButton->>Select: ArrowUp or ArrowDown
Select->>useListNavigation: Update activeIndex
useListNavigation->>SelectOptionList: Expose active option
SelectOptionList->>SelectOption: Apply active-descendant and item props
SelectOptionList->>Select: Enter selects active option
Loading

Possibly related PRs

  • clerk/javascript#9176: Both changes refactor Select.tsx around floating-ui interaction props, including behavior relevant to Select components nested inside a Drawer.

Suggested labels:ui

Poem

I’m a bunny hopping through the list,
With arrow keys and options crisp.
Floating-ui shows where I am,
ARIA tells the screen reader’s jam.
Enter picks the carrot bright—
Keyboard paths now work just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly matches the main change: Select keyboard navigation and ARIA handling now use floating-ui hooks.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-newBot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9179

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9179

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9179

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9179

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9179

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9179

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9179

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9179

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9179

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9179

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9179

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9179

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9179

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9179

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9179

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9179

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9179

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9179

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9179

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9179

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9179

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9179

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9179

commit: 311ca4a

@coderabbitaicoderabbitaiBot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Select.tsx (1)

507-521: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass consumer handlers through getReferenceProps.
{...rest} can overwrite Floating UI’s composed onClick/onKeyDown, so consumer-supplied handlers break trigger toggling and keyboard opening. Move the remaining props into getReferenceProps instead of spreading them afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 507 - 521, Update the
trigger prop composition around getReferenceProps so consumer props from rest
are passed into getReferenceProps and its composed handlers take precedence.
Remove the trailing {...rest} spread, preserving aria attributes and styling
while ensuring consumer onClick/onKeyDown handlers do not overwrite Floating UI
trigger behavior.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
packages/ui/src/elements/Select.tsx (1)

201-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the option generic instead of erasing it with any.

SelectrenderOptionProps<any> discards O across option, renderOption, and handleSelect. Keep these fields typed as O and preserve the generic through React.memo.

As per coding guidelines, “No any types without justification in code review.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/elements/Select.tsx` around lines 201 - 214, Update
SelectrenderOptionProps and SelectRenderOption to preserve the option generic O:
type option and handleSelect with O, use RenderOption<O>, and remove the any
instantiation. Preserve O through the React.memo-wrapped component rather than
erasing it at the component boundary.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 507-521: Update the trigger prop composition around
getReferenceProps so consumer props from rest are passed into getReferenceProps
and its composed handlers take precedence. Remove the trailing {...rest} spread,
preserving aria attributes and styling while ensuring consumer onClick/onKeyDown
handlers do not overwrite Floating UI trigger behavior.
---
Nitpick comments:
In `@packages/ui/src/elements/Select.tsx`:
- Around line 201-214: Update SelectrenderOptionProps and SelectRenderOption to
preserve the option generic O: type option and handleSelect with O, use
RenderOption<O>, and remove the any instantiation. Preserve O through the
React.memo-wrapped component rather than erasing it at the component boundary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a183e7aa-821b-451e-b50b-e46045e34ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 2632d88 and cbaf0bc.

📒 Files selected for processing (3)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Select.test.tsx

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T17:02:29.149Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 311ca4a.

@maxyingermaxyinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

@alexcarpenter

Copy link
Copy Markdown
MemberAuthor

TIL about aria-activedescendant. very cool. less useEffect logic too. nice 😎

yeah, currently navigating the Phone Input options via keyboard while the input is focused doesn't announce which option you are on, aria-activedescendant helps connect it

BEFORE

before.mov

AFTER

after.mov

@alexcarpenter
alexcarpenter merged commit b0ed361 into mainJul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the select-floating-ui-keyboard branch July 16, 2026 18:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@alexcarpenter@maxyinger