COMPONENTS / ATOMS / al-select← ALL COMPONENTS

Select

<al-select><ALSelect> REACTbeta

Reference generated from the custom elements manifest — 19 properties, 4 slots, 2 events.

PLAYGROUND11 CONTROLS
PREVIEW / al-select / SOUTHLEFT
Loading preview
List item 1List item 2List item 3List item 4List item 5List item 6List item 7List item 8List item 9List item 10
align
FLAGS
ICON SLOTS
CODE — HTML / REACT

Guidance

AUTHORED · CITED TO SOURCE

A choice from a known list, rendered as a field the user opens rather than types into. It composes al-input for the field itself and al-dropdown-panel for the options, so it inherits the whole form-field treatment — label, required marker, field note, error note — and adds the open/closed state and keyboard traversal on top.

WHEN TO USE

  • The value comes from a set the product knows in advance: a country, a status, an assignee, a plan.
  • The list is long enough that showing every option at once would dominate the form, but short enough that the user recognises the option rather than recalling it.
  • The list is long enough to need filtering — set hasSearch and a search field is rendered in the panel header.

WHEN NOT TO USE

  • The user may need to enter a value that is not in the list. The field is isReadonly by default, so typing an unlisted value is not possible.USE COMBOBOX →
  • There are only two or three options and the form has room. Showing them all costs one click less and makes the choice visible while filling the rest of the form.USE RADIO-GROUP →
  • The choice is a binary on/off for a single setting.USE TOGGLE →
  • What is being opened is a list of actions or destinations rather than a value the form will submit.USE MENU →

DO

  • Give the panel a search field with hasSearch once the list passes roughly ten options — scanning a long unfiltered list is where this control gets slow.
  • Use align="top" when the field sits near the bottom of the viewport, so the panel opens into space rather than off-screen.
  • Set isError alongside errorNote, exactly as with al-input — the field treatment is inherited, and so is that requirement.
  • Keep isReadonly at its default true unless you have deliberately built a type-ahead. It is what stops the user typing a value the list cannot honour.

DON’T

  • Don't expect the options to exist in the DOM while the select is closed. The dropdown panel is only rendered when the panel is open, so anything that walks the light DOM for options — a test, a form serializer, an autofill heuristic — will find nothing until it is opened.
  • Don't stamp role="option" onto the slotted items. They are al-list-items that render their own <button>, and an interactive role wrapping an interactive control is exactly the pattern the component avoided on purpose.
  • Don't use this for multi-select without checking what the composed panel actually renders — the component registers al-checkbox for that case, but the behaviour is not a documented public prop.
  • Don't reach for a select when the answer is a date. There is a dedicated picker with the calendar semantics that come with it.

ACCESSIBILITY IN PRACTICE

  • The combobox semantics — role="combobox", aria-expanded, aria-haspopup="listbox" and aria-controls — were added deliberately after a review found the control announced only as "edit text", with nothing saying a list would open.
  • Those attributes are set on the <al-input> host element rather than on the <input> inside its shadow root that actually takes focus. Verify the announcement in your target screen reader before relying on it.
  • aria-controls is emitted only while the panel is open, which is correct — the panel it names does not exist when closed.
  • The options stay un-roled on purpose. aria-haspopup="listbox" states the intent without claiming a tree structure the DOM does not have.

CONTENT GUIDELINES

  • Label the field with the thing being chosen ("Assignee", "Billing country"), not the act of choosing ("Select an assignee").
  • Use placeholder for the unselected state and make it a real instruction the user can act on — it is the only text in the field before a choice is made.
  • Order options by what the user is most likely to want, not alphabetically, unless the list is long enough that alphabetical order is how they will search it.
  • Keep option labels parallel in grammar and length so the list can be scanned rather than read.

WHERE THIS CAME FROM

  • libs/al-web-components/components/select/select.tsThe combobox semantics and the recorded reason they were added — the control previously announced as plain edit text.
  • libs/al-web-components/components/select/select.tsThe field is read-only by default; that default is what makes this a picker rather than a text field.
  • libs/al-web-components/components/select/select.tsThe optional search field rendered into the dropdown panel header.
  • libs/al-web-components/components/select/select.tsThe panel — and therefore every option — is rendered only while open.

Each line above names a file and a literal string that must still appear in it.pnpm gate:guidance re-reads them from this built page, so guidance that has stopped being true fails a check rather than misleading you.

NO DESIGN-SYSTEM PROJECT TRACKS THIS COMPONENT AGAINST A FIGMA FILE — IT IS EITHER OUTSIDE EVERY PROJECT’S DECLARED SCOPE, OR NOT YET SEEDED INTO ITS PROJECT’S PARITY MANIFEST.

