Dropdown: reveal the current value on open + proper listbox/menu item roles#396
Open
acusti wants to merge 6 commits into
Open
Dropdown: reveal the current value on open + proper listbox/menu item roles#396acusti wants to merge 6 commits into
acusti wants to merge 6 commits into
Conversation
acusti
force-pushed
the
dropdown-highlight-selected-value
branch
from
July 16, 2026 15:45
32457be to
c303417
Compare
acusti
force-pushed
the
dropdown-value-label-pair
branch
from
July 16, 2026 17:46
a59b8d3 to
60ef117
Compare
acusti
force-pushed
the
dropdown-highlight-selected-value
branch
2 times, most recently
from
July 16, 2026 18:13
027bd7a to
a58042b
Compare
A controlled dropdown opened at the top of its list regardless of the current value. On open, mark the item whose data-ukt-value matches props.value aria-selected, make it the active item so keyboard navigation starts there, and scroll it into view — so a long list opens showing, and scrolled to, the current selection. setActiveItem's event becomes optional (it only builds the onActiveItem payload, which is skipped when there's no event) so an item can be activated programmatically on open. Adds a themeable --uktdd-body-bg-color-selected tint for the persistent selection marker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dropdown set the container role (listbox/menu/dialog) but left its items as plain <li>s, so a searchable listbox exposed no options and a menu no menuitems. On open, fill in the roles the consumer hasn't set: option for a listbox, menuitem for a menu (and for any submenu, itself a menu), with the <ul>/<ol> wrappers marked role="presentation" so the container owns its items directly rather than through an intervening list. Skip items that carry their own semantics — a natively interactive item (button, link, input) or one with a consumer-set role keeps it. aria-selected on the current option is already set by the reveal-on-open behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The open-time annotation marks the item matching props.value, but with keepOpenOnSubmit the body outlives the submit, so selecting another item left the tint (and aria-selected) on the previous selection until the next open. Move it to the submitted option in handleSubmitItem; a closing body unmounts, so reopening re-derives it from props.value as before. Also document that the open-time annotations (roles, aria-selected, reveal) run once per open, so items rendered into an already-open body aren't annotated until the next open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HcJxNoQKN6Yx5hmYpwJHMJ
The ARIA attributes section covered the trigger, body, submenu, and menubar annotations but not the item-level ones added alongside them: option/menuitem role fill-in, the presentation role on list wrappers, aria-selected on the current listbox option, and the reveal-on-open behavior with its --uktdd-body-bg-color-selected custom property. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HcJxNoQKN6Yx5hmYpwJHMJ
acusti
force-pushed
the
dropdown-highlight-selected-value
branch
from
July 16, 2026 22:37
a58042b to
c48606a
Compare
The reveal changeset claimed the matching item is marked aria-selected, but that only happens in a listbox (aria-selected isn't valid on a menuitem) — say so, matching the README and TSDoc. Also state at the reveal site that the top-level-only lookup is deliberate: search/typeahead also match per level, and no native menu opens with submenus pre-expanded, so a value matching only a submenu item is a graceful no-op. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HcJxNoQKN6Yx5hmYpwJHMJ
Three fixes to the open-time annotations, all following the existing your-values-win rule: - A submenu parent item in a listbox no longer gets role="option": its aria-haspopup/aria-expanded (from annotateParentItems) are invalid on an option, and no valid role exists for a disclosure parent inside a listbox, so its role is left to the consumer. - Consumer-authored aria-selected anywhere in the body means the consumer manages selection ARIA: the reveal-on-open marking and the keepOpenOnSubmit move both stand down, so a single-select listbox never shows two selected options. - The keepOpenOnSubmit aria-selected move is scoped to the popup body, so it can't strip aria-selected from consumer content outside it (e.g. a custom trigger). The body ref callback now latches per body element (a WeakSet): an inline ref re-attaches on every re-render while open, and the per-open work must run exactly once — showPopover() throws on an already-shown popover, and the ownership check would otherwise read the reveal's own aria-selected annotation back as consumer-authored on the second run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HcJxNoQKN6Yx5hmYpwJHMJ
acusti
force-pushed
the
dropdown-highlight-selected-value
branch
from
July 17, 2026 06:28
a7b1711 to
6238202
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related improvements for controlled dropdowns.
Reveal the current value on open
On open, the item whose
data-ukt-valuematchesprops.valueis made the active item (so keyboard navigation starts from the current selection) and scrolled into view — a long list opens at the current value instead of the top. In a listbox it's also markedaria-selected="true"(with a themeable--uktdd-body-bg-color-selectedtint). The lookup is deliberately top-level only: search/typeahead also match per level, and no native menu opens with submenus pre-expanded, so a value matching only a submenu item is a graceful no-op.Proper listbox / menu item roles
The dropdown set the container role (
listbox/menu/dialog) but left its items as plain<li>s. On open it now fills in the roles the consumer hasn't set:role="option"for a searchable (listbox) dropdown,role="menuitem"for a menu — and alwaysmenuiteminside a submenu (itself a menu).<ul>/<ol>wrappers getrole="presentation"so the listbox/menu owns its items directly rather than through an intervening list.aria-selected, and withkeepOpenOnSubmitit moves to the newly submitted option (scoped to the popup body).option).aria-selectedanywhere in the body (true or false) means the consumer manages selection ARIA — the reveal marking and thekeepOpenOnSubmitmove both stand down, so a single-select listbox never shows two selected options.Details
annotateParentItems(which already gives submenusrole="menu"). The per-open work is latched per body element (a WeakSet), since an inline ref callback re-attaches on every re-render while open and must not re-runshowPopover()or read its ownaria-selectedannotation back as consumer-authored.setActiveItem'seventbecomes optional — it's only used to build theonActiveItempayload, which is skipped without an event — so an item can be activated programmatically on open without a synthetic event or a spurious callback.aria-selectedis listbox-only (it isn't valid on amenuitem); menus get the visual active highlight but noaria-selected.Testing
keepOpenOnSubmit, scoped to the popup body; consumer-authored aria-selected wins). Full suite: 251 passing.minorchangesets.OpensToCurrentValueStorybook story — a long list with the value far down, so opening scrolls straight to it.build/test/tsc/lint/formatall green.Follow-up (not here)
aria-activedescendanton the searchable input (announcing the active option as you arrow through it) is the natural next a11y step for the combobox interaction — left out to keep this focused on roles + reveal.🤖 Generated with Claude Code