Skip to content

Conversation

@KumJungMin
Copy link
Contributor

Closes #5551

📝 Description

When a ListboxItem includes interactive elements such as buttons inside
startContent or endContent, clicking those elements could cause focus
to unexpectedly move to the first ListboxItem, resulting in incorrect
focus styles being applied.

This PR updates the focus handling logic so that focus events originating
from the start/end content slots are no longer treated as option focus,
preventing unintended focus movement and styling.


⛳️ Current behavior (updates)

  • When startContent or endContent contains focusable elements (e.g. buttons)
  • Clicking those elements causes focus events to bubble up to the parent ListboxItem
  • useOption from react-aria interprets the bubbled focus as option focus, which:
    • Applies focus styles to the item, or
    • In certain cases, incorrectly moves focus to the first ListboxItem

This occurs even though the option itself was not directly interacted with,
but rather an internal interactive element was clicked.


🚀 New behavior

  • Ideally, focusable descendants should be configurable at the useOption level.
    However, since modifying react-aria internals is not feasible, this PR addresses
    the issue within HeroUI.

  • Focus events originating from elements within
    data-slot="startContent" or data-slot="endContent" are no longer treated
    as option focus.

  • Clicking non-interactive elements (e.g. span, p) inside start/end content
    continues to apply option focus as before.

  • Clicking interactive elements such as buttons or links no longer triggers
    unintended focus styles or incorrect focus movement.


As a result, ListboxItem focus behavior now correctly reflects the rule:
only direct interactions with the option itself should apply option focus.

2025-12-27.4.45.03.mov
  • Clicking interactive elements does not trigger option focus or apply focus styles.
  • Clicking non-interactive elements correctly applies focus styles to the corresponding list item.

💣 Is this a breaking change (Yes/No):

No

  • Existing option click behavior and keyboard navigation (Arrow / Tab) remain unchanged.
  • When start/end content does not include interactive elements, behavior is identical
    to the previous implementation.
  • The change only restricts unintended focus bubbling in edge cases.

📝 Additional Information

  • Instead of post-processing focus state, this implementation prevents
    focus propagation at the onFocusCapture phase.
  • Since react-aria’s internal state (useOption, SelectionManager) cannot be
    modified directly, HeroUI conditionally filters focus events at the component level.
  • If react-aria introduces more granular control over focusable descendants
    in useOption in the future, this implementation can be revisited and improved.

non-interactive slot content, and prevented focus leakage from interactive
start/end content such as buttons.
@changeset-bot
Copy link

changeset-bot bot commented Dec 27, 2025

🦋 Changeset detected

Latest commit: 65cde6f

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

This PR includes changesets to release 4 packages
Name Type
@heroui/listbox Patch
@heroui/autocomplete Patch
@heroui/select Patch
@heroui/react Patch

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

@vercel
Copy link

vercel bot commented Dec 27, 2025

@KumJungMin is attempting to deploy a commit to the HeroUI Inc Team on Vercel.

A member of the Team first needs to authorize it.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 27, 2025

Open in StackBlitz

@heroui/accordion

npm i https://pkg.pr.new/@heroui/accordion@6060

@heroui/alert

npm i https://pkg.pr.new/@heroui/alert@6060

@heroui/autocomplete

npm i https://pkg.pr.new/@heroui/autocomplete@6060

@heroui/avatar

npm i https://pkg.pr.new/@heroui/avatar@6060

@heroui/badge

npm i https://pkg.pr.new/@heroui/badge@6060

@heroui/breadcrumbs

npm i https://pkg.pr.new/@heroui/breadcrumbs@6060

@heroui/button

npm i https://pkg.pr.new/@heroui/button@6060

@heroui/calendar

npm i https://pkg.pr.new/@heroui/calendar@6060

@heroui/card

npm i https://pkg.pr.new/@heroui/card@6060

@heroui/checkbox

npm i https://pkg.pr.new/@heroui/checkbox@6060

