Select

This page covers two components. Select renders its own listbox and controls the popup from script, so it can show content a native picker cannot. NativeSelect is a real <select> styled with appearance: base-select. The browser handles the popup, the keyboard behaviour and the placement.

Usage

The sections up to NativeSelect all describe Select.

Elderberry
Elderberry
<script lang="ts">
	import { Select } from 'noph-ui'
</script>

<Select
	label="Fruit"
	name="fruit"
	options={[
		{ value: '', label: '' },
		{ value: 'apple', label: 'Apple', selected: true },
		{ value: 'apricot', label: 'Apricot' },
		{ value: 'banana', label: 'Banana' },
		{ value: 'cherry', label: 'Cherry' },
		{ value: 'elderberry', label: 'Elderberry', disabled: true },
		{ value: 'fig', label: 'Fig' },
		{ value: 'grape', label: 'Grape' },
	]}
/>
<Select
	label="Fruit"
	variant="filled"
	name="fruit"
	options={[
		{ value: '', label: '', selected: true },
		{ value: 'apple', label: 'Apple' },
		{ value: 'apricot', label: 'Apricot' },
		{ value: 'banana', label: 'Banana' },
		{ value: 'cherry', label: 'Cherry' },
		{ value: 'elderberry', label: 'Elderberry', disabled: true },
		{ value: 'fig', label: 'Fig' },
		{ value: 'grape', label: 'Grape' },
	]}
/>

Disabled

<script lang="ts">
	import { Select } from 'noph-ui'
</script>

<Select label="Fruit" name="fruit" disabled options={[{ value: '', label: '' }]} />
<Select
	label="Fruit"
	variant="filled"
	name="fruit"
	disabled
	options={[{ value: '', label: '', selected: true }]}
/>

Validation

<script lang="ts">
	import { Button, Select } from 'noph-ui'
</script>

<form>
	<Select
		label="Genre"
		name="genre"
		required
		options={[
			{ value: '', label: '' },
			{ value: 'rock', label: 'Rock' },
			{ value: 'pop', label: 'Pop' },
			{ value: 'jazz', label: 'Jazz' },
		]}
	/>
	<div class="button-area">
		<Button type="submit">Send</Button>
	</div>
</form>

<style>
	.button-area {
		display: flex;
		justify-content: flex-end;
		margin-top: 1rem;
	}
</style>

Multiple selection

<script lang="ts">
	import { Select } from 'noph-ui'
</script>

<Select
	label="Favorite fruit"
	name="fruit"
	style="max-width: 300px"
	multiple
	options={[
		{ value: 'apple', label: 'Apple' },
		{ value: 'apricot', label: 'Apricot' },
		{ value: 'banana', label: 'Banana' },
		{ value: 'cherry', label: 'Cherry' },
		{ value: 'elderberry', label: 'Elderberry' },
		{ value: 'fig', label: 'Fig' },
	]}
/>

Virtual list

Options use the Intersection Observer API to render fast. This works for lists of up to 300 options. Above that, Select renders the options in a virtual list, which has a fixed height. virtualThreshold sets where it switches. Lower it when your options are expensive to render.

<script lang="ts">
	import { Select } from 'noph-ui'
</script>

<Select
	label="Virtual list"
	name="virtual_list"
	style="max-width: 300px"
	options={[
		...Array.from({ length: 4001 }, (_, i) => ({
			value: `option${i + 1}`,
			label: `Option ${i + 1}`,
		})),
	]}
/>

Icons

<script lang="ts">
	import { Select } from 'noph-ui'
	import { Icon } from 'noph-ui/icons'
</script>

<Select
	label="Favorite fruit"
	name="fruit"
	options={[
		{ value: '', label: '' },
		{ value: 'apple', label: 'Apple' },
		{ value: 'apricot', label: 'Apricot' },
		{ value: 'banana', label: 'Banana' },
	]}
>
	{#snippet start()}
		<Icon>favorite</Icon>
	{/snippet}
</Select>
<Select
	label="Favorite car"
	name="car"
	options={[
		{ value: '', label: '' },
		{ value: 'audi', label: 'Audi' },
		{ value: 'bmw', label: 'BMW' },
		{ value: 'mercedes', label: 'Mercedes' },
		{ value: 'vw', label: 'Volkswagen' },
	]}
>
	{#snippet end()}
		<Icon>favorite</Icon>
	{/snippet}
</Select>

NativeSelect

NativeSelect takes its options as children, either plain <option> elements or the Option component, not as an options array.

An option with value="" is the placeholder. Give it no text. While it is selected, the label stays inside the field. Any other selected option floats the label above the field. This includes the option the browser selects when you pass no value. bind:value takes over that initial selection, so the label and the submitted value always match.

<script lang="ts">
	import { NativeSelect, Option } from 'noph-ui'
</script>

<NativeSelect label="Fruit" name="fruit">
	<Option value=""></Option>
	<Option value="apple">Apple</Option>
	<Option value="apricot">Apricot</Option>
	<Option value="banana">Banana</Option>
	<Option value="cherry">Cherry</Option>
	<Option value="elderberry" disabled>Elderberry</Option>
	<Option value="fig">Fig</Option>
	<Option value="grape">Grape</Option>
</NativeSelect>
<NativeSelect variant="filled" label="Fruit" name="fruit">
	<Option value=""></Option>
	<Option value="apple">Apple</Option>
	<Option value="apricot">Apricot</Option>
	<Option value="banana">Banana</Option>
	<Option value="cherry">Cherry</Option>
	<Option value="elderberry" disabled>Elderberry</Option>
	<Option value="fig">Fig</Option>
	<Option value="grape">Grape</Option>
</NativeSelect>

Disabled

<script lang="ts">
	import { NativeSelect, Option } from 'noph-ui'
</script>

<NativeSelect label="Fruit" name="fruit" disabled>
	<Option value="apple">Apple</Option>
</NativeSelect>
<NativeSelect variant="filled" label="Fruit" name="fruit" disabled>
	<Option value="apple">Apple</Option>
</NativeSelect>

Validation

<script lang="ts">
	import { Button, NativeSelect, Option } from 'noph-ui'
</script>

<form>
	<NativeSelect label="Genre" name="genre" required>
		<Option value=""></Option>
		<Option value="rock">Rock</Option>
		<Option value="pop">Pop</Option>
		<Option value="jazz">Jazz</Option>
	</NativeSelect>
	<div class="button-area">
		<Button type="submit">Send</Button>
	</div>
</form>

<style>
	.button-area {
		display: flex;
		justify-content: flex-end;
		margin-top: 1rem;
	}
</style>

Theming

Filled select tokens

TokenDefault value
--np-filled-select-text-field-container-shape--np-shape-corner-extra-small
--np-filled-select-text-field-container-color--np-color-surface-container-highest

Filled select example

<script lang="ts">
	import { Select } from 'noph-ui'
</script>

<Select
	variant="filled"
	label="Filled"
	options={[
		{ value: '', label: '', selected: true },
		{ value: 'apple', label: 'Apple' },
		{ value: 'apricot', label: 'Apricot' },
		{ value: 'banana', label: 'Banana' },
	]}
	--np-filled-select-text-field-container-shape="0"
	--np-filled-select-text-field-container-color="var(--np-color-surface-container)"
	--np-color-primary="var(--np-color-tertiary)"
/>

Outlined select tokens

TokenDefault value
--np-outlined-select-text-field-container-shape--np-shape-corner-extra-small

Outlined select example

<script lang="ts">
	import { Select } from 'noph-ui'
</script>

<Select
	options={[
		{ value: '', label: '', selected: true },
		{ value: 'apple', label: 'Apple' },
		{ value: 'apricot', label: 'Apricot' },
		{ value: 'banana', label: 'Banana' },
	]}
	label="Outlined"
	--np-outlined-select-text-field-container-shape="0"
	--np-color-primary="var(--np-color-tertiary)"
/>

Sizing tokens

These apply to both variants and set how wide the select can get.

TokenDefault value
--np-select-min-width210px
--np-select-max-width100%

These tokens apply to NativeSelect as well.

Accessibility

Select renders the field as a role="combobox" with aria-expanded, aria-controls and aria-activedescendant, and the popup as a role="listbox" whose options have aria-selected. It supports arrow keys, Home, End, typeahead, Enter and Escape. The first arrow press moves to the selected option. Disabled options can take focus so screen readers read them out, but they cannot be selected. label names the field, with the asterisk when it is required. The field also has aria-required.

Supporting text is linked to the field with aria-describedby. An error from issues sets aria-invalid on the field, points aria-errormessage at the message and is announced with role="alert". If you don't need the extras of Select, use NativeSelect. It is a real <select> and uses the platform picker, also on mobile.

API

Select attributes

AttributeTypeDefaultDescription
optionsSelectOption[][]Required. The options to choose from. See the table below for their shape.
variant'outlined' | 'filled''outlined'Visual style of the select.
labelstring | undefinedundefinedFloating label of the select.
startSnippet | undefinedundefinedIcon at the start of the select.
endSnippet | undefinedundefinedReplaces the arrow.
supportingTextstring''Extra text below the select, such as usage instructions.
issues{ message: string }[]undefinedError messages shown instead of the supporting text. Pass a remote form field's issues().
requiredbooleanfalseMakes the select required.
noAsteriskbooleanfalseHides the asterisk on the label when the select is required.
disabledbooleanfalseDisables the select.
multipleboolean | null | undefinedundefinedAllows selecting more than one option.
clampMenuWidthbooleanfalseLimits the menu width to the width of the select.
virtualThresholdnumber300From this number of options on, the menu renders as a virtual list.
...attributesHTMLAttributes<HTMLDivElement>Passed to the component container.

SelectOption

PropertyTypeDescription
valuestring | numberRequired. Value submitted with the form.
labelstringRequired. Text shown for the option.
selectedboolean | null | undefinedPreselects the option, and is the state a form reset returns it to.
disabledboolean | undefinedMakes the option non-selectable. It can still be focused.
supportingTextstring | Snippet | undefinedSecond line under the label.
start / endSnippet | undefined, end also stringLeading and trailing content of the option. With multiple the check box takes the place of start.
class / styleClassValue / stringStyles a single option, the same way as on an AutoComplete option.

Select bindables

AttributeTypeDescription
elementHTMLElementThe root DOM element of the component, bound with bind:this.
valueanyValue of the underlying select.

NativeSelect attributes

AttributeTypeDefaultDescription
variant'outlined' | 'filled''outlined'Visual style of the select.
labelstring | undefinedundefinedFloating label of the select.
supportingTextstring | undefinedundefinedExtra text below the select, such as usage instructions.
issues{ message: string }[] | undefinedundefinedError messages shown instead of the supporting text. Pass a remote form field's issues().
requiredbooleanfalseMakes the select required.
noAsteriskbooleanfalseHides the asterisk on the label when the select is required.
disabledbooleanfalseDisables the select.
idstring | undefinedundefinedId of the underlying <select>. Auto-generated when omitted.
childrenSnippet | undefinedundefinedThe <option> / Option elements to render inside the select.
...attributesHTMLSelectAttributesThe rest, such as name, form, and autofocus, are forwarded to the underlying <select>.

NativeSelect bindables

AttributeTypeDescription
valueanyValue of the underlying select.