CHECKEVIDENCEDETAILRESULT
Default stateAXEselect--DefaultPASS
Advanced statesAXE12 further storiesPASS
Screen readerMANUALNo screen-reader pass has been recorded for this component.NOT RECORDED
Keyboard navigationINTERACTION TESTSNo interaction tests and no manual keyboard pass.NOT RECORDED

MEASURED 2026-09-08 · 13 STORIES · AXE WCAG2A + WCAG2AA + WCAG21A + WCAG21AA + WCAG22AA

COLOR CONTRAST1 RULE FAILING

The CI gate disables color-contrast globally (libs/al-web-components/story-fixture/src/main.ts), so its result never reaches a test run. It is enabled for this measurement, and reported here whichever way it comes out.

RULEWHEREELEMENTS
color-contrastElements must meet minimum color contrast ratio thresholdsselect--Disabled1
import '@southleft/al-web-components/components/select';
import { ALSelect } from '@southleft/al-react';
PROPTYPEDEFAULT
isActivebooleanis Active
isActiveDropdownbooleanis Active select 1. Select is open when set to true. Close when set to false
fieldIdstringThe unique id of the select
titlestringThe select's title
labelstringThe select's label'Label'
namestringThe select's name attribute
valuestringThe select's value attribute
placeholderstringPlaceholder attribute - Specifies a short hint that describes the expected value of an input element
fieldNotestringThe select field note
errorNotestringThe select error note
ariaDescribedBystringAria describedby 1. Used to connect the field note in select to the select menu for accessibility
isRequiredbooleanThe select's required attributefalse
isOptionalbooleanOptional state - Specifies that a field is optional and adds the text 'optional' to the label
isDisabledbooleanThe select's disabled attribute
isErrorbooleanError state
isReadonlybooleanReadonly attribute - Specifies that an input field is read-onlytrue
hideLabelbooleanHide the label?
hasSearchbooleanAdd a search input to the dropdown panelfalse
align'bottom' | 'top'**Select alignment** - **bottom** Dropdown panel appears on the bottom - **top** Dropdown panel appears on the top'bottom'
SLOTDESCRIPTION
beforeOptional leading content within the select control.
(default)The select content
field-noteIf content is slotted, it will display in place of the fieldNote property
errorIf content is slotted, it will display in place of the errorNote property
EVENTDESCRIPTIONTYPE
onSelectOpenFired when the option list opens. Detail: `{ active }` — always `true`.CustomEvent
onSelectCloseFired when the option list closes. Detail: `{ active }` — always `false`.CustomEvent
METHODDESCRIPTION
handleOnClickOutside()Handle click outside the component 1. Close the show hide panel on click outside 2. If the nav is already closed then we don't care about outside clicks and we can bail early 3. By the time a user clicks on the page the shadowRoot will almost certainly be defined, but TypeScript isn't that trusting and sees this.shadowRoot as possibly undefined. To work around that we'll check that we have a shadowRoot (and a rendered .host) element here to appease the TypeScript compiler. This should never actually be shown or run for a human end user. 4. Check to see if we clicked inside the active panel 5. If the panel is active and we've clicked outside of the panel then it should be closed.
toggleActive()Set menu active state 1. Toggle the active state between true and false
handleOnActiveDropdown()Handles the activation behavior of the select: 1. Positions the dropdown panel based on available viewport space 2. Sets focus to the first element in the dropdown panel or the search box (if available) when active 3. When the select is active: 3.1. If hasSearch is false and a value is available, focuses on the selected item & scrolls to view it 3.2. If hasSearch is true and a value is available, focuses on the search box (no need to focus the active element) & scrolls to view it 4. When the select is active and no value is available: 4.1. If hasSearch is false, focuses on the first non-disabled element 4.2. If hasSearch is true, as the search box has focus, no need to focus the first non-disabled element
handleOnKeydown()Handle on select input keydown 1. If key selected is enter or spacebar, toggle the menu open/close
handleOnKeydownDropdownPanel()Handle on keydown 1. If the panel is open and escape is keyed, close the menu and return focus to the trigger button 2. Find the last item in the list. Set the last element to that item to define variable 3. If the last element is defined, once Tab is selected after that the panel will close
addClickHandlers()Lifecycle method triggered when the component is first updated on the page 1. Attaches click handlers to ALListItem components within the select that do not contain children 2. If the component is not readonly, clears the input value 3. If the component is not readonly, focuses on the select after selecting an item 4. Sets the select value with the text content of the selected list item 5. Resizes the select to fit the value width if the variant is bare 6. Ensures only the last selected item remains active
handleOnChange()Change output binding 1. If the input field is not readonly, then allow typing 2. Clear the label & value's to show the filling text 3. Set the select to active while filling

SOURCE — components/select/select.ts · Atoms/Form/Select