@heroui/chip

npm i https://pkg.pr.new/@heroui/chip@6060

@heroui/code

npm i https://pkg.pr.new/@heroui/code@6060

@heroui/date-input

npm i https://pkg.pr.new/@heroui/date-input@6060

@heroui/date-picker

npm i https://pkg.pr.new/@heroui/date-picker@6060

@heroui/divider

npm i https://pkg.pr.new/@heroui/divider@6060

@heroui/drawer

npm i https://pkg.pr.new/@heroui/drawer@6060

@heroui/dropdown

npm i https://pkg.pr.new/@heroui/dropdown@6060

@heroui/form

npm i https://pkg.pr.new/@heroui/form@6060

@heroui/image

npm i https://pkg.pr.new/@heroui/image@6060

@heroui/input

npm i https://pkg.pr.new/@heroui/input@6060

@heroui/input-otp

npm i https://pkg.pr.new/@heroui/input-otp@6060

@heroui/kbd

npm i https://pkg.pr.new/@heroui/kbd@6060

@heroui/link

npm i https://pkg.pr.new/@heroui/link@6060

@heroui/listbox

npm i https://pkg.pr.new/@heroui/listbox@6060

@heroui/menu

npm i https://pkg.pr.new/@heroui/menu@6060

@heroui/modal

npm i https://pkg.pr.new/@heroui/modal@6060

@heroui/navbar

npm i https://pkg.pr.new/@heroui/navbar@6060

@heroui/number-input

npm i https://pkg.pr.new/@heroui/number-input@6060

@heroui/pagination

npm i https://pkg.pr.new/@heroui/pagination@6060

@heroui/popover

npm i https://pkg.pr.new/@heroui/popover@6060

@heroui/progress

npm i https://pkg.pr.new/@heroui/progress@6060

@heroui/radio

npm i https://pkg.pr.new/@heroui/radio@6060

@heroui/ripple

npm i https://pkg.pr.new/@heroui/ripple@6060

@heroui/scroll-shadow

npm i https://pkg.pr.new/@heroui/scroll-shadow@6060

@heroui/select

npm i https://pkg.pr.new/@heroui/select@6060

@heroui/skeleton

npm i https://pkg.pr.new/@heroui/skeleton@6060

@heroui/slider

npm i https://pkg.pr.new/@heroui/slider@6060

@heroui/snippet

npm i https://pkg.pr.new/@heroui/snippet@6060

@heroui/spacer

npm i https://pkg.pr.new/@heroui/spacer@6060

@heroui/spinner

npm i https://pkg.pr.new/@heroui/spinner@6060

@heroui/switch

npm i https://pkg.pr.new/@heroui/switch@6060

@heroui/table

npm i https://pkg.pr.new/@heroui/table@6060

@heroui/tabs

npm i https://pkg.pr.new/@heroui/tabs@6060

@heroui/toast

npm i https://pkg.pr.new/@heroui/toast@6060

@heroui/tooltip

npm i https://pkg.pr.new/@heroui/tooltip@6060

@heroui/user

npm i https://pkg.pr.new/@heroui/user@6060

@heroui/react

npm i https://pkg.pr.new/@heroui/react@6060

@heroui/system

npm i https://pkg.pr.new/@heroui/system@6060

@heroui/system-rsc

npm i https://pkg.pr.new/@heroui/system-rsc@6060

@heroui/theme

npm i https://pkg.pr.new/@heroui/theme@6060

@heroui/use-aria-accordion

npm i https://pkg.pr.new/@heroui/use-aria-accordion@6060

@heroui/use-aria-accordion-item

npm i https://pkg.pr.new/@heroui/use-aria-accordion-item@6060

@heroui/use-aria-button

npm i https://pkg.pr.new/@heroui/use-aria-button@6060

@heroui/use-aria-link

npm i https://pkg.pr.new/@heroui/use-aria-link@6060

@heroui/use-aria-modal-overlay

npm i https://pkg.pr.new/@heroui/use-aria-modal-overlay@6060

@heroui/use-aria-multiselect

npm i https://pkg.pr.new/@heroui/use-aria-multiselect@6060

@heroui/use-aria-overlay

npm i https://pkg.pr.new/@heroui/use-aria-overlay@6060

@heroui/use-callback-ref

npm i https://pkg.pr.new/@heroui/use-callback-ref@6060

@heroui/use-clipboard

npm i https://pkg.pr.new/@heroui/use-clipboard@6060

@heroui/use-data-scroll-overflow

npm i https://pkg.pr.new/@heroui/use-data-scroll-overflow@6060

@heroui/use-disclosure

npm i https://pkg.pr.new/@heroui/use-disclosure@6060

@heroui/use-draggable

npm i https://pkg.pr.new/@heroui/use-draggable@6060

@heroui/use-form-reset

npm i https://pkg.pr.new/@heroui/use-form-reset@6060

@heroui/use-image

npm i https://pkg.pr.new/@heroui/use-image@6060

@heroui/use-infinite-scroll

npm i https://pkg.pr.new/@heroui/use-infinite-scroll@6060

@heroui/use-intersection-observer

npm i https://pkg.pr.new/@heroui/use-intersection-observer@6060

@heroui/use-is-mobile

npm i https://pkg.pr.new/@heroui/use-is-mobile@6060

@heroui/use-is-mounted

npm i https://pkg.pr.new/@heroui/use-is-mounted@6060

@heroui/use-measure

npm i https://pkg.pr.new/@heroui/use-measure@6060

@heroui/use-pagination

npm i https://pkg.pr.new/@heroui/use-pagination@6060

@heroui/use-real-shape

npm i https://pkg.pr.new/@heroui/use-real-shape@6060

@heroui/use-ref-state

npm i https://pkg.pr.new/@heroui/use-ref-state@6060

@heroui/use-resize

npm i https://pkg.pr.new/@heroui/use-resize@6060

@heroui/use-safe-layout-effect

npm i https://pkg.pr.new/@heroui/use-safe-layout-effect@6060

@heroui/use-scroll-position

npm i https://pkg.pr.new/@heroui/use-scroll-position@6060

@heroui/use-ssr

npm i https://pkg.pr.new/@heroui/use-ssr@6060

@heroui/use-theme

npm i https://pkg.pr.new/@heroui/use-theme@6060

@heroui/use-update-effect

npm i https://pkg.pr.new/@heroui/use-update-effect@6060

@heroui/use-viewport-size

npm i https://pkg.pr.new/@heroui/use-viewport-size@6060

@heroui/aria-utils

npm i https://pkg.pr.new/@heroui/aria-utils@6060

@heroui/dom-animation

npm i https://pkg.pr.new/@heroui/dom-animation@6060

@heroui/framer-utils

npm i https://pkg.pr.new/@heroui/framer-utils@6060

@heroui/react-rsc-utils

npm i https://pkg.pr.new/@heroui/react-rsc-utils@6060

@heroui/react-utils

npm i https://pkg.pr.new/@heroui/react-utils@6060

@heroui/shared-icons

npm i https://pkg.pr.new/@heroui/shared-icons@6060

@heroui/shared-utils

npm i https://pkg.pr.new/@heroui/shared-utils@6060

@heroui/stories-utils

npm i https://pkg.pr.new/@heroui/stories-utils@6060

@heroui/test-utils

npm i https://pkg.pr.new/@heroui/test-utils@6060

commit: 65cde6f

@vercel
Copy link

vercel bot commented Dec 27, 2025

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

Project Deployment Review Updated (UTC)
heroui Ready Ready Preview, Comment Dec 27, 2025 10:28am
heroui-sb Ready Ready Preview, Comment Dec 27, 2025 10:28am

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] - clicking endContent of the last listbox item selects the first

2 participants