Select
This page covers two components. Select renders its own listbox, so it can show
things a native picker cannot, and drives the popup from script. NativeSelect keeps a
real <select> and styles it with appearance: base-select, so the
browser owns 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
An option uses the Intersection Observer API to improve performance. This enables
lists of up to 300 options. Beyond that the Select component uses a virtual list to
render the options, which has the limitation of having a fixed height. The switchover point is virtualThreshold; 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 wraps a real <select> element styled with appearance: base-select, instead of building a custom listbox. Options are passed as
children, either plain <option> elements or the Option component,
rather than an options array.
An option with value="" is the placeholder: while it is the selected one the label
stays inside the field instead of floating above it, so give it no text. Every other option floats
the label, including the one the browser picks on its own when you pass no value - bind:value adopts that initial selection, so the label and the value the form submits always
agree.
<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 control how wide the select is allowed to 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 carry aria-selected. Arrow
keys, Home, End, typeahead, Enter and Escape are wired to that, and the field is named by label.
Supporting text is tied to the field through aria-describedby, and an error from issues is announced through role="alert". Where none of the extras are
needed, NativeSelect keeps a real <select>, which brings the
platform picker with it, mobile included.
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' | Defines the visual style of the select component. |
label | string | undefined | undefined | Specifies the floating label for the select field. |
start | Snippet | undefined | undefined | Icon displayed at the beginning of the select field. |
end | Snippet | undefined | undefined | Replaces the arrow |
supportingText | string | '' | Provides additional information below the select, such as usage instructions. |
issues | { message: string }[] | undefined | Displays error messages below the select. Optimized to use with remote form field issues. |
required | boolean | false | Indicates whether the select field is required. |
noAsterisk | boolean | false | Disables the asterisk on the floating label when the select field is required. |
disabled | boolean | false | Indicates whether the select field is disabled. |
multiple | boolean | null | undefined | undefined | Allows multiple selections when set to true. |
clampMenuWidth | boolean | false | Restricts the menu width to match the width of the select component. |
virtualThreshold | number | 300 | Number of options from which on the menu renders as a virtual list. |
...attributes | HTMLAttributes<HTMLDivElement> | Attributes are 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. |
Select bindables
| Attribute | Type | Description |
|---|---|---|
element | HTMLElement | A reference to the root DOM element of the component. This variable is bound using bind:this, allowing direct access to the underlying HTML element for manipulation or querying within
the component's logic. |
value | any | Value of the underlying select |
NativeSelect attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
variant | 'outlined' | 'filled' | 'outlined' | Defines the visual style of the select component. |
label | string | undefined | undefined | Specifies the floating label for the select field. |
supportingText | string | undefined | undefined | Provides additional information below the select, such as usage instructions. |
issues | { message: string }[] | undefined | undefined | Displays error messages below the select. Optimized to use with remote form field issues. |
required | boolean | false | Indicates whether the select field is required. |
noAsterisk | boolean | false | Disables the asterisk on the floating label when the select field is required. |
disabled | boolean | false | Indicates whether the select field is disabled. |
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. |