rc-listbox
Listbox that keeps option DOM in light DOM for aria-activedescendant navigation, following the WAI-ARIA Listbox pattern.
rc-listbox is primarily an infrastructure component that can be used
directly when an application or component controls option data and selection state.
- Package
@rcarls/rc-listbox- Element
<rc-listbox>- Native dependency
- <ul>/<li> rendered into the host's light DOM
- State model
- Controlled or uncontrolled selection
- Main events
rc-listbox-change
Installation
- npm
- Yarn
- pnpm
npm install @rcarls/rc-webcomponents@next
yarn add @rcarls/rc-webcomponents@next
pnpm add @rcarls/rc-webcomponents@next
import '@rcarls/rc-webcomponents/rc-listbox/define';
Usage
rc-listbox is usually an infrastructure component inside composite controls. Use
it directly when application code owns the option data and selection state.
const listbox = document.querySelector('rc-listbox');
listbox.options = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry', disabled: true },
];
listbox.setSelectedValues(['apple']);
Action options
Most rows should be selectable options. Rows that perform commands, such as
create, clear, or other flows, can use kind: 'action'. The change event is a
discriminated union, so event.detail.reason narrows the detail type.
listbox.options = [
{ value: 'apple', label: 'Apple' },
{ kind: 'action', action: 'clear', value: 'clear', label: 'Clear selection' },
];
listbox.addEventListener('rc-listbox-change', (event) => {
if (event.detail.reason === 'action') {
console.log(event.detail.action);
return;
}
console.log(event.detail.option.value, event.detail.selected);
});
Progressive enhancement with pre-rendered markup
Consumers may supply a <ul> with <li> children directly in the component's
slot. On first connect, the component reads those elements to bootstrap its
internal options. The pre-rendered list is visible and keyboard-navigable before
JavaScript runs.
<rc-listbox aria-label="Fruit">
<ul>
<li value="apple">Apple</li>
<li value="banana">Banana</li>
<li value="cherry" disabled>Cherry</li>
</ul>
</rc-listbox>
Once the options property is set at any point, it becomes the authoritative
source and the pre-rendered markup is replaced.
Live demo
Accessibility
The host has role="listbox", with aria-multiselectable="true" when multiple is set. Options
render with role="option" and aria-selected; disabled options have aria-disabled="true" and
are skipped. Focus stays on the listbox, which tracks the active option with
aria-activedescendant. Name the listbox with aria-label or aria-labelledby.
| Key | Action |
|---|---|
| Down Arrow | Moves to the next option. A single-select listbox also selects it. |
| Up Arrow | Moves to the previous option. A single-select listbox also selects it. |
| Home, End | Moves to the first or last option. A single-select listbox also selects it. |
| Enter, Space | Selects the active option, toggles it in a multiple listbox, or runs an action option. |
Key presses with Alt, Ctrl, or Meta pass through to the surrounding component, such as
rc-transfer-list. See the WAI-ARIA Listbox pattern.
Theming
The default mode shows the component without a package theme applied. Use the shared preview controls on this page to compare inherited, light, and dark color schemes, or to apply the optional Material theme inside the demo frame.
Base styles (layout, system-color selection, disabled state) are injected once
per root node with a style[data-rc-light-dom-base="rc-listbox"] element in
an rc-base cascade layer.
Localization
Built-in text follows the element's own lang, else <html lang>. Override it with
messages, or register it for every instance under rc-listbox: create. See
Localization.
API
Properties
| Property | Markup | Type | Default | Description |
|---|---|---|---|---|
multiple | multiple | boolean | false | Allow multiple selection. Reflected as `aria-multiselectable` on the host. |
checkmark | checkmark | boolean | false | Render a checkmark indicator inside each option element. Hidden by default; enable for combobox / select patterns where the consumer's CSS shows it conditionally via `[aria-selected='true']`. |
filterStrategy | filter-strategy | FilterStrategy | 'contains' | How option labels are matched against the active filter text. Defaults to `'contains'` (substring). Set to `'prefix'` for starts-with matching, or pass a custom predicate for full control. Function values are JS-only; string values may be set via the `filter-strategy` attribute. |
messages | JS property only | Partial<RCListboxMessages> | undefined | Not specified | Built-in text overrides; unset keys fall back to registered text, then English. |
allOptions | JS property only | readonly ListboxOption[] | Not specified | All options regardless of filter state. |
filteredOptions | JS property only | readonly ListboxOption[] | Not specified | Options currently passing the active filter. |
options | JS property only | ListboxOption[] | Not specified | Replace the full options list. |
selectedValues | JS property only | string[] | Not specified | Current selection as a consistently-array-shaped read-only view. |
value | JS property only | string | string[] | Not specified | Current selection. Host writes update silently. |
defaultValue | JS property only | string | string[] | undefined | Not specified | Initial uncontrolled selection. |
navigableItems | JS property only | Element[] | Not specified | Ordered list of currently navigable option elements (visible and not disabled). Feed this to `ActiveDescendantController.items` in the parent component. Includes the create option element when one is set. |
Methods
| Method | Description |
|---|---|
appendOption(option: ListboxOption) | Append a single option without replacing the list. |
setSelectedValues(values: string[]) | Replace the selection set without firing `rc-listbox-change`. |
toggleOption(value: string) | Toggle the selected state of the option with `value`. In single-select mode, toggling a selected item deselects it (and selects the new item). Fires `rc-listbox-change`. |
clearSelection() | Clears all selected values without firing `rc-listbox-change`. |
filterOptions(text: string) | Filter visible options to those whose label matches `text` (case-insensitive). |
clearFilter() | Removes any active filter, making all options visible. |
setCreateOption(label: string | null) | Show or hide the "Create" option at the end of the list. Pass `null` to hide it, or a non-empty string to show `Create "{label}"` in the listbox's locale. Fires `rc-listbox-change` with `reason: 'action'` and `action: 'create'` when activated. |
Events
| Event | Detail type | Description |
|---|---|---|
rc-listbox-change | CustomEvent | Fired when an option is activated (clicked or Enter/Space) |
Slots
| Name | Description |
|---|---|
— | Accepts pre-rendered `<ul>` with `<li>` children for progressive enhancement. |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--rc-listbox-option-gap | 0.25rem | Gap between the checkmark and option label. |
--rc-listbox-option-min-block-size | 0px | Minimum block size (height) of each option row. |
--rc-listbox-option-padding-block | 2px | Block-axis padding of each option row. |
--rc-listbox-option-padding-inline | 4px | Inline-axis padding of each option row. |
--rc-listbox-option-transition | Not specified | CSS transition applied to each option row. |
--rc-listbox-hover-bg | color-mix(in srgb, Highlight 8%, transparent) | Background of a hovered option. |
--rc-listbox-hover-color | inherit | Text color of a hovered option. |
--rc-listbox-active-bg | color-mix(in srgb, Highlight 8%, transparent) | Background of the keyboard-active option. |
--rc-listbox-active-color | var(--rc-listbox-hover-color, inherit) | Text color of the keyboard-active option. |
--rc-listbox-selected-bg | Highlight | Background of a selected option. |
--rc-listbox-selected-color | HighlightText | Text color of a selected option. |
--rc-listbox-disabled-color | GrayText | Text color of a disabled option. |
--rc-listbox-disabled-opacity | 1 | Opacity of a disabled option. |
--rc-item-font-family | inherit | Option font family; shared with menu and adaptive-menu items. |
--rc-item-font-size | inherit | Option font size; shared with menu and adaptive-menu items. |
--rc-item-font-weight | inherit | Option font weight; shared with menu and adaptive-menu items. |
--rc-item-line-height | inherit | Option line height; shared with menu and adaptive-menu items. |
--rc-item-letter-spacing | inherit | Option letter spacing; shared with menu and adaptive-menu items. |
CSS Parts
| Part | Description |
|---|---|
option | Individual `<li role="option">` elements |
option-checkmark | The checkmark `<span>` inside each option (when `checkmark` is true) |
create-option | The "Create" option when allow-create is active |