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.
<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
| Token | Default 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
| Token | Default 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.
| Token | Default value |
|---|---|
--np-select-min-width | 210px |
--np-select-max-width | 100% |
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
| Attribute | Type | Default | Description |
|---|---|---|---|
options | SelectOption[] | [] | Required. The options to choose from. See the table below for their shape. |
variant | 'outlined' | 'filled' | 'outlined' | Visual style of the select. |
label | string | undefined | undefined | Floating label of the select. |
start | Snippet | undefined | undefined | Icon at the start of the select. |
end | Snippet | undefined | undefined | Replaces the arrow. |
supportingText | string | '' | Extra text below the select, such as usage instructions. |
issues | { message: string }[] | undefined | Error messages shown instead of the supporting text. Pass a remote form field's issues(). |
required | boolean | false | Makes the select required. |
noAsterisk | boolean | false | Hides the asterisk on the label when the select is required. |
disabled | boolean | false | Disables the select. |
multiple | boolean | null | undefined | undefined | Allows selecting more than one option. |
clampMenuWidth | boolean | false | Limits the menu width to the width of the select. |
virtualThreshold | number | 300 | From this number of options on, the menu renders as a virtual list. |
...attributes | HTMLAttributes<HTMLDivElement> | Passed to the component container. |
SelectOption
| Property | Type | Description |
|---|---|---|
value | string | number | Required. Value submitted with the form. |
label | string | Required. Text shown for the option. |
selected | boolean | null | undefined | Preselects the option, and is the state a form reset returns it to. |
disabled | boolean | undefined | Makes the option non-selectable. It can still be focused. |
supportingText | string | Snippet | undefined | Second line under the label. |
start / end | Snippet | undefined, end also string | Leading and trailing content of the option. With multiple the check box takes
the place of start. |
class / style | ClassValue / string | Styles a single option, the same way as on an AutoComplete option. |
Select bindables
| Attribute | Type | Description |
|---|---|---|
element | HTMLElement | The root DOM element of the component, bound with bind:this. |
value | any | Value of the underlying select. |
NativeSelect attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
variant | 'outlined' | 'filled' | 'outlined' | Visual style of the select. |
label | string | undefined | undefined | Floating label of the select. |
supportingText | string | undefined | undefined | Extra text below the select, such as usage instructions. |
issues | { message: string }[] | undefined | undefined | Error messages shown instead of the supporting text. Pass a remote form field's issues(). |
required | boolean | false | Makes the select required. |
noAsterisk | boolean | false | Hides the asterisk on the label when the select is required. |
disabled | boolean | false | Disables the select. |
id | string | undefined | undefined | Id of the underlying <select>. Auto-generated when omitted. |
children | Snippet | undefined | undefined | The <option> / Option elements to render inside the select. |
...attributes | HTMLSelectAttributes | The rest, such as name, form, and autofocus, are
forwarded to the underlying <select>. |
NativeSelect bindables
| Attribute | Type | Description |
|---|---|---|
value | any | Value of the underlying select. |