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
npm install @rcarls/rc-listbox
yarn add @rcarls/rc-listbox
import '@rcarls/rc-listbox/define';
Live demo
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.
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.
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. |
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}"`. 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 | Not specified | Background of a hovered option. |
--rc-listbox-hover-color | Not specified | Text color of a hovered option. |
--rc-listbox-active-bg | Not specified | Background of the keyboard-active option. |
--rc-listbox-active-color | Not specified | Text color of the keyboard-active option. |
--rc-listbox-selected-bg | Not specified | Background of a selected option. |
--rc-listbox-selected-color | Not specified | Text color of a selected option. |
--rc-listbox-disabled-color | Not specified | Text color of a disabled option. |
--rc-listbox-disabled-opacity | Not specified | Opacity of a disabled option. |
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 |