Select
Lets people choose one value from a closed list — a trigger that looks and measures like a text input's box and shows the chosen option, and a list of options, optionally grouped, that opens from it — without a label row, help or error text of its own. For a bar, a toolbar or a table, or inside a field a product composes itself.
- Category
- Selection
- Tokens
comp.select- Level
- Base
- Used in
- Select field
- Web
tanrei-selectschedule - SwiftUI
TanreiSelectradio_button_unchecked - Jetpack Compose
TanreiSelectradio_button_unchecked - Flutter
TanreiSelectradio_button_unchecked - React Native
Selectradio_button_unchecked
- Variants not shipped in
- tanrei: filled, outlined
Generated from docs/status/status.json, specs/select.spec.json
Bare: a theme select for a bar, and a sort order over a list
A select is the box of a select field, alone: the trigger with the value it shows, its leading icon and its chevron, and the list of options that opens from it. It has no label row above it, no requirement mark, no help and no error text below it.
Use it bare where the label a person reads stands somewhere else, or is plain from where the select stands and what it shows:
| Where | What tells a person what it sets | The select’s label |
|---|---|---|
| A theme or a language in an app bar | Its place, its glyph and the value it shows | “Theme”, “Language” |
| A sort order or a filter in a toolbar | The list it stands over | “Sort by” |
| A cell in a column of a table; the rows-per-page select under a table | The column’s header; the text beside the select | The column and the row: “Role, Ada Lovelace” |
Bare, the select needs a label of its own. It is the accessible name. A stacked select never draws it; a filled or an outlined one draws it as the label that floats in the box.
In a form, use the select field: this select inside a field, with a label above it, a requirement mark, help and an error.
Used in the select field. To frame it yourself, put it in a field, which names it, describes it, marks it required and puts it in its error condition.
Where you set no width, a bare select is as wide as its widest option needs, so choosing never changes its width. In a field it fills the field.
On the web it is <tanrei-select> with <tanrei-option> children. The other platforms have not implemented it yet.
The options are children of the select: each has a value and a label. value names the chosen one. The select emits change when the person chooses another option, and toggle when the person opens or closes the list. Neither is emitted when you set value or open yourself.
<!-- In an app bar: small, named by its own label, which is not drawn. -->
<tanrei-select size="small" label="Theme" value="dark">
<tanrei-option value="light" leading-icon="light_mode">Light</tanrei-option>
<tanrei-option value="dark" leading-icon="dark_mode">Dark</tanrei-option>
</tanrei-select>
<!-- A cell of a table: the column's header is the visible label. -->
<td>
<tanrei-select name="role-ada" size="small" label="Role, Ada Lovelace" value="editor">
<tanrei-option value="viewer">Viewer</tanrei-option>
<tanrei-option value="editor">Editor</tanrei-option>
</tanrei-select>
</td>
<script type="module">
import '@tanrei/web/components/select';
const theme = document.querySelector('tanrei-select');
// The person chose another option. The value is already the new one.
theme.addEventListener('change', () => apply(theme.value));
// Opened or closed by the person; a close says why.
theme.addEventListener('toggle', (e) => console.log(e.detail.open, e.detail.reason));
</script>import { Option, Select } from '@tanrei/react';
<Select size="small" label="Theme" value={theme} onChange={(event) => setTheme(event.currentTarget.value)}>
<Option value="light" leadingIcon="light_mode">Light</Option>
<Option value="dark" leadingIcon="dark_mode">Dark</Option>
</Select>A field around a select is a select field written out. Leave the select’s own label empty: the field names it. label, error, disabled and the requirement are said on the field; name, value, the options and everything about choosing on the select.
<tanrei-field label="Country" required required-text="Required"
supporting-text="Where your invoices are addressed.">
<tanrei-select name="country" placeholder="Choose a country">
<tanrei-option value="jp">Japan</tanrei-option>
<tanrei-option value="ke">Kenya</tanrei-option>
</tanrei-select>
</tanrei-field>A field with a select in it: the same picture as a select field
The select tells its field what only it knows. Its help starts where the value starts, past the padding and a leading icon. A filled or an outlined select draws the field’s label itself, floating in the box, so the field draws no label row. And while its list is open the select is drawn focused, whether or not it has focus, so the field draws its label and its help focused for as long.
Bare selects: groups, supporting text with a disabled option, in error, read-only and disabled
| Part | What it is |
|---|---|
| Value | The chosen option’s label, on one line, cut with an ellipsis where it does not fit |
| Placeholder | A hint shown while nothing is chosen. Never a label, and never a choice |
| Leading icon | The glyph in the leading-icon slot, or else the chosen option’s own |
| Chevron | Says that the box opens a list. It does not turn. A read-only select draws none |
| Floating label | In a filled or an outlined select only: its name, resting in the line while nothing is chosen and floating above the value otherwise. An asterisk follows it where the field around the select says it is required |
The three variants are the text input’s. The default system ships stacked only; filled and outlined are for systems that draw their text inputs that way. Use one style per form.
small (32), medium (40) and large (56) are the text input’s heights. small is for a bar, a toolbar or a dense row of controls, where a small select and a small text input stand at one height.
- The select draws the box, the value, the placeholder, the icons, the list and its options, and the label that floats in a filled or an outlined box. It holds the value, its place in a form and the events. It says to assistive technology the chosen option, whether the list is open, the active option, and that it is read-only.
- The field around it draws the label row above a stacked select, the required or optional mark, the help and the error text. It holds nothing. It gives the select its name, the help or the error text as its description, and that it is required, invalid or disabled.
Bare, without a field:
- The name is
label.labelHiddenstops a filled or an outlined select from drawing it; it changes nothing on a stacked one. - An error is
error, which you set: the box is drawn in its error condition and exposed as invalid. The select has no error text. Say what is wrong in words beside it, or frame it. - Required is a field’s to say. A bare select has no requirement setting: where the form needs an answer, validate it yourself.
The list opens comp.select.list.offset below the container with the start edges together, and above it where there is no room below. It is as wide as its options need on one line: never narrower than the container or list.min-width (160), and never wider than list.max-width (320) where the container is narrower than that, so a long label or a supporting line reads in fewer lines without the container changing width. An option that needs more wraps. It is as tall as its options up to list.max-height (320), and scrolls beyond that. When it opens, the chosen option is in view.
| Part | What it is |
|---|---|
| Option | A row with a label, and optionally a supporting line and a glyph. The chosen one has a quiet fill and a check |
| Group | A run of options under an optional title. Groups are parted by a line or by space, as the design system or separation says |
| Disabled option | Reachable with the arrow keys, so people learn it exists, and never chosen |
An option’s glyph stands in a column before its label. iconSpace says where that column is kept in an option without a glyph. auto (the default) keeps it in a section where another option has a glyph, so that the labels of the section start on one line, and keeps none in a section without glyphs; a section is a group, or a run of options outside any group. always keeps it in every option, and never only in an option that has a glyph. The menu has the same iconSpace, with the same values and default.
density sets how much room an option takes: regular (the default, 48 high) or compact (32 high with 20 glyphs, the menu’s compact row), for a tool used with a mouse whose lists are long. The text keeps its size, and the container is size’s whatever the density. For touch an option is never lower than the touch target.
An option whose value is the empty string is a choice like any other, for “None” or “Any”. The select has no way of its own back to nothing chosen: where no choice is allowed, give an option that says so.
Where the options come from somewhere that takes time or can fail, say so in the list. status, statusText and emptyText are the combobox’s and the search’s properties, with the same values and meaning.
| You set | The list shows after its options |
|---|---|
status="loading" and statusText |
A small loading indicator, named by statusText (“Loading projects”). The list is marked busy and nothing is announced. Options already there can still be chosen |
status="error" and statusText |
The text in the error color, read at once (“Projects could not be loaded”) |
statusText alone |
The text, read politely (“Your 3 most recent projects”) |
emptyText, while there is no option |
The text in place of the options (“No projects yet”) |
This status row stays in view while the options scroll. It is not an option: a press on it does nothing, and the arrow keys never reach it. What it already says when the list opens is read as the list opens.
The select never fetches, waits or retries. Set status to loading when you start, give the options and set it back to idle when they arrive, or set error with words for the failure.
A select with no option, no status and no emptyText does not open. Give it an emptyText, or the product’s noOptionsText once with setWords(), so that a person who opens it learns why there is nothing to choose.
Loading, a failure, and nothing to choose
| Input | What happens |
|---|---|
| A press on the container; Enter, Space, Down Arrow or Up Arrow | Opens the list. The chosen option is the active one, or the first enabled one |
| A printable character | Opens the list and makes active the next option that begins with what was typed |
| Down Arrow, Up Arrow | The next or previous option, across groups, stopping at the ends |
| Home, End; Page Down, Page Up | The first or last option; ten options down or up |
| Enter or Space; a press on an option | Chooses the active option and closes the list |
| Escape; Tab; a press outside; a press on the container | Closes the list and chooses nothing |
The value changes only when the person confirms an option in the open list: a press, Enter or Space. Arrow keys and typing on a closed select open the list instead of changing the value unseen, because a change may save, navigate or rebuild a page.
toggle carries the reason for a close: select, escape, outside, focus-out, anchor-hidden, press or tab.
Anatomy
| Part | Description |
|---|---|
1 container | The trigger's box: it shows the chosen value and opens the list. It measures and is drawn as the text input's container of the same variant and size, carries the hover state layer and shows focus by its edge. A press anywhere on it opens the list. |
2 outline · optional | The border of an outlined select, which opens a gap where the floated label crosses it; in a stacked select, the line drawn inside the container's edge while it is focused or in error, which takes no layout space. |
3 active-indicator · optional | The line along the bottom of a filled select; its focused state is thicker and colored. |
4 label · optional | In a filled or outlined select, its name drawn inside the box: it rests in the line while nothing is chosen and the select is neither focused nor open, and floats above the value otherwise. A stacked select draws none: the label above it is the row of the Field that frames it. |
5 required-asterisk · optional | After the floating label's text, an asterisk, where the Field that frames the select says it is required and marks it (SEL-7). Kept from assistive technology. |
6 value | The chosen option's label, on one line in the container, cut where it does not fit. It is drawn from the input part of the tokens: the text input's name for the same line. |
7 placeholder · optional | A hint shown in the line while nothing is chosen; never a label. |
8 leading-icon · optional | A glyph at the start of the container: the one the product gives the select, or else the chosen option's own. |
9 chevron | The glyph expand_more of the system's icon set at the end of the container: it says that the box opens a list. It is drawn from the trailing-icon part of the tokens, is not a target of its own, does not turn while the list is open and is never mirrored. A read-only select draws none. |
10 list | The floating surface that holds the options: the raised sheet a menu and a popover are drawn on, placed against the container as a popover is against its anchor, as wide as it, scrolling when its options do not fit. |
11 option | One choice: a row of the list, laid out and drawn as the menu's item. It carries hover, the active highlight and press and, while it is the chosen one, the quiet selection's fill. |
12 option-label | The option's text, its accessible name and what the container shows once the option is chosen. |
13 option-supporting-text · optional | A line below an option's label that says more about the choice. It is shown in the list only. |
14 option-leading-icon · optional | A glyph before an option's label. Its column is kept in every row of a list where any option has one. |
15 mark · optional | The glyph check of the system's icon set at the end of the chosen option's row, in a box kept at the end of every row. With the row's fill it shows the choice; the fill never shows it alone. |
16 group · optional | A run of related options (an option group), optionally under a title, separated from what is next to it by a divider or by a gap. |
17 group-title · optional | The heading above a group's options. It names the group, takes no focus and is not a choice. |
18 divider · optional | The hairline between two groups while the separation is divider. Under gap there is none. |
19 status · optional | A row after the options that says what the product's source of options is doing — a loading indicator, a count, a failure — or, while the select holds no option, that there is nothing to choose (the empty text). Not an option: it takes no input and is never active. It stands under the options, outside the region that scrolls. |
20 focus-ring | Gap-then-ring indicator inside an option's row while the option is the active one by the keyboard. The container itself draws no ring: its focused edge is its focus indicator. |
Properties
| Property | Type | Default | Description |
|---|---|---|---|
variant | stacked | filled | outlined | "stacked" | The text input's three looks, so that a select is drawn as the text inputs beside it. Stacked is a filled box that shows focus and error with a line inside its edge and draws no label: the label of the Field that frames it stands above it. Filled and outlined float the label inside the box. Use one style per form. A design system may leave some out (the Tanrei system ships stacked only) or add values. |
size | small | medium | large | "medium" | Minimum height of the container: stacked 32 / 40 / 56, as tall as a Button of the same size, so that the box and a Button side by side in one row stand as tall as each other; filled and outlined 40 / 48 / 56, which hold the label that floats inside them. The text input's three sizes: medium and large for forms, small for a select in an app bar, a toolbar or a dense row of controls, where nothing frames it. The hit area always meets the minimum target. A design system may add values. |
value | string | "" | The value of the chosen option. The select shows that option's label; while value names no option it shows the placeholder. An option whose value is the empty string is a choice like any other ("None", "Any") and is the chosen one while value is empty. |
label | string | "" | Names the select for assistive technology where no Field frames it; a filled or outlined select also draws it, as the label that floats in its box. Inside a Field leave it empty: the Field's label names the select, and is what floats. |
labelHidden | boolean | false | Does not draw the floating label of a filled or outlined select, which stays the accessible name. Inside a Field, hide the label on the Field. A stacked select draws no label, so this changes nothing there. |
placeholder | string | "" | A hint shown while nothing is chosen and the label is not in the line ("Choose a country"). Never a replacement for label, and never a choice. |
error | boolean | false | Draws the select in its error condition and exposes it as invalid. A Field sets it from its own error; set it here only where no Field frames the select. What a value means is the product's to validate; the select never sets this itself. |
disabled | boolean | false | Disables the select: it takes no focus, does not open and is left out of form submission. Inside a Field, disable the Field, so that its label follows. |
readOnly | boolean | false | The value is shown, can be focused and is submitted, but cannot be changed: the list does not open and no chevron is drawn. |
open | boolean | false | Whether the list is shown. The select opens and closes itself and emits toggle; the product may also set it. Ignored while the select is disabled or read-only or has nothing to show: no option, no status row and no empty text (SEL-61). |
separation | auto | divider | gap | "auto" | How a group of options is told from what is next to it. divider: a line between them. gap: empty space and no line. Auto takes comp.select.separation, which follows the design system's choice. |
iconSpace | auto | always | never | "auto" | Whether an option keeps the room of the leading icon column where it has no icon, so that the labels of a section start on one line — a section being an option group, or a run of adjacent options outside any group. auto: an option keeps the column while another option of its section has a leading icon, so that a section without icons starts its labels where the row's content starts. always: every option keeps it. never: only an option that has an icon has the column. The menu's iconSpace, with the same values and default (SEL-66). |
density | regular | compact | "regular" | How much room an option of the list takes: its least height, its paddings, the gap between its columns and the size of its glyphs (the leading icon, the check), and never its text style. regular (a 48 row in the default system) is for a product used by touch as well as by pointer; compact (a 32 row with 20 glyphs, the menu's compact row) is for a tool used with a pointer, whose lists are long. For touch an option is never lower than the touch target in either (SEL-37). The field's own box is not changed: its height is size's. A design system may add values. |
status | idle | loading | error | "idle" | What the product's source of options is doing, which the product sets: idle (the options given are final), loading (options are on their way: the status row shows a loading indicator and the list is marked busy) or error (the source failed: the status row shows statusText in the error color, announced at once). The combobox's and the search's values, with the same meaning. The select never loads anything itself. |
statusText | string | "" | The status row's text, in the product's language, read to assistive technology when the list opens and when it changes: a count ("20 of 340 projects shown"), that loading failed ("Projects could not be loaded"), or, while status is loading, the loading indicator's name ("Loading projects"). Empty draws no status row unless status is loading. |
emptyText | string | "" | Shown in the list while the select holds no option, status is idle and statusText is empty, in the product's language ("No projects yet"), so that the select still opens and says why there is nothing to choose. The product's: Tanrei has no words for it. Empty reads the product's words (noOptionsText); with neither, the select has nothing to show and does not open (SEL-61), and it is reported. |
Slots
| Slot | Description |
|---|---|
leading-icon | A glyph at the start of the container that stands for the field whatever is chosen (a globe before a language). It takes the place of the chosen option's own glyph. |
Events
| Event | Description |
|---|---|
change | The person chose an option whose value differs from the select's; carries the new value. Not emitted when the product sets value, when the chosen option is chosen again, or while the person only moves through the list. |
toggle | The list was opened or closed by the person; carries the new open and, for a close, the reason: select, escape, back, outside, focus-out, anchor-hidden, press or tab (in the platform's casing). After a choice it follows change. |
Items
The kinds of item the component repeats. A fixture lists its items in order; each platform names each kind in its own idiom.
option
One choice of the select: a label, the value it stands for, and optionally a supporting line and a glyph.
| Property | Type | Default | Description |
|---|---|---|---|
label | string | "" | The option's text and its accessible name; the container shows it once the option is chosen. Required. |
value | string | "" | Identifies the choice: the option is the chosen one while the select's value equals it. Unique within the select. The empty string is a value like any other, for an option that stands for no choice ("None"). |
supportingText | string | "" | A line below the label that says more about the choice. Shown in the list, not in the container. |
leadingIcon | string | "" | The name of an icon (a glyph of the system's icon set) before the label; the container shows it too while the option is chosen and the select has no leading icon of its own. |
disabled | boolean | false | Prevents the option from being chosen. It stays reachable with the arrow keys. |
| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| Item | tanrei-option | TanreiSelectOption | TanreiSelectOption | TanreiSelectOption | SelectOption |
label · Property | label | title | text | label | label |
value · Property | value | tag | value | value | value |
supportingText · Property | supportingText | supportingText | supportingText | supportingText | supportingText |
leadingIcon · Property | leadingIcon | leadingIcon | leadingIcon | leadingIcon | leadingIcon |
disabled · Property | disabled | disabled | enabled | enabled | disabled |
| Notes | <tanrei-option value="jp">Japan</tanrei-option>: the label is the label attribute or, without one, the element's text, as <option>. Attributes supporting-text, leading-icon, disabled. The element is data for its select: it draws nothing itself. tanrei-option and tanrei-option-group are one pair of elements for the select and the combobox: a select that needs a filter becomes a combobox with the same children. Whether an option may have the empty value is each parent's rule (a select's may, SEL-17; a combobox's may not). | TanreiSelectOption(_ title:, supportingText:, leadingIcon:).tag(value), as the content of a Picker; disabled follows .disabled(_:). | TanreiSelectOption(text, value, supportingText, leadingIcon, enabled), called in the select's content lambda. | TanreiSelectOption<String>(value:, label:, supportingText:, leadingIcon:, enabled:), in the select's options list, as DropdownMenuEntry. | { label, value, supportingText?, leadingIcon?, disabled? }: an element of the options array. |
option-group · group — holds items
A run of related options, optionally titled. The select separates a group from what is next to it by a divider or a gap.
| Property | Type | Default | Description |
|---|---|---|---|
title | string | "" | A short heading drawn above the group's options, which also names the group; empty draws none and leaves the group unnamed. |
| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| Item | tanrei-option-group | TanreiSelectSection | TanreiSelectGroup | TanreiSelectGroup | SelectOptionGroup |
title · Property | label | title | title | title | title |
| Notes | The title is the label attribute, as <optgroup label> and <tanrei-menu-group label>: a title attribute would also draw the browser's tooltip. | TanreiSelectSection(_ title:) { … }, as Section. | TanreiSelectGroup(title) { … }. | TanreiSelectGroup(title:, options: […]), in the same list. | { kind: 'group', title?, options: SelectOption[] } in the same array. |
States
Behavior
Requirements in EARS form. Every platform implementation is tested against these ids.
| ID | Requirement |
|---|---|
SEL-1 | THE select SHALL draw its container at least comp.input-box.stacked.container.height.medium tall WHILE variant is stacked (or a variant a design system adds, which is drawn as a stacked box) and at least comp.input-box.container.height.medium tall WHILE variant is filled or outlined, for size medium (and so on for each size), with a stacked or outlined select's four corners and a filled select's top corners rounded by comp.input-box.container.shape, a filled select's bottom corners by comp.input-box.container.shape-bottom, all eased by sys.shape.corner-smoothing, and SHALL lay out in it one row, centred vertically: the leading icon where there is one (SEL-18), the value or the placeholder (SEL-16) taking the remaining width, and the chevron at the end (SEL-19), comp.input-box.container.gap.medium apart; text SHALL be comp.input-box.container.padding-inline.medium from the container's edge and a glyph comp.input-box.container.padding-inline-with-icon.medium, and the row's tallest item SHALL have at least comp.input-box.stacked.container.padding-block.medium above and below it in a stacked box and comp.input-box.container.padding-block.medium in a filled or an outlined one (and so on for each size). |
SEL-2 | THE select SHALL measure as the text input of the same variant and size measures (TXI-16), at every size: the same container height, paddings and corners, and the same text style and start edge for the value as the text input's text, so that a select and a text input side by side share every horizontal line and a select below a text input shares its start edges. The two are drawn from one set of tokens (SEL-65): what a layer changes of the box, it changes for both. The label row above it and the supporting row below it are the rows of the Field that frames it (FLD-1, FLD-2, FLD-7), which are one frame for every control. |
SEL-7 | WHERE variant is filled or outlined, WHILE the floating label is drawn (SEL-15) and the Field that frames the select gives it an asterisk as its required mark (FLD-25) THE select SHALL draw the character "*" after the label's text, comp.input-box.requirement.asterisk-gap from it, in the label's text style and at the label's place, resting and floated with it, in the required-asterisk color of its variant (comp.input-box.outlined.required-asterisk.color for an outlined select; comp.input-box.outlined.error.required-asterisk.color WHILE the select is in error), and SHALL keep it from assistive technology, because the required state already says it (SEL-11). It SHALL draw no tag and no optional label: which mark a required control gets is the Field's to say (FLD-6), and a tag named there reaches a floating label as the asterisk. WHILE no Field frames it, or the Field gives no mark, or the label is not drawn, THE select SHALL draw no mark. |
SEL-9 | WHILE error is true — set by the product, or by the Field that frames it (FLD-8) — THE select SHALL draw every part of its trigger, in every state, from the error parts of its variant (comp.input-box.stacked.error.outline.color and the other error. parts), so that the select itself is marked and not only a text below it, and SHALL expose itself as invalid to assistive technology; its list and its options SHALL stay as they are drawn. THE select SHALL never set error itself and SHALL show no error text of its own: the words that say what is wrong are the errorText of the Field that frames it, which that Field draws and gives the select as its description (FLD-7, FLD-11). |
SEL-11 | THE select SHALL take its name, its description and its required state from the Field that frames it, as every control a Field frames does (FLD-10, FLD-11): the Field's label is its accessible name and the name of its list (SEL-48), whether or not any label is drawn, and the Field's supporting text with, in error, the error text after it is its description. WHILE label is not empty THE select SHALL use it as the accessible name instead, for a select no Field frames or one whose visible label is elsewhere. It SHALL never use placeholder as the name, WHILE it has either name THE select SHALL expose that one alone, over a name the product gives it on the element (aria-label or aria-labelledby); and WHILE it has neither name and the product gives it a name of its own on the element — aria-label, or aria-labelledby naming an element — THE select SHALL expose that name as its accessible name; IF it has none of the three it SHALL still render and work, and in debug builds SHALL report that it has no accessible name. WHILE its Field says it is required THE select SHALL expose itself as required, whichever mark is drawn and where none is, and on the web SHALL report a missing value to the form's constraint validation WHILE value is the empty string; it has no requirement setting of its own and SHALL NOT validate because of one. WHILE an option's label is empty THE select SHALL still render the option and in debug builds report that it has no accessible name. |
SEL-13 | THE select SHALL add one stop to the focus order, its container, and no other. A press on the label row of the Field that frames it moves focus to the container and opens nothing: that press is the Field's (FLD-12). A label that floats in the container is part of the container, and a press on it is a press on the container (SEL-22, SEL-23). |
SEL-15 | WHERE variant is filled or outlined and the label is drawn THE select SHALL rest the label in the value's line, in comp.input-box.label.typescale.medium for size medium (and so on), WHILE no option is chosen and the select is neither focused nor open, and otherwise float it as the text input of that variant floats its label (TXI-13): to the top of a filled container, or onto an outlined container's top edge with a gap of comp.input-box.outline.notch-padding in the outline on each side of it, in comp.input-box.label.floated-typescale.medium for size medium (and so on); a filled select that draws its label SHALL be at least twice comp.input-box.container.padding-block-with-label.medium plus one line of each of those two text styles tall (and so on for each size), so that it does not change height when the label floats. WHEN the label floats or rests THE select SHALL move and resize it over comp.input-box.motion.label-duration with comp.input-box.motion.label-easing, and at once where motion is reduced. The label SHALL wrap rather than be cut and SHALL never be drawn over the value. The label it floats SHALL be its accessible name (SEL-11) — the label of the Field that frames it, or its own label where none does — in the label color of its variant (comp.input-box.outlined.label.color for an outlined select, its error. part WHILE the select is in error). WHILE labelHidden is true — its own, or that Field's (FLD-25) — or it has no name THE select SHALL draw no label. WHERE variant is stacked, or a variant laid out as stacked (SEL-60), THE select SHALL draw no label at all: the label above it is the Field's row (FLD-2). |
SEL-16 | THE select SHALL show in its container the label of the chosen option (SEL-17), on one line, in comp.input-box.input.typescale.medium for size medium (and so on) and the input color of its variant (comp.input-box.stacked.input.color for a stacked select, its error. part WHILE the select is in error); it SHALL NOT wrap it, and WHEN the container is narrower than the label needs SHALL cut it with an ellipsis, keeping the leading icon, the chevron and the paddings whole, the full label remaining what assistive technology reads. WHILE no option is chosen THE select SHALL show placeholder in the same place and style in the placeholder color of its variant (comp.input-box.stacked.placeholder.color for a stacked select), only while the label is not in the line — a stacked select, whose label is the row of its Field; floated; or not drawn — and nothing WHILE placeholder is empty. THE select SHALL NOT show an option's supporting text in its container. |
SEL-17 | THE select SHALL treat as the chosen option the first option, in order and across groups, whose value equals value, a disabled option and an option whose value is the empty string included; WHILE no option's value equals value THE select SHALL draw itself with nothing chosen, SHALL keep value as the product set it, and in debug builds SHALL report a value that is not empty and names no option. WHEN two options share a value THE select SHALL treat the first as the one both stand for and in debug builds report it. WHEN the options change THE select SHALL find the chosen option again by value and SHALL NOT change value or emit change. |
SEL-18 | WHERE the leading-icon slot holds a glyph THE select SHALL draw it at the start of its container; otherwise, WHILE the chosen option has a leadingIcon, THE select SHALL draw that glyph there; otherwise it SHALL draw none and start the value at the text padding. It SHALL draw the glyph at comp.input-box.icon.size.medium for size medium (and so on), with sys.icon.family.unselected and sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, in the leading-icon color of its variant (comp.input-box.stacked.leading-icon.color for a stacked select, its error. part WHILE the select is in error), and SHALL keep it from assistive technology. The slot's content SHALL be a glyph only: an interactive element there SHALL be reported in debug builds. |
SEL-19 | WHILE readOnly is false THE select SHALL draw at the end of its container the glyph expand_more of the system's icon set, at comp.input-box.icon.size.medium for size medium (and so on), with sys.icon.family.unselected and sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, in the trailing-icon color of its variant (comp.input-box.stacked.trailing-icon.color for a stacked select, its error. part WHILE the select is in error). THE select SHALL draw the same glyph whether the list is closed or open and wherever the list is placed, without turning or exchanging it, SHALL NOT mirror it in a right-to-left layout, SHALL keep it from assistive technology, SHALL NOT make it a target or a focus stop of its own, and SHALL draw no mark of its own in its place. WHILE readOnly is true THE select SHALL draw no chevron, because nothing opens. IF the icon set cannot draw a glyph yet (its font is still loading or failed to load) THE select SHALL draw nothing in that glyph's place, never its name as text, and keep the glyph's box. |
SEL-20 | WHILE hovered or focused THE select SHALL render its container, outline, active indicator, floating label and its asterisk, value, placeholder, leading icon, chevron and opacity, and the widths of its outline and active indicator, from the component tokens of the highest-precedence active state, with precedence focused over hovered; focus gained by pointer or touch SHALL select the focused state, as in a text input (TXI-6), and THE select SHALL be drawn focused for as long as its list is open, and SHALL say so to the Field that frames it, so that the rows of that Field are drawn focused with it (SEL-63, FLD-13). WHILE variant is stacked it SHALL draw no edge at rest (comp.input-box.stacked.outline.color is transparent), SHALL draw its focused outline color and width as a line inside the container's edge that takes no layout space, and WHILE in error SHALL draw that line at rest in comp.input-box.stacked.error.outline.color at comp.input-box.outline.width; a hovered stacked or filled select SHALL show the state layer on its container. WHEN its state changes THE select SHALL animate colors and widths over the state duration of its size (comp.input-box.motion.state-duration.small, .medium or .large) with comp.input-box.motion.state-easing, and SHALL change them at once where motion is reduced. |
SEL-21 | THE select SHALL show focus by its focused tokens — the focused outline or active indicator is its focus indicator — and SHALL NOT draw the focus ring around its container; WHERE sys.behavior.focus-style is glow it SHALL also draw the glow around the container while focused. |
SEL-22 | WHERE the container is shorter than the minimum target THE select SHALL extend its hit area vertically to sys.target.touch for touch input and sys.target.pointer for fine pointers, without changing its visual size; a platform that cannot tell the input type SHALL use sys.target.touch. The whole container — its paddings, the leading icon, the value and the chevron — SHALL be one target. THE select SHALL draw no press answer on its container (no pressed state layer, ripple or dim): a press on it is answered by the list, which opens with the press, and by the focused edge. |
SEL-23 | WHEN the person presses the container by pointer or touch, or presses Enter, Space, Down Arrow or Up Arrow while the select has focus, WHILE the list is closed and the select is neither disabled nor read-only and has something to show — an option, a status row or the empty text (SEL-68) — THE select SHALL take focus if it does not have it, set open to true, emit toggle once with open true, and present its list (SEL-24). The container is the list's anchor and its opener (POP-18): THE select SHALL NOT open or close its list when a pointer enters or leaves, when focus arrives, on a long-press or after a time, and SHALL expose on its container that it opens a list box, whether the list is open and which element it is (aria-haspopup with the value listbox, aria-expanded and aria-controls on the web). THE select SHALL NOT change value by opening, and SHALL NOT change value from the keyboard while its list is closed: the arrow keys and typing (SEL-33) open the list instead, so that a value never changes without the person seeing what it changed to and confirming it. |
SEL-24 | WHILE open is true THE select SHALL present its list in the platform's overlay layer, the one a popover's container and the menu's surface use (POP-1), and SHALL place it against its container as a popover whose placement is bottom-start is placed against its anchor (POP-2, POP-4): comp.select.list.offset below the container with the start edges aligned, its own token in the place of the popover's offset. It SHALL keep the list at least comp.select.list.viewport-margin inside the visible part of the window's safe area, above the on-screen keyboard, by the steps POP-5 gives, in that order — flipping above the container where below has no room and above has, limiting the list to the room where neither has, shifting along the container, and keeping its side while that side has room — SHALL follow the container as it moves or changes size, SHALL place the list again WHEN the on-screen keyboard shows or hides or the visible part of the window otherwise moves or changes size (POP-36), and SHALL take the start edge as the right edge in a right-to-left layout (POP-34). It SHALL stay an anchored list at every window size and SHALL NOT turn into a sheet or a dialog (POP-43): a list of options is the menu's kind of surface, which Tanrei's rule for compact windows leaves beside its control. WHEN the container is removed, hidden or scrolled out of sight so that no part of it can be seen THE select SHALL close the list with the reason anchor-hidden (SEL-27), as POP-5 says of a popover's anchor. |
SEL-25 | THE select SHALL fill its list with comp.select.list.color, raise it by comp.select.list.elevation, round it by comp.select.list.shape eased by sys.shape.corner-smoothing, clip its options to that shape and inset them by comp.select.list.padding-block and comp.select.list.padding-inline. It SHALL make the list as wide as its widest option needs unwrapped, its paddings included — never narrower than its container nor than comp.select.list.min-width, never wider than comp.select.list.max-width or its container where the container is wider, following the container's width as it changes, and never wider than the window's safe area less comp.select.list.viewport-margin on each side — so that a long label or a supporting text reads in fewer lines without the container, and the layout around it, changing width (SEL-49); an option whose text needs more than that width SHALL wrap (SEL-37). A list wider than its container keeps the start edges aligned and shifts along the container to stay inside the window (SEL-24). It SHALL make the list as tall as its options and its paddings, at most comp.select.list.max-height and at most the room its side leaves (POP-8), and SHALL scroll the options beyond that, vertically only, the list's paddings scrolling with them; the list has no header and no actions, so it draws no divider of a scrolling popover. WHEN the list opens with a chosen option THE select SHALL scroll it so that the whole of that option is in view. A long list MAY draw only the rows near its scroll position and the active option with its neighbours, each row it does not draw keeping its height so that every option is reached by scrolling and by the keys; such a list SHALL state each drawn option's position among all the options and their number (SEL-48), and SHALL take its width from the rows drawn as it opens and keep that width while it scrolls, so that the list does not change width under the pointer. |
SEL-26 | WHILE the list is open THE select SHALL keep focus on its container, because the list is an operated overlay whose focus is the field it belongs to, as the search's docked view is: it SHALL NOT move focus into the list when it opens (this is the select's own rule, where a popover moves focus in, POP-19), SHALL NOT trap focus, make anything inert or draw a scrim; a press on an option, on a group title, on the status row or on the list's surface SHALL NOT take focus from the container. One option at a time SHALL be the active option, which the keys act on and assistive technology is told of (SEL-48). |
SEL-27 | WHEN the person presses Escape or the platform's back while the list is open, presses outside the list and the container, moves focus out of the select, or WHEN the container goes out of sight (SEL-24), THE select SHALL close the list, set open to false and emit toggle once with open false and that reason — escape, back, outside, focus-out or anchor-hidden, the popover's reasons (POP-21); and it SHALL do the same with the select's own reasons WHEN the person presses the container while the list is open (press), presses Tab or Shift+Tab (tab), or chooses an option (select, SEL-34). Tab closes the list at once and moves focus on from the container as it would have, as the menu's rule is and not the popover's (POP-20), because the list holds one set of options and nothing a person could tab to. A close for any reason but select SHALL choose nothing, whatever option is active, and value SHALL stay as it is. Focus SHALL stay on the container, or where the person put it WHEN the close came from focus moving to, or a press landing on, another focusable element (POP-22). Focus leaving the window SHALL NOT count as focus leaving the select. Whether a press outside also reaches the element under it SHALL follow the platform and be stated in its README, as for the menu (MNU-5). |
SEL-28 | WHILE the list is open, WHEN the user presses Escape or the platform's back THE select SHALL take the key, close as SEL-27 says, and let it go no further, so that it does not also close a dialog, a sheet or a popover the select stands in (POP-23). WHILE the list is closed THE select SHALL NOT act on Escape and SHALL leave it to whatever holds the select. |
SEL-29 | WHERE the select stands inside a popover or a modal overlay (a dialog, a modal sheet) THE select's list SHALL belong to that overlay as POP-24 and POP-25 say of an overlay opened from inside one: a press in the list is not a press outside it, the list is presented above it and is operable while the rest of the window is inert, and the list closes with it. THE select SHALL open no overlay from inside its list. |
SEL-30 | WHEN the list opens THE select SHALL make it appear as a popover's container appears (POP-26) — from where that appearance starts it, grown from the middle of the edge that faces the container — over comp.select.motion.open-duration with comp.select.motion.open-easing, and WHEN it closes take it back the same way over comp.select.motion.close-duration with comp.select.motion.close-easing; WHERE motion is reduced it SHALL only fade in and out, over sys.motion.role.reduced. WHEN it is told to close while still opening, or to open while still closing, it SHALL turn round from where it is, and WHILE closed or closing it SHALL take no pointer input (POP-27). The value and the open state SHALL change when the list closes, not when its motion ends. |
SEL-31 | WHEN the list opens THE select SHALL make the chosen option the active one, or the first option that is not disabled WHILE none is chosen. WHILE the list is open THE select SHALL make active the next option with Down Arrow and the previous one with Up Arrow, in order and across groups, stopping at the last and at the first without wrapping; the first and the last option with Home and End; and the option ten after or ten before, or the last or the first where fewer remain, with Page Down and Page Up. A disabled option SHALL be reached like any other, so that people learn it exists; a group title, a divider and the status row are never active, and WHILE the list holds no option no option is active and Enter and Space choose nothing. Making an option active SHALL NOT change value. WHEN an option becomes active while part of it is scrolled out of view THE select SHALL scroll it into view. |
SEL-32 | WHILE an option is the active one by the keyboard THE select SHALL render it in the focused state and show the focus ring inside its row, which the list would otherwise clip, without animation; WHEN a pointer moves onto an option THE select SHALL make that option the active one, render it hovered and show no focus ring, so that the arrow keys continue from it; WHEN the pointer leaves the list the option SHALL stay active. |
SEL-33 | WHEN the person types printable characters while the select has focus, each within one second of the one before, THE select SHALL treat them as one string and make active the next option after the active one, wrapping to the first, whose label begins with that string, compared without regard to case as the label's language compares; a string of one character typed again SHALL move on to the next option that begins with it. WHILE the list is closed the first character SHALL open it (SEL-23) and then do the same. Typing SHALL NOT change value, and a Space typed within that second SHALL be a character of the string and SHALL NOT choose. |
SEL-34 | WHEN the person presses an option that is not disabled by pointer or touch, or presses Enter or Space while such an option is the active one, THE select SHALL, WHEN that option's value differs from value, set value to it, show its label in the container and emit change once with the new value; it SHALL then close the list with the reason select (SEL-27), so that toggle follows change. Choosing the option that is already chosen SHALL emit no change and still close the list. THE select SHALL NOT emit change when the product sets value, and SHALL have no way of its own to return to nothing chosen: a product that allows no choice gives an option that says so. |
SEL-35 | WHILE an option's disabled is true THE select SHALL NOT choose it by any input, SHALL leave the list open when it is pressed or when Enter or Space is pressed on it, SHALL expose its disabled state to assistive technology, and SHALL render its container, label, supporting text, leading icon, mark and opacity from the disabled component tokens of its condition. A disabled option whose value equals value is still the chosen one: the container shows its label, and its row draws the fill and the check from the disabled tokens. |
SEL-36 | WHEN the product sets open THE select SHALL show or remove its list without emitting toggle; WHEN the product sets value THE select SHALL show the chosen option's label and SHALL NOT emit change or toggle, and WHILE the list is open SHALL move the fill and the check to the new chosen option and leave the active option where it is. WHEN the options change while the list is open THE select SHALL keep the active option where an option of the same value still exists and otherwise make active the option SEL-31 names for an opening list. WHEN status, statusText or emptyText change while the list is open THE select SHALL draw the status row as it then is (SEL-68) and keep the list open. WHEN the select becomes disabled or read-only, or is left with nothing to show (SEL-61), while its list is open THE select SHALL close the list without emitting toggle. |
SEL-37 | THE select SHALL lay out an option as one row: comp.select.option.padding-inline.regular from each edge, the leading icon column (as SEL-66 says), the label with the supporting text below it taking the remaining width, and at the end a box of side comp.select.option.mark.size.regular for the check (SEL-39), each comp.select.option.gap.regular from the next; the box of the check SHALL keep its width in every row so that labels wrap alike. It SHALL make the row at least comp.select.option.height.regular tall, and at least sys.target.touch tall for touch input, in both densities, and for fine pointers at least sys.target.pointer tall WHILE density is regular and as tall as its density gives WHILE density is compact (SEL-70), because rows that touch cannot extend a hit area over each other (a platform that cannot tell the input type SHALL use sys.target.touch); pad its content by comp.select.option.padding-block.regular above and below; draw the label in comp.select.option.label.typescale and the supporting text in comp.select.option.supporting-text.typescale; and wrap text and grow the row, with long labels and with enlarged text, rather than cut anything in the list. The whole row SHALL be the option's hit area. |
SEL-38 | THE select SHALL draw an option from the condition it is in — selected-option for the chosen option, option for every other — and from the state it is in, with precedence pressed over focused over hovered: its row filled with the container color of that condition and state within the corners comp.select.option.shape gives, eased by sys.shape.corner-smoothing, over comp.select.list.color and under the row's content; its label, supporting text and leading icon in that condition's colors (comp.select.stacked.option.label.color, comp.select.stacked.selected-option.label.color and so on for a stacked select). The option parts hold the same values under every variant: a list does not change with the look of its field. WHEN the colors of an option change between states THE select SHALL animate them over comp.select.motion.state-duration with comp.select.motion.state-easing, and SHALL change them at once where motion is reduced. |
SEL-39 | WHILE an option is the chosen one THE select SHALL fill its row with the selected-option container color of its state (comp.select.stacked.selected-option.container.color at rest for a stacked select) and SHALL draw the glyph check of the system's icon set centred in the box at the end of the row, in the mark color of that condition and state (comp.select.stacked.selected-option.mark.color at rest), with sys.icon.family.selected, sys.icon.fill.selected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max; it SHALL draw nothing in that box for any other option, SHALL NOT mirror the check in a right-to-left layout, and SHALL draw no mark of its own (no path, shape or character of its own for the check). THE select SHALL NOT show the choice by the fill alone: the check is always drawn with it, and a design system whose fill is none still marks the chosen option. WHEN the chosen option changes while the list is open THE select SHALL fade the check in or out over comp.select.motion.state-duration with comp.select.motion.state-easing without moving or scaling it. |
SEL-40 | THE select SHALL draw an option's leadingIcon at comp.select.option.leading-icon.size.regular through the icon helper, with the family and fill of the option's condition (sys.icon.family.selected and sys.icon.fill.selected for the chosen option, otherwise sys.icon.family.unselected and sys.icon.fill.unselected), sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, in the leading-icon color of its condition and state, hidden from assistive technology; an empty name SHALL draw nothing. |
SEL-41 | WHEN the option is pressed by pointer, touch, Enter or Space THE option SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner. WHEN that press chooses the option and closes the list (SEL-34) THE select SHALL close the list at once and SHALL NOT hold it open to show the pressed state: the pressed state is then seen only while the list closes, and the choice is shown by the new value in the container. |
SEL-42 | WHILE pressed THE option SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole control and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; label, supporting text, leading icon, mark, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.select.motion.feedback-duration with comp.select.motion.state-easing, fades over comp.select.motion.state-duration with comp.select.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. |
SEL-43 | THE option SHALL ease its container corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show its keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. |
SEL-44 | WHILE a group's title is not empty THE select SHALL draw it above the group's options in comp.select.group-title.typescale and comp.select.group-title.color, starting comp.select.group-title.padding-inline.regular from the rows' start edge and ending comp.select.group-title.padding-inline.regular from their end edge, with comp.select.option.padding-block.regular above and below, wrapping and growing with enlarged text rather than clipping. The title SHALL take no focus, SHALL never be the active option, SHALL NOT be reached by typing and SHALL scroll with its options. WHEN a group holds another group THE select SHALL draw the inner group's options as the outer group's, with no title or separation of the inner group's own, and in debug builds report it. |
SEL-45 | THE select SHALL separate a group from an adjacent group or an adjacent option outside any group as its separation names: WHILE separation is auto, as comp.select.separation names, which follows sys.behavior.separation unless a design system sets it apart; WHILE separation is divider or gap, as that value names, whatever the tokens say. Under divider it SHALL draw a line of comp.select.divider.thickness in comp.select.divider.color across the list's inner width, inside comp.select.list.padding-inline on each side, with comp.select.divider.space above and below it, exposed as a separator that takes no focus; under gap it SHALL leave comp.select.group.gap of empty space, draw no line and expose no separator. It SHALL draw neither before the first nor after the last element of the list. WHEN separation names a value the select does not define THE select SHALL treat it as auto and in debug builds report the unknown value. A platform that cannot draw a choice SHALL fall back to the first value, divider, and state that in its README. |
SEL-46 | WHILE disabled is true — set by the product, or by the Field that frames it (FLD-14) — THE select SHALL NOT be focusable, SHALL NOT open, SHALL NOT emit change or toggle, SHALL expose the disabled state to assistive technology, SHALL be left out of form submission on the web, and SHALL render every part of its trigger, the chevron, the floating label and its asterisk included, from the disabled component tokens of its variant (of its error parts WHILE error is true). |
SEL-47 | WHILE readOnly is true and disabled is false THE select SHALL stay focusable and show its value, SHALL NOT open by any input and SHALL NOT let the person change the value, SHALL NOT emit change or toggle, SHALL expose the read-only state to assistive technology, SHALL be submitted with its form on the web, and SHALL render as an enabled select without its chevron (SEL-19), including its hovered and focused states. |
SEL-48 | THE select SHALL expose its container, on the web, as a combobox that is not editable: named by label (SEL-11), with the chosen option's label as its value (the placeholder is not a value), and WHILE the list is open aria-activedescendant naming the active option; and the list as a listbox named by label whose options are named by their label and described by their supporting text, each with its selected state — true for the chosen option alone — its disabled state, its position and the number of options, counted among all the options also where a long list draws only some of their rows (SEL-25), and whose groups are groups named by their title. On the other platforms THE select SHALL expose the container as the platform's control that opens a list of choices (a pop-up button, a drop-down list, a combo box) named by label with the chosen option's label as its value and its expanded state, and the options as selectable items in a list with the same names, states and positions. Icons, the chevron, the check and the divider SHALL be hidden from assistive technology. |
SEL-49 | WHILE no Field frames it and the product gives it no width THE select SHALL be as wide as its widest option would make it — the widest option label in the value's text style, with the leading icon column where the select or any option has a leading icon, the chevron, the gaps and the paddings — so that choosing an option never changes its width, and at least as wide as its placeholder needs; it SHALL never be wider than its layout gives it, cutting the value instead (SEL-16), and WHERE the product gives it a width it SHALL fill that width. WHILE a Field frames it THE select SHALL fill the width that Field gives it (FLD-1), as a text input does, so that a field of a form is as wide as its column whatever control it holds; the width of such a pair is given to the Field. |
SEL-51 | WHILE the text size is enlarged THE select SHALL grow its container with the value's line, keeping the height of its size as a minimum and its glyphs at their sizes, SHALL keep the value on one line, SHALL wrap its options, its group titles and the status row's text rather than cut them, and SHALL scroll its list where the taller options no longer fit (SEL-25), so that text enlarged to 200% loses no content in the list and none but a cut value in the container, which the list and assistive technology still give in full. |
SEL-52 | WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE select SHALL draw its floating label, that label's asterisk and the status row's text, in error too, in the platform's text color, and SHALL rely on no color of its own to show an error: the error is carried by the invalid state (SEL-9) and by the words the Field that frames it shows, in that Field's own rows (FLD-17). |
SEL-53 | WHERE the platform draws with a person's own forced colors THE select SHALL draw an edge comp.input-box.outline.width wide in the platform's control text color around its container in every variant, that edge in the platform's highlight color at the focused width while focused, an edge sys.border.hairline wide in the platform's text color round its list, as a popover's container has (POP-32), the active option in the platform's highlight colors, and a disabled select and a disabled option in the platform's disabled text color; the chosen option SHALL then be shown by its check alone. |
SEL-55 | WHERE the layout direction is right-to-left THE select SHALL mirror its container and its list: the leading icon at the right of the container and the chevron at the left, a floating label from the right edge with its asterisk at its left, the list aligned to the container's right edge (SEL-24), and in each option the leading icon at the right and the check at the left, all paddings applied by logical start and end. The rows of the Field that frames it mirror by FLD-18. |
SEL-56 | THE select SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the select is shown THE select SHALL draw itself for the new direction at once, without a motion of its own, keeping its value, its open state, its active option and its focus, the list placed again by the new direction (SEL-24). |
SEL-57 | WHERE the layout direction is right-to-left THE select SHALL draw the glyph of its leading-icon slot and an option's leadingIcon mirrored left to right exactly when the system's icon set marks that glyph's name as mirrored or the product forces mirroring for that use, and unmirrored when the product forbids it: the icon helper mirrors the glyph inside its own box, about the box's vertical centre line, changing neither its place, size, color and fill nor the order SEL-55 gives. THE select SHALL NOT mirror a glyph itself, and SHALL NOT mirror content the product gives as an image or a view of its own rather than by a glyph's name. The chevron's expand_more and the check are not marked and are never mirrored. The glyph before the label of the Field that frames it is that Field's (FLD-20). |
SEL-58 | THE select SHALL lay out its floating label as text of its own direction, isolated from the text around it: it takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right word, a number with its sign or unit, a telephone number and an address keep their own order in a right-to-left layout and reorder nothing beside them; THE select SHALL still place it and align its lines by the layout direction, not by the text's own. The texts of the rows of the Field that frames it are laid out by FLD-21. |
SEL-59 | THE select SHALL lay out its placeholder, the value it shows, an option's label and supporting text, a group's title and the status row's text in the same way (SEL-58): each as text of its own direction, isolated, placed and aligned by the layout direction; and SHALL cut a value that does not fit (SEL-16) at the end of its own direction, so that a left-to-right value in a right-to-left layout loses its right end and never its beginning. |
SEL-60 | WHEN variant or size names a value that the active design system does not have THE select SHALL render with the default value's tokens, and in debug builds report the unknown value; WHEN variant names a value the active system has but the select does not know by name (one a layer added) it SHALL draw no label and draw its edge as a stacked select does (SEL-15, SEL-20), with that value's own tokens. WHEN status names a value the select does not define THE select SHALL behave as idle and in debug builds report it. |
SEL-61 | WHILE the select has nothing to show — it holds no option, status is idle, statusText is empty and it has no empty text (SEL-68) — THE select SHALL draw its trigger as it is, SHALL stay focusable, SHALL NOT open by any input or because open is true, and in debug builds SHALL report a select without options; it SHALL draw no empty list. WHILE it holds no option and has a status row or an empty text it SHALL open as any select does (SEL-23), its list holding that row alone. |
SEL-62 | THE select SHALL have no words of its own: it shows its options' labels, its placeholder, its name and the status row's statusText, each given by the product, and SHALL take its empty text from the instance's emptyText and, WHILE that is empty, from the product's words of the name noOptionsText — the words a product sets once for all its components — and from nowhere else. WHILE neither gives it, the select draws no empty text (SEL-61) and, WHEN the person tries to open a select that holds nothing to show, reports once in debug builds that the words are missing. The words of a requirement are read by the Field that frames it (FLD-24). |
SEL-63 | THE select SHALL say to the Field that frames it what only the select knows, and SHALL say it again WHEN it changes: that it draws the label itself WHILE its variant floats one (SEL-15), so that the Field draws no label row (FLD-25); how far its value stands from its own start edge — its inline padding and, beside a leading icon, that icon and its gap (SEL-1, SEL-18) — and the padding it keeps at its end edge (SEL-1), so that the Field's supporting row starts and ends there (FLD-26); and that it is drawn focused WHILE its list is open (SEL-20), so that the Field draws its rows from their focused tokens for as long, whether or not the select has focus (FLD-13). It SHALL take from that Field its name, its description and its required, invalid and disabled states (SEL-9, SEL-11, SEL-46), whether the label is hidden (SEL-15) and whether an asterisk follows it (SEL-7), so that a Field and a select placed by hand are drawn, named, described and marked exactly as a select field is. THE select SHALL keep no count and report no refusal of an entry (FLD-27, FLD-28): it holds only what its options offer. |
SEL-64 | THE select SHALL draw no label row above itself, no requirement tag, no help and no error text: those are the rows of the Field that frames it (FLD-2 to FLD-7). WHILE no Field frames it THE select SHALL be complete as a bare control — its container, the label that floats in it where its variant floats one, the value it shows, its list and its options, its states and its events — named by its own label, and exactly as tall as its container. |
SEL-65 | THE select SHALL draw its box from the one set of tokens every input box is drawn from, the group comp.input-box (comp.input-box.container.shape, comp.input-box.container.height.medium, and so on): its container, its corners, its edges and their widths, the label that floats in it and the asterisk after it, the line that shows the chosen value and the placeholder, the leading icon and the chevron, the paddings, the gaps and the least height of its size (a stacked box's from comp.input-box.stacked.container.height.medium and comp.input-box.stacked.container.padding-block.medium for size medium, as tall as a Button of the same size, and a filled or an outlined box's from comp.input-box.container.height.medium and comp.input-box.container.padding-block.medium, and so on) — small included, and the colors of each of these in its look, at rest, hovered, focused and disabled, plain and in error. It SHALL have no token of its own for any of them: the tokens of its own group are the list's — its surface, its options and their states, its groups, its motion (SEL-25, SEL-37 to SEL-42). WHEN a design system or a product sets a token of the box THE select SHALL follow it, as the text input, the combobox, the number input and the color input do, which read the same tokens; no token is held equal to another for this. WHERE one select is to differ from the other boxes of a product, the token is set for that instance — on its element or on a subtree that holds it, in each platform's way of setting a token for a subtree — and THE select SHALL draw itself from the value it finds there. |
SEL-66 | THE select SHALL decide the leading icon column for each section of its list — an option group, with the options of a group inside it (SEL-44), or a run of adjacent options outside any group: the parts SEL-45 separates. WHILE iconSpace is auto an option SHALL keep the column, empty where it has no icon, WHERE an option of its section has a leading icon, so that the labels of a section start on one line, and a section where no option has one SHALL keep no column, its labels starting at the row's content start; WHILE iconSpace is always every option SHALL keep the column, whether or not any option of the list has an icon; WHILE iconSpace is never an option SHALL have the column only where it has a leading icon itself. The column kept is comp.select.option.leading-icon.size.regular wide with comp.select.option.gap.regular after it, draws nothing and is nothing to assistive technology. A group's title SHALL keep its own place whatever the column of its section is (SEL-44). WHEN an option is added or removed, or an option's leadingIcon changes, THE select SHALL decide its section again; WHEN iconSpace names a value the select does not define THE select SHALL treat it as auto and in debug builds report the unknown value. |
SEL-68 | WHILE the list is open and statusText is not empty or status is loading THE select SHALL draw, after the last option, a status row, outside the region that scrolls, under it and inside the list's surface, so that the region that scrolls is the list box the keys move through and the row stays in view while the options scroll (SEL-25): inset by comp.select.option.padding-inline.regular at the sides and padded by comp.select.option.padding-block.regular above and below, taking no input, no state and no focus, never the active option and never reached by typing (SEL-33). WHILE status is idle it SHALL draw statusText, wrapping, in comp.select.status.typescale and comp.select.status.color; WHILE status is error it SHALL draw it in comp.select.status.error.color; WHILE status is loading it SHALL draw, in the text's place and at the row's start, one Tanrei loading indicator of size small named by statusText, which waits its own show delay and enters as the loading indicator does (LDG-11 to LDG-14), and no text, and the row SHALL keep the height of one line of text whether or not the indicator is shown yet. The options the product leaves in the list WHILE status is loading or error SHALL stay drawn and choosable. WHILE the select holds no option, status is idle and statusText is empty THE select SHALL draw its empty text — emptyText, else the product's words (SEL-62) — in the status row's place and style, so that a person who opens a list with nothing in it is told why instead of facing a select that does not open; WHILE neither gives it the select has nothing to show (SEL-61). The row and the empty text are the combobox's (CMB-32) with the select's own tokens, which hold the combobox's values in every shipped design system; a select filters nothing, so its list is empty only while it holds no option. |
SEL-69 | THE select SHALL expose the status row as a live region that is present while the list is shown and holds nothing while it is closed: WHEN the list has been shown, and WHILE it is open WHEN statusText changes or the empty text appears, THE select SHALL have assistive technology read the row's text — politely, after what it is saying, WHILE status is idle (the status role on the web), and at once WHILE status is error (the alert role) — so that what the row already says as the list opens is read as well as what it comes to say, focus staying on the container (SEL-26). WHILE status is loading it SHALL announce nothing — the loading indicator does not announce itself (LDG-6) — and SHALL mark the list box as busy (aria-busy on the web) until status leaves loading. THE select SHALL NOT compose, count or translate a text: the number of options is already exposed by the list (SEL-48), and the words are the product's. |
SEL-70 | THE select SHALL draw the options and the group titles of its list at its density. WHILE density is regular they SHALL take the regular value of every token of the density axis this spec names (comp.select.option.height.regular, comp.select.option.padding-block.regular, comp.select.option.padding-inline.regular, comp.select.option.gap.regular, comp.select.option.leading-icon.size.regular, comp.select.option.mark.size.regular, comp.select.group-title.padding-inline.regular); WHILE density is compact they SHALL take the compact value of each in its place (comp.select.option.height.compact, comp.select.option.padding-block.compact, comp.select.option.padding-inline.compact, comp.select.option.gap.compact, comp.select.option.leading-icon.size.compact, comp.select.option.mark.size.compact, comp.select.group-title.padding-inline.compact), so that a compact option is lower, set in less from its edges and draws smaller glyphs, the room kept for an empty leading icon column (SEL-66) shrinking with them. The text styles, the list's surface and its paddings, the dividers, the group gap and the status row SHALL be the same in both, and so SHALL the field's box. WHEN density names a value the active design system does not have THE select SHALL draw the default density's rows and in debug builds report the unknown value; a density a design system added SHALL be drawn from that density's tokens, as the core values are. WHEN density changes while the list is open THE select SHALL draw the new rows at once, without a motion, keeping the active option. |
SEL-71 | WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE select SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on the element that carries its role — an id reference by the elements it names in the host's tree or a tree around it, an error message as part of the description while the select is invalid — except those the select owns (aria-haspopup, aria-expanded, aria-controls, aria-disabled), which it SHALL move and not expose. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE select SHALL take it off that element. WHEN the product sets an aria- attribute the select owns THE select SHALL report it in debug builds. |
Platform name mapping
Parity does not mean identical APIs. Each platform uses its own idiom for the same contract.
| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| Component | tanrei-select | TanreiSelect | TanreiSelect | TanreiSelect | Select |
variant · Property | variant | variant | variant | variant | variant |
size · Property | size | size | size | size | size |
value · Property | value | selection | value | value | value |
label · Property | label | label | label | labelText | label |
labelHidden · Property | labelHidden | labelsHidden | labelHidden | labelHidden | labelHidden |
placeholder · Property | placeholder | prompt | placeholder | hintText | placeholder |
error · Property | error | error | isError | isError | error |
disabled · Property | disabled | disabled | enabled | enabled | disabled |
readOnly · Property | readOnly | readOnly | readOnly | readOnly | readOnly |
open · Property | open | isPresented | expanded | controller | open |
separation · Property | separation | separation | separation | separation | separation |
iconSpace · Property | iconSpace | iconSpace | iconSpace | iconSpace | iconSpace |
density · Property | density | density | density | density | density |
status · Property | status | status | status | status | status |
statusText · Property | statusText | statusText | statusText | statusText | statusText |
emptyText · Property | emptyText | emptyText | emptyText | emptyText | emptyText |
change · Event | change | selection | onValueChange | onChanged | onValueChange |
toggle · Event | toggle | isPresented | onExpandedChange | onOpenChanged | onOpenChange |
| Notes | The tag tanrei-select, the class TanreiSelect and the React name Select are the bare control, without the frame. Lagging: the element still draws the label row, the requirement mark and the supporting row itself. A form-associated custom element (static formAssociated = true): it takes name, submits value, restores it on navigation (formStateRestoreCallback), resets to the value it was first given, follows <fieldset disabled> and reports valueMissing while the Field that frames it says it is required and value is empty. Its two calls on ElementInternals and its form go through the shared field primitives, so that a select field can repeat them. The trigger is a <div role="combobox" tabindex="0"> in the shadow root carrying the name, the description, aria-invalid, aria-required, aria-readonly, aria-haspopup="listbox", aria-expanded, aria-controls and aria-activedescendant — set on that element, not through ElementInternals, which axe cannot see. The name and the description are values the Field gives (tanrei-field-connect, src/primitives/field.ts), written into the select's own shadow root, the select's own label winning; the connection runs in both directions (SEL-63): the control's side says drawsLabel, inset: { start, end } (lengths the select writes from its own tokens and its measured icon box) and drawnFocused (true while the list is open), and takes labelHidden and requiredMark (asterisk or none) beside the name, the description and the three states. A select that a Field frames is a block that fills the Field's width (SEL-49); a bare one is an inline box as wide as its widest option, measured from a hidden grid cell that stacks every option's label in the value's style. The options are <tanrei-option> and <tanrei-option-group> elements, read as data and never drawn in place: the select renders the rows itself in its shadow root, so that the combobox, its aria-controls listbox and its aria-activedescendant option are in one tree. It reads them from its own children, and — in src/primitives/option-list.ts, for the combobox too — through any <slot> among its children: the elements assigned to that slot, flattened, stand in the slot's place, in order. That is how a select field's options, which are the product's children of <tanrei-select-field>, reach the select inside it, and how a product's own element can pass options on. Changes are observed on the children, on each such slot (slotchange) and on each element assigned through one. The list is a manual popover in the top layer, placed with CSS anchor positioning and a script fallback and followed with the FollowController the menu uses, with the select's own light dismiss so that toggle carries a reason; a press outside also reaches the element under it. The select sets its own value before firing change (a composed, bubbling Event, as a native select's) and its own open before firing toggle, a CustomEvent whose detail holds open and reason — which is composed and not bubbling (the shipped element's is not yet): composed so that it is heard on a select field, not bubbling so that an ancestor's own toggle listener, a <details>'s, never hears a select's. Attributes: label-hidden, readonly, the rest as the properties. The leading-icon slot is a named slot. Parts: container, outline, active-indicator, label (the floating label), requirement-label (its asterisk), value, placeholder, leading-icon, chevron, list, option, option-label, option-supporting-text, option-leading-icon, mark, group, group-title, divider, status. The status row (SEL-68, SEL-69) is a <div part="status"> after the list box, outside it and outside the box that scrolls; its text is drawn in a span kept from assistive technology, and two visually hidden spans, role="status" and role="alert", are its live regions, always in the shadow root and given the text one animation frame after the list is shown (empty while it is closed), so that what the row already says as the list opens is read. While loading the list box has aria-busy="true" and the row holds one <tanrei-loading-indicator size="small"> named by status-text. empty-text left empty is read from the product's words (setWords({ noOptionsText })). The inline attribute, for tests and fixtures, draws the open list in place below the trigger. What belongs to the frame and not to the element: the attributes supporting-text, error-text, required, optional, required-mark, required-text, optional-text, the label-icon slot, the parts label-icon and supporting-text, and the label row of the stacked look. In a debug build the element reports any of those six attributes, or a child for label-icon, once, and names tanrei-select-field: nothing is kept for them in a production build, and no alias exists. Before the element is defined its options are running text in the page; what a server-rendered page shows then is the product's to guard today: hide tanrei-select:not(:defined) > * and reserve the block size of the select's size, and where the page must work without script put a native <select> or links in a <noscript> beside it. The native <select> is not used inside: its list cannot be drawn with the system's tokens, groups, glyphs and supporting text. The chevron is the icon helper's expand_more and the check its check. The element has no words and reads none. | TanreiSelect(_ label:, selection: Binding<String>) { TanreiSelectOption("Japan").tag("jp") … }, shaped as Picker: the platform's control of this kind, whose call site and whose accessibility a product expects. SwiftUI's own Picker and Menu are not used to draw it, for the reason TanreiMenu gives: their lists cannot be drawn with the system's tokens. The trigger is Tanrei's own box; the list is presented as TanreiMenu presents its surface (.popover with presentationCompactAdaptation(.popover), so it stays anchored on iPhone). Writing the binding is change; isPresented is an optional binding, and writing it is toggle. labelHidden follows .labelsHidden(), disabled follows .disabled(_:). The trigger is one accessibility element with the button trait, the label as its label, the chosen option's label as its value and a pop-up hint; the options are buttons with the selected trait. A press outside does not reach what is under the list. With a hardware keyboard the arrow keys move the highlight and Return chooses, focus staying on the trigger. The bare control: label names it where no Field frames it and floats in a filled or outlined look; in a form it stands in TanreiField, which is TanreiSelectField. | Compose idiom, as an exposed dropdown menu (ExposedDropdownMenuBox with a read-only field): TanreiSelect(value, onValueChange, label) { TanreiSelectOption("Japan", "jp") … }; the caller keeps value (state hoisting). expanded and onExpandedChange are optional: left out, the select keeps its own open state. enabled is the inverse flag and isError keeps Compose's name. The trigger is Tanrei's own box with Role.DropdownList, the label as its content description, the chosen label as its state description and the expanded state; the list is shown in a Popup as TanreiMenu's is, its options selectable with their selected state inside a selectableGroup. Material's own exposed dropdown is not used to draw it: its field and its menu take Material's tokens. A press outside also reaches what is under the list. The bare control: label names it where no Field frames it; in a form it stands in TanreiField, which is TanreiSelectField. | Flutter idiom, as DropdownMenu and DropdownButtonFormField: TanreiSelect(value:, onChanged:, labelText:, options: […]), with hintText and enabled keeping InputDecoration's names, enabled the inverse flag. The list is shown in the Overlay with OverlayPortal, placed as TanreiMenuAnchor places its surface; an optional TanreiSelectController opens and closes it, and onOpenChanged is toggle. Semantics: the trigger is a button with expanded, the label as its label and the chosen label as its value; options carry selected and their index in the set. A press outside also reaches what is under the list. The bare control: labelText names it where no Field frames it; in a form it stands in TanreiField, which is TanreiSelectField. | <Select label="Country" value={value} onValueChange={setValue} options={[…]} />: options is an array of options and groups, as Menu's items. open and onOpenChange are optional: left out, the select keeps its own open state. React Native has no select of its own on both platforms, so the list is Tanrei's on the overlay layer that TanreiProvider renders, placed by measuring the trigger, as Menu is. The trigger has accessibilityRole="combobox", accessibilityState={{ expanded, disabled }}, the label as accessibilityLabel and the chosen label as accessibilityValue.text; options are pressable rows with accessibilityState={{ selected, disabled }} and their position among the options in the label. Follows the app's direction where an ancestor's is not known. A press outside does not reach what is under the list. The bare control: label names it where no Field frames it; in a form it stands in Field, which is SelectField. |
Tokens
| Token | Aliases | Resolved (light) |
|---|---|---|
--tanrei-comp-select-disabled-opacity | — | 1 |
--tanrei-comp-select-divider-color | sys.color.outline-variant | |
--tanrei-comp-select-divider-space | sys.space.space50 | 4px |
--tanrei-comp-select-divider-thickness | sys.border.hairline | 1px |
--tanrei-comp-select-filled-disabled-option-container-color | — | |
--tanrei-comp-select-filled-disabled-option-label-color | — | |
--tanrei-comp-select-filled-disabled-option-leading-icon-color | — | |
--tanrei-comp-select-filled-disabled-option-state-layer-color | — | |
--tanrei-comp-select-filled-disabled-option-supporting-text-color | — | |
--tanrei-comp-select-filled-disabled-selected-option-container-color | — | |
--tanrei-comp-select-filled-disabled-selected-option-label-color | — | |
--tanrei-comp-select-filled-disabled-selected-option-leading-icon-color | — | |
--tanrei-comp-select-filled-disabled-selected-option-mark-color | — | |
--tanrei-comp-select-filled-disabled-selected-option-state-layer-color | — | |
--tanrei-comp-select-filled-disabled-selected-option-supporting-text-color | — | |
--tanrei-comp-select-filled-focused-option-container-color | — | |
--tanrei-comp-select-filled-focused-option-label-color | — | |
--tanrei-comp-select-filled-focused-option-leading-icon-color | — | |
--tanrei-comp-select-filled-focused-option-state-layer-color | — | |
--tanrei-comp-select-filled-focused-option-supporting-text-color | — | |
--tanrei-comp-select-filled-focused-selected-option-container-color | — | |
--tanrei-comp-select-filled-focused-selected-option-label-color | — | |
--tanrei-comp-select-filled-focused-selected-option-leading-icon-color | — | |
--tanrei-comp-select-filled-focused-selected-option-mark-color | — | |
--tanrei-comp-select-filled-focused-selected-option-state-layer-color | — | |
--tanrei-comp-select-filled-focused-selected-option-supporting-text-color | — | |
--tanrei-comp-select-filled-hovered-option-container-color | — | |
--tanrei-comp-select-filled-hovered-option-label-color | — | |
--tanrei-comp-select-filled-hovered-option-leading-icon-color | — | |
--tanrei-comp-select-filled-hovered-option-state-layer-color | — | |
--tanrei-comp-select-filled-hovered-option-supporting-text-color | — | |
--tanrei-comp-select-filled-hovered-selected-option-container-color | — | |
--tanrei-comp-select-filled-hovered-selected-option-label-color | — | |
--tanrei-comp-select-filled-hovered-selected-option-leading-icon-color | — | |
--tanrei-comp-select-filled-hovered-selected-option-mark-color | — | |
--tanrei-comp-select-filled-hovered-selected-option-state-layer-color | — | |
--tanrei-comp-select-filled-hovered-selected-option-supporting-text-color | — | |
--tanrei-comp-select-filled-option-container-color | — | |
--tanrei-comp-select-filled-option-label-color | sys.color.on-surface | |
--tanrei-comp-select-filled-option-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-select-filled-option-state-layer-color | comp.select.filled.option.label.color | |
--tanrei-comp-select-filled-option-supporting-text-color | sys.color.on-surface-variant | |
--tanrei-comp-select-filled-pressed-option-container-color | — | |
--tanrei-comp-select-filled-pressed-option-label-color | — | |
--tanrei-comp-select-filled-pressed-option-leading-icon-color | — | |
--tanrei-comp-select-filled-pressed-option-state-layer-color | — | |
--tanrei-comp-select-filled-pressed-option-supporting-text-color | — | |
--tanrei-comp-select-filled-pressed-selected-option-container-color | — | |
--tanrei-comp-select-filled-pressed-selected-option-label-color | — | |
--tanrei-comp-select-filled-pressed-selected-option-leading-icon-color | — | |
--tanrei-comp-select-filled-pressed-selected-option-mark-color | — | |
--tanrei-comp-select-filled-pressed-selected-option-state-layer-color | — | |
--tanrei-comp-select-filled-pressed-selected-option-supporting-text-color | — | |
--tanrei-comp-select-filled-selected-option-container-color | sys.color.secondary-container | |
--tanrei-comp-select-filled-selected-option-label-color | sys.color.on-secondary-container | |
--tanrei-comp-select-filled-selected-option-leading-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-select-filled-selected-option-mark-color | sys.color.primary | |
--tanrei-comp-select-filled-selected-option-state-layer-color | comp.select.filled.selected-option.label.color | |
--tanrei-comp-select-filled-selected-option-supporting-text-color | sys.color.on-secondary-container | |
--tanrei-comp-select-focused-opacity | — | 1 |
--tanrei-comp-select-group-title-color | sys.color.on-surface-variant | |
--tanrei-comp-select-group-title-padding-inline-compact | comp.select.option.padding-inline.compact | 8px |
--tanrei-comp-select-group-title-padding-inline-regular | comp.select.option.padding-inline.regular | 12px |
--tanrei-comp-select-group-gap | sys.space.space200 | 16px |
--tanrei-comp-select-hovered-opacity | — | 1 |
--tanrei-comp-select-list-color | sys.color.surface-bright | |
--tanrei-comp-select-list-elevation | sys.elevation.level2 | 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 2px 0px rgb(0 0 0 / 0.04) |
--tanrei-comp-select-list-max-height | — | 320px |
--tanrei-comp-select-list-max-width | — | 320px |
--tanrei-comp-select-list-min-width | — | 160px |
--tanrei-comp-select-list-offset | sys.space.space50 | 4px |
--tanrei-comp-select-list-padding-block | sys.space.space50 | 4px |
--tanrei-comp-select-list-padding-inline | sys.space.space50 | 4px |
--tanrei-comp-select-list-shape | sys.shape.corner.medium | 8px |
--tanrei-comp-select-list-viewport-margin | sys.space.space100 | 8px |
--tanrei-comp-select-motion-close-duration | sys.motion.role.appear.anchored.exit | 250ms |
--tanrei-comp-select-motion-close-easing | sys.motion.easing.exit | cubic-bezier(0.3, 0, 1, 1) |
--tanrei-comp-select-motion-feedback-duration | sys.motion.role.feedback | 450ms |
--tanrei-comp-select-motion-open-duration | sys.motion.role.appear.anchored.enter | 300ms |
--tanrei-comp-select-motion-open-easing | sys.motion.easing.enter | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-select-motion-state-duration | sys.motion.role.state.default | 250ms |
--tanrei-comp-select-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-select-option-gap-compact | comp.menu.container.gap.compact | 8px |
--tanrei-comp-select-option-gap-regular | sys.space.space150 | 12px |
--tanrei-comp-select-option-height-compact | comp.menu.container.height.compact | 32px |
--tanrei-comp-select-option-height-regular | — | 48px |
--tanrei-comp-select-option-leading-icon-size-compact | comp.menu.leading-icon.size.compact | 20px |
--tanrei-comp-select-option-leading-icon-size-regular | sys.icon.medium | 24px |
--tanrei-comp-select-option-mark-size-compact | comp.menu.mark.size.compact | 20px |
--tanrei-comp-select-option-mark-size-regular | sys.icon.medium | 24px |
--tanrei-comp-select-option-padding-block-compact | comp.menu.container.padding-block.compact | 6px |
--tanrei-comp-select-option-padding-block-regular | — | 8px |
--tanrei-comp-select-option-padding-inline-compact | comp.menu.container.padding-inline.compact | 8px |
--tanrei-comp-select-option-padding-inline-regular | sys.space.space150 | 12px |
--tanrei-comp-select-option-shape | — | 4px |
--tanrei-comp-select-outlined-disabled-option-container-color | — | |
--tanrei-comp-select-outlined-disabled-option-label-color | — | |
--tanrei-comp-select-outlined-disabled-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-disabled-option-state-layer-color | — | |
--tanrei-comp-select-outlined-disabled-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-disabled-selected-option-container-color | — | |
--tanrei-comp-select-outlined-disabled-selected-option-label-color | — | |
--tanrei-comp-select-outlined-disabled-selected-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-disabled-selected-option-mark-color | — | |
--tanrei-comp-select-outlined-disabled-selected-option-state-layer-color | — | |
--tanrei-comp-select-outlined-disabled-selected-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-focused-option-container-color | — | |
--tanrei-comp-select-outlined-focused-option-label-color | — | |
--tanrei-comp-select-outlined-focused-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-focused-option-state-layer-color | — | |
--tanrei-comp-select-outlined-focused-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-focused-selected-option-container-color | — | |
--tanrei-comp-select-outlined-focused-selected-option-label-color | — | |
--tanrei-comp-select-outlined-focused-selected-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-focused-selected-option-mark-color | — | |
--tanrei-comp-select-outlined-focused-selected-option-state-layer-color | — | |
--tanrei-comp-select-outlined-focused-selected-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-hovered-option-container-color | — | |
--tanrei-comp-select-outlined-hovered-option-label-color | — | |
--tanrei-comp-select-outlined-hovered-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-hovered-option-state-layer-color | — | |
--tanrei-comp-select-outlined-hovered-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-hovered-selected-option-container-color | — | |
--tanrei-comp-select-outlined-hovered-selected-option-label-color | — | |
--tanrei-comp-select-outlined-hovered-selected-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-hovered-selected-option-mark-color | — | |
--tanrei-comp-select-outlined-hovered-selected-option-state-layer-color | — | |
--tanrei-comp-select-outlined-hovered-selected-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-option-container-color | — | |
--tanrei-comp-select-outlined-option-label-color | sys.color.on-surface | |
--tanrei-comp-select-outlined-option-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-select-outlined-option-state-layer-color | comp.select.outlined.option.label.color | |
--tanrei-comp-select-outlined-option-supporting-text-color | sys.color.on-surface-variant | |
--tanrei-comp-select-outlined-pressed-option-container-color | — | |
--tanrei-comp-select-outlined-pressed-option-label-color | — | |
--tanrei-comp-select-outlined-pressed-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-pressed-option-state-layer-color | — | |
--tanrei-comp-select-outlined-pressed-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-pressed-selected-option-container-color | — | |
--tanrei-comp-select-outlined-pressed-selected-option-label-color | — | |
--tanrei-comp-select-outlined-pressed-selected-option-leading-icon-color | — | |
--tanrei-comp-select-outlined-pressed-selected-option-mark-color | — | |
--tanrei-comp-select-outlined-pressed-selected-option-state-layer-color | — | |
--tanrei-comp-select-outlined-pressed-selected-option-supporting-text-color | — | |
--tanrei-comp-select-outlined-selected-option-container-color | sys.color.secondary-container | |
--tanrei-comp-select-outlined-selected-option-label-color | sys.color.on-secondary-container | |
--tanrei-comp-select-outlined-selected-option-leading-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-select-outlined-selected-option-mark-color | sys.color.primary | |
--tanrei-comp-select-outlined-selected-option-state-layer-color | comp.select.outlined.selected-option.label.color | |
--tanrei-comp-select-outlined-selected-option-supporting-text-color | sys.color.on-secondary-container | |
--tanrei-comp-select-pressed-opacity | — | 1 |
--tanrei-comp-select-separation | sys.behavior.separation | 0 |
--tanrei-comp-select-stacked-disabled-option-container-color | — | |
--tanrei-comp-select-stacked-disabled-option-label-color | — | |
--tanrei-comp-select-stacked-disabled-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-disabled-option-state-layer-color | — | |
--tanrei-comp-select-stacked-disabled-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-disabled-selected-option-container-color | — | |
--tanrei-comp-select-stacked-disabled-selected-option-label-color | — | |
--tanrei-comp-select-stacked-disabled-selected-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-disabled-selected-option-mark-color | — | |
--tanrei-comp-select-stacked-disabled-selected-option-state-layer-color | — | |
--tanrei-comp-select-stacked-disabled-selected-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-focused-option-container-color | — | |
--tanrei-comp-select-stacked-focused-option-label-color | — | |
--tanrei-comp-select-stacked-focused-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-focused-option-state-layer-color | — | |
--tanrei-comp-select-stacked-focused-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-focused-selected-option-container-color | — | |
--tanrei-comp-select-stacked-focused-selected-option-label-color | — | |
--tanrei-comp-select-stacked-focused-selected-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-focused-selected-option-mark-color | — | |
--tanrei-comp-select-stacked-focused-selected-option-state-layer-color | — | |
--tanrei-comp-select-stacked-focused-selected-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-hovered-option-container-color | — | |
--tanrei-comp-select-stacked-hovered-option-label-color | — | |
--tanrei-comp-select-stacked-hovered-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-hovered-option-state-layer-color | — | |
--tanrei-comp-select-stacked-hovered-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-hovered-selected-option-container-color | — | |
--tanrei-comp-select-stacked-hovered-selected-option-label-color | — | |
--tanrei-comp-select-stacked-hovered-selected-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-hovered-selected-option-mark-color | — | |
--tanrei-comp-select-stacked-hovered-selected-option-state-layer-color | — | |
--tanrei-comp-select-stacked-hovered-selected-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-option-container-color | — | |
--tanrei-comp-select-stacked-option-label-color | sys.color.on-surface | |
--tanrei-comp-select-stacked-option-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-select-stacked-option-state-layer-color | comp.select.stacked.option.label.color | |
--tanrei-comp-select-stacked-option-supporting-text-color | sys.color.on-surface-variant | |
--tanrei-comp-select-stacked-pressed-option-container-color | — | |
--tanrei-comp-select-stacked-pressed-option-label-color | — | |
--tanrei-comp-select-stacked-pressed-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-pressed-option-state-layer-color | — | |
--tanrei-comp-select-stacked-pressed-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-pressed-selected-option-container-color | — | |
--tanrei-comp-select-stacked-pressed-selected-option-label-color | — | |
--tanrei-comp-select-stacked-pressed-selected-option-leading-icon-color | — | |
--tanrei-comp-select-stacked-pressed-selected-option-mark-color | — | |
--tanrei-comp-select-stacked-pressed-selected-option-state-layer-color | — | |
--tanrei-comp-select-stacked-pressed-selected-option-supporting-text-color | — | |
--tanrei-comp-select-stacked-selected-option-container-color | sys.color.secondary-container | |
--tanrei-comp-select-stacked-selected-option-label-color | sys.color.on-secondary-container | |
--tanrei-comp-select-stacked-selected-option-leading-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-select-stacked-selected-option-mark-color | sys.color.primary | |
--tanrei-comp-select-stacked-selected-option-state-layer-color | comp.select.stacked.selected-option.label.color | |
--tanrei-comp-select-stacked-selected-option-supporting-text-color | sys.color.on-secondary-container | |
--tanrei-comp-select-status-color | sys.color.on-surface-variant | |
--tanrei-comp-select-status-error-color | sys.color.error | |
--tanrei-comp-input-box-active-indicator-width | sys.border.hairline | 1px |
--tanrei-comp-input-box-affix-gap | sys.space.space25 | 2px |
--tanrei-comp-input-box-container-gap-large | sys.space.space200 | 16px |
--tanrei-comp-input-box-container-gap-medium | sys.space.space200 | 16px |
--tanrei-comp-input-box-container-gap-small | sys.space.space100 | 8px |
--tanrei-comp-input-box-container-height-large | — | 56px |
--tanrei-comp-input-box-container-height-medium | — | 48px |
--tanrei-comp-input-box-container-height-small | — | 40px |
--tanrei-comp-input-box-container-padding-block-with-label-large | — | 4px |
--tanrei-comp-input-box-container-padding-block-with-label-medium | — | 4px |
--tanrei-comp-input-box-container-padding-block-with-label-small | — | 2px |
--tanrei-comp-input-box-container-padding-block-large | — | 14px |
--tanrei-comp-input-box-container-padding-block-medium | — | 10px |
--tanrei-comp-input-box-container-padding-block-small | — | 8px |
--tanrei-comp-input-box-container-padding-inline-with-icon-large | sys.space.space150 | 12px |
--tanrei-comp-input-box-container-padding-inline-with-icon-medium | sys.space.space150 | 12px |
--tanrei-comp-input-box-container-padding-inline-with-icon-small | sys.space.space100 | 8px |
--tanrei-comp-input-box-container-padding-inline-large | sys.space.space200 | 16px |
--tanrei-comp-input-box-container-padding-inline-medium | sys.space.space200 | 16px |
--tanrei-comp-input-box-container-padding-inline-small | sys.space.space150 | 12px |
--tanrei-comp-input-box-container-shape | sys.shape.corner.small | 6px |
--tanrei-comp-input-box-container-shape-bottom | sys.shape.corner.none | 0px |
--tanrei-comp-input-box-disabled-active-indicator-width | — | 1px |
--tanrei-comp-input-box-disabled-opacity | — | 1 |
--tanrei-comp-input-box-disabled-outline-width | — | 1px |
--tanrei-comp-input-box-filled-active-indicator-color | sys.color.outline | |
--tanrei-comp-input-box-filled-affix-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-filled-caret-color | sys.color.primary | |
--tanrei-comp-input-box-filled-container-color | sys.color.surface-container-highest | |
--tanrei-comp-input-box-filled-disabled-active-indicator-color | — | |
--tanrei-comp-input-box-filled-disabled-affix-color | — | |
--tanrei-comp-input-box-filled-disabled-caret-color | — | |
--tanrei-comp-input-box-filled-disabled-container-color | — | |
--tanrei-comp-input-box-filled-disabled-error-active-indicator-color | — | |
--tanrei-comp-input-box-filled-disabled-error-affix-color | — | |
--tanrei-comp-input-box-filled-disabled-error-caret-color | — | |
--tanrei-comp-input-box-filled-disabled-error-container-color | — | |
--tanrei-comp-input-box-filled-disabled-error-input-color | — | |
--tanrei-comp-input-box-filled-disabled-error-label-color | — | |
--tanrei-comp-input-box-filled-disabled-error-leading-icon-color | — | |
--tanrei-comp-input-box-filled-disabled-error-outline-color | — | |
--tanrei-comp-input-box-filled-disabled-error-placeholder-color | — | |
--tanrei-comp-input-box-filled-disabled-error-required-asterisk-color | — | |
--tanrei-comp-input-box-filled-disabled-error-state-layer-color | — | |
--tanrei-comp-input-box-filled-disabled-error-trailing-icon-color | — | |
--tanrei-comp-input-box-filled-disabled-input-color | — | |
--tanrei-comp-input-box-filled-disabled-label-color | — | |
--tanrei-comp-input-box-filled-disabled-leading-icon-color | — | |
--tanrei-comp-input-box-filled-disabled-outline-color | — | |
--tanrei-comp-input-box-filled-disabled-placeholder-color | — | |
--tanrei-comp-input-box-filled-disabled-required-asterisk-color | — | |
--tanrei-comp-input-box-filled-disabled-state-layer-color | — | |
--tanrei-comp-input-box-filled-disabled-trailing-icon-color | — | |
--tanrei-comp-input-box-filled-error-active-indicator-color | sys.color.error | |
--tanrei-comp-input-box-filled-error-affix-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-filled-error-caret-color | sys.color.error | |
--tanrei-comp-input-box-filled-error-container-color | sys.color.surface-container-highest | |
--tanrei-comp-input-box-filled-error-input-color | sys.color.on-surface | |
--tanrei-comp-input-box-filled-error-label-color | sys.color.error | |
--tanrei-comp-input-box-filled-error-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-filled-error-outline-color | — | |
--tanrei-comp-input-box-filled-error-placeholder-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-filled-error-required-asterisk-color | sys.color.primary | |
--tanrei-comp-input-box-filled-error-state-layer-color | comp.input-box.filled.error.input.color | |
--tanrei-comp-input-box-filled-error-trailing-icon-color | sys.color.error | |
--tanrei-comp-input-box-filled-focused-active-indicator-color | — | |
--tanrei-comp-input-box-filled-focused-affix-color | — | |
--tanrei-comp-input-box-filled-focused-caret-color | — | |
--tanrei-comp-input-box-filled-focused-container-color | — | |
--tanrei-comp-input-box-filled-focused-error-active-indicator-color | — | |
--tanrei-comp-input-box-filled-focused-error-affix-color | — | |
--tanrei-comp-input-box-filled-focused-error-caret-color | — | |
--tanrei-comp-input-box-filled-focused-error-container-color | — | |
--tanrei-comp-input-box-filled-focused-error-input-color | — | |
--tanrei-comp-input-box-filled-focused-error-label-color | — | |
--tanrei-comp-input-box-filled-focused-error-leading-icon-color | — | |
--tanrei-comp-input-box-filled-focused-error-outline-color | — | |
--tanrei-comp-input-box-filled-focused-error-placeholder-color | — | |
--tanrei-comp-input-box-filled-focused-error-required-asterisk-color | — | |
--tanrei-comp-input-box-filled-focused-error-state-layer-color | — | |
--tanrei-comp-input-box-filled-focused-error-trailing-icon-color | — | |
--tanrei-comp-input-box-filled-focused-input-color | — | |
--tanrei-comp-input-box-filled-focused-label-color | — | |
--tanrei-comp-input-box-filled-focused-leading-icon-color | — | |
--tanrei-comp-input-box-filled-focused-outline-color | — | |
--tanrei-comp-input-box-filled-focused-placeholder-color | — | |
--tanrei-comp-input-box-filled-focused-required-asterisk-color | — | |
--tanrei-comp-input-box-filled-focused-state-layer-color | — | |
--tanrei-comp-input-box-filled-focused-trailing-icon-color | — | |
--tanrei-comp-input-box-filled-hovered-active-indicator-color | — | |
--tanrei-comp-input-box-filled-hovered-affix-color | — | |
--tanrei-comp-input-box-filled-hovered-caret-color | — | |
--tanrei-comp-input-box-filled-hovered-container-color | — | |
--tanrei-comp-input-box-filled-hovered-error-active-indicator-color | — | |
--tanrei-comp-input-box-filled-hovered-error-affix-color | — | |
--tanrei-comp-input-box-filled-hovered-error-caret-color | — | |
--tanrei-comp-input-box-filled-hovered-error-container-color | — | |
--tanrei-comp-input-box-filled-hovered-error-input-color | — | |
--tanrei-comp-input-box-filled-hovered-error-label-color | — | |
--tanrei-comp-input-box-filled-hovered-error-leading-icon-color | — | |
--tanrei-comp-input-box-filled-hovered-error-outline-color | — | |
--tanrei-comp-input-box-filled-hovered-error-placeholder-color | — | |
--tanrei-comp-input-box-filled-hovered-error-required-asterisk-color | — | |
--tanrei-comp-input-box-filled-hovered-error-state-layer-color | — | |
--tanrei-comp-input-box-filled-hovered-error-trailing-icon-color | — | |
--tanrei-comp-input-box-filled-hovered-input-color | — | |
--tanrei-comp-input-box-filled-hovered-label-color | — | |
--tanrei-comp-input-box-filled-hovered-leading-icon-color | — | |
--tanrei-comp-input-box-filled-hovered-outline-color | — | |
--tanrei-comp-input-box-filled-hovered-placeholder-color | — | |
--tanrei-comp-input-box-filled-hovered-required-asterisk-color | — | |
--tanrei-comp-input-box-filled-hovered-state-layer-color | — | |
--tanrei-comp-input-box-filled-hovered-trailing-icon-color | — | |
--tanrei-comp-input-box-filled-input-color | sys.color.on-surface | |
--tanrei-comp-input-box-filled-label-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-filled-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-filled-outline-color | — | |
--tanrei-comp-input-box-filled-placeholder-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-filled-required-asterisk-color | sys.color.primary | |
--tanrei-comp-input-box-filled-state-layer-color | comp.input-box.filled.input.color | |
--tanrei-comp-input-box-filled-trailing-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-focused-active-indicator-width | — | 2px |
--tanrei-comp-input-box-focused-opacity | — | 1 |
--tanrei-comp-input-box-focused-outline-width | — | 2px |
--tanrei-comp-input-box-hovered-active-indicator-width | — | 1px |
--tanrei-comp-input-box-hovered-opacity | — | 1 |
--tanrei-comp-input-box-hovered-outline-width | — | 1px |
--tanrei-comp-input-box-icon-size-large | sys.icon.medium | 24px |
--tanrei-comp-input-box-icon-size-medium | sys.icon.medium | 24px |
--tanrei-comp-input-box-icon-size-small | sys.icon.small | 20px |
--tanrei-comp-input-box-motion-label-duration | sys.motion.role.state.default | 250ms |
--tanrei-comp-input-box-motion-label-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-input-box-motion-state-duration-large | sys.motion.role.state.default | 250ms |
--tanrei-comp-input-box-motion-state-duration-medium | sys.motion.role.state.default | 250ms |
--tanrei-comp-input-box-motion-state-duration-small | sys.motion.role.state.compact | 200ms |
--tanrei-comp-input-box-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-input-box-outline-notch-padding | sys.space.space50 | 4px |
--tanrei-comp-input-box-outline-width | sys.border.hairline | 1px |
--tanrei-comp-input-box-outlined-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-affix-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-outlined-caret-color | sys.color.primary | |
--tanrei-comp-input-box-outlined-container-color | — | |
--tanrei-comp-input-box-outlined-disabled-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-disabled-affix-color | — | |
--tanrei-comp-input-box-outlined-disabled-caret-color | — | |
--tanrei-comp-input-box-outlined-disabled-container-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-affix-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-caret-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-container-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-input-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-label-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-leading-icon-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-outline-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-placeholder-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-required-asterisk-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-state-layer-color | — | |
--tanrei-comp-input-box-outlined-disabled-error-trailing-icon-color | — | |
--tanrei-comp-input-box-outlined-disabled-input-color | — | |
--tanrei-comp-input-box-outlined-disabled-label-color | — | |
--tanrei-comp-input-box-outlined-disabled-leading-icon-color | — | |
--tanrei-comp-input-box-outlined-disabled-outline-color | — | |
--tanrei-comp-input-box-outlined-disabled-placeholder-color | — | |
--tanrei-comp-input-box-outlined-disabled-required-asterisk-color | — | |
--tanrei-comp-input-box-outlined-disabled-state-layer-color | — | |
--tanrei-comp-input-box-outlined-disabled-trailing-icon-color | — | |
--tanrei-comp-input-box-outlined-error-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-error-affix-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-outlined-error-caret-color | sys.color.error | |
--tanrei-comp-input-box-outlined-error-container-color | — | |
--tanrei-comp-input-box-outlined-error-input-color | sys.color.on-surface | |
--tanrei-comp-input-box-outlined-error-label-color | sys.color.error | |
--tanrei-comp-input-box-outlined-error-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-outlined-error-outline-color | sys.color.error | |
--tanrei-comp-input-box-outlined-error-placeholder-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-outlined-error-required-asterisk-color | sys.color.primary | |
--tanrei-comp-input-box-outlined-error-state-layer-color | comp.input-box.outlined.error.input.color | |
--tanrei-comp-input-box-outlined-error-trailing-icon-color | sys.color.error | |
--tanrei-comp-input-box-outlined-focused-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-focused-affix-color | — | |
--tanrei-comp-input-box-outlined-focused-caret-color | — | |
--tanrei-comp-input-box-outlined-focused-container-color | — | |
--tanrei-comp-input-box-outlined-focused-error-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-focused-error-affix-color | — | |
--tanrei-comp-input-box-outlined-focused-error-caret-color | — | |
--tanrei-comp-input-box-outlined-focused-error-container-color | — | |
--tanrei-comp-input-box-outlined-focused-error-input-color | — | |
--tanrei-comp-input-box-outlined-focused-error-label-color | — | |
--tanrei-comp-input-box-outlined-focused-error-leading-icon-color | — | |
--tanrei-comp-input-box-outlined-focused-error-outline-color | — | |
--tanrei-comp-input-box-outlined-focused-error-placeholder-color | — | |
--tanrei-comp-input-box-outlined-focused-error-required-asterisk-color | — | |
--tanrei-comp-input-box-outlined-focused-error-state-layer-color | — | |
--tanrei-comp-input-box-outlined-focused-error-trailing-icon-color | — | |
--tanrei-comp-input-box-outlined-focused-input-color | — | |
--tanrei-comp-input-box-outlined-focused-label-color | — | |
--tanrei-comp-input-box-outlined-focused-leading-icon-color | — | |
--tanrei-comp-input-box-outlined-focused-outline-color | — | |
--tanrei-comp-input-box-outlined-focused-placeholder-color | — | |
--tanrei-comp-input-box-outlined-focused-required-asterisk-color | — | |
--tanrei-comp-input-box-outlined-focused-state-layer-color | — | |
--tanrei-comp-input-box-outlined-focused-trailing-icon-color | — | |
--tanrei-comp-input-box-outlined-hovered-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-hovered-affix-color | — | |
--tanrei-comp-input-box-outlined-hovered-caret-color | — | |
--tanrei-comp-input-box-outlined-hovered-container-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-active-indicator-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-affix-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-caret-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-container-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-input-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-label-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-leading-icon-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-outline-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-placeholder-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-required-asterisk-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-state-layer-color | — | |
--tanrei-comp-input-box-outlined-hovered-error-trailing-icon-color | — | |
--tanrei-comp-input-box-outlined-hovered-input-color | — | |
--tanrei-comp-input-box-outlined-hovered-label-color | — | |
--tanrei-comp-input-box-outlined-hovered-leading-icon-color | — | |
--tanrei-comp-input-box-outlined-hovered-outline-color | — | |
--tanrei-comp-input-box-outlined-hovered-placeholder-color | — | |
--tanrei-comp-input-box-outlined-hovered-required-asterisk-color | — | |
--tanrei-comp-input-box-outlined-hovered-state-layer-color | — | |
--tanrei-comp-input-box-outlined-hovered-trailing-icon-color | — | |
--tanrei-comp-input-box-outlined-input-color | sys.color.on-surface | |
--tanrei-comp-input-box-outlined-label-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-outlined-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-outlined-outline-color | sys.color.outline | |
--tanrei-comp-input-box-outlined-placeholder-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-outlined-required-asterisk-color | sys.color.primary | |
--tanrei-comp-input-box-outlined-state-layer-color | comp.input-box.outlined.input.color | |
--tanrei-comp-input-box-outlined-trailing-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-requirement-asterisk-gap | sys.space.space25 | 2px |
--tanrei-comp-input-box-selection-color | sys.color.primary | |
--tanrei-comp-input-box-selection-content-color | sys.color.on-primary | |
--tanrei-comp-input-box-stacked-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-affix-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-stacked-caret-color | sys.color.primary | |
--tanrei-comp-input-box-stacked-container-color | sys.color.surface-container-highest | |
--tanrei-comp-input-box-stacked-container-height-large | comp.button.container.height.large | 56px |
--tanrei-comp-input-box-stacked-container-height-medium | comp.button.container.height.medium | 40px |
--tanrei-comp-input-box-stacked-container-height-small | comp.button.container.height.small | 32px |
--tanrei-comp-input-box-stacked-container-padding-block-large | — | 14px |
--tanrei-comp-input-box-stacked-container-padding-block-medium | — | 6px |
--tanrei-comp-input-box-stacked-container-padding-block-small | — | 4px |
--tanrei-comp-input-box-stacked-disabled-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-disabled-affix-color | — | |
--tanrei-comp-input-box-stacked-disabled-caret-color | — | |
--tanrei-comp-input-box-stacked-disabled-container-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-affix-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-caret-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-container-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-input-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-label-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-leading-icon-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-outline-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-placeholder-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-required-asterisk-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-state-layer-color | — | |
--tanrei-comp-input-box-stacked-disabled-error-trailing-icon-color | — | |
--tanrei-comp-input-box-stacked-disabled-input-color | — | |
--tanrei-comp-input-box-stacked-disabled-label-color | — | |
--tanrei-comp-input-box-stacked-disabled-leading-icon-color | — | |
--tanrei-comp-input-box-stacked-disabled-outline-color | — | |
--tanrei-comp-input-box-stacked-disabled-placeholder-color | — | |
--tanrei-comp-input-box-stacked-disabled-required-asterisk-color | — | |
--tanrei-comp-input-box-stacked-disabled-state-layer-color | — | |
--tanrei-comp-input-box-stacked-disabled-trailing-icon-color | — | |
--tanrei-comp-input-box-stacked-error-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-error-affix-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-stacked-error-caret-color | sys.color.error | |
--tanrei-comp-input-box-stacked-error-container-color | sys.color.surface-container-highest | |
--tanrei-comp-input-box-stacked-error-input-color | sys.color.on-surface | |
--tanrei-comp-input-box-stacked-error-label-color | sys.color.on-surface | |
--tanrei-comp-input-box-stacked-error-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-stacked-error-outline-color | sys.color.error | |
--tanrei-comp-input-box-stacked-error-placeholder-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-stacked-error-required-asterisk-color | sys.color.primary | |
--tanrei-comp-input-box-stacked-error-state-layer-color | comp.input-box.stacked.error.input.color | |
--tanrei-comp-input-box-stacked-error-trailing-icon-color | sys.color.error | |
--tanrei-comp-input-box-stacked-focused-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-focused-affix-color | — | |
--tanrei-comp-input-box-stacked-focused-caret-color | — | |
--tanrei-comp-input-box-stacked-focused-container-color | — | |
--tanrei-comp-input-box-stacked-focused-error-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-focused-error-affix-color | — | |
--tanrei-comp-input-box-stacked-focused-error-caret-color | — | |
--tanrei-comp-input-box-stacked-focused-error-container-color | — | |
--tanrei-comp-input-box-stacked-focused-error-input-color | — | |
--tanrei-comp-input-box-stacked-focused-error-label-color | — | |
--tanrei-comp-input-box-stacked-focused-error-leading-icon-color | — | |
--tanrei-comp-input-box-stacked-focused-error-outline-color | — | |
--tanrei-comp-input-box-stacked-focused-error-placeholder-color | — | |
--tanrei-comp-input-box-stacked-focused-error-required-asterisk-color | — | |
--tanrei-comp-input-box-stacked-focused-error-state-layer-color | — | |
--tanrei-comp-input-box-stacked-focused-error-trailing-icon-color | — | |
--tanrei-comp-input-box-stacked-focused-input-color | — | |
--tanrei-comp-input-box-stacked-focused-label-color | — | |
--tanrei-comp-input-box-stacked-focused-leading-icon-color | — | |
--tanrei-comp-input-box-stacked-focused-outline-color | — | |
--tanrei-comp-input-box-stacked-focused-placeholder-color | — | |
--tanrei-comp-input-box-stacked-focused-required-asterisk-color | — | |
--tanrei-comp-input-box-stacked-focused-state-layer-color | — | |
--tanrei-comp-input-box-stacked-focused-trailing-icon-color | — | |
--tanrei-comp-input-box-stacked-hovered-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-hovered-affix-color | — | |
--tanrei-comp-input-box-stacked-hovered-caret-color | — | |
--tanrei-comp-input-box-stacked-hovered-container-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-active-indicator-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-affix-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-caret-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-container-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-input-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-label-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-leading-icon-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-outline-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-placeholder-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-required-asterisk-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-state-layer-color | — | |
--tanrei-comp-input-box-stacked-hovered-error-trailing-icon-color | — | |
--tanrei-comp-input-box-stacked-hovered-input-color | — | |
--tanrei-comp-input-box-stacked-hovered-label-color | — | |
--tanrei-comp-input-box-stacked-hovered-leading-icon-color | — | |
--tanrei-comp-input-box-stacked-hovered-outline-color | — | |
--tanrei-comp-input-box-stacked-hovered-placeholder-color | — | |
--tanrei-comp-input-box-stacked-hovered-required-asterisk-color | — | |
--tanrei-comp-input-box-stacked-hovered-state-layer-color | — | |
--tanrei-comp-input-box-stacked-hovered-trailing-icon-color | — | |
--tanrei-comp-input-box-stacked-input-color | sys.color.on-surface | |
--tanrei-comp-input-box-stacked-label-color | sys.color.on-surface | |
--tanrei-comp-input-box-stacked-leading-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-stacked-outline-color | — | |
--tanrei-comp-input-box-stacked-placeholder-color | sys.color.on-surface-variant | |
--tanrei-comp-input-box-stacked-required-asterisk-color | sys.color.primary | |
--tanrei-comp-input-box-stacked-state-layer-color | comp.input-box.stacked.input.color | |
--tanrei-comp-input-box-stacked-trailing-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-select-group-title-typescale | sys.typescale.label-small | 500 12px/1.3333 |
--tanrei-comp-select-option-label-typescale | sys.typescale.label | 500 14px/1.4286 |
--tanrei-comp-select-option-supporting-text-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-select-status-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-input-box-input-typescale-large | sys.typescale.body-large | 400 16px/1.625 |
--tanrei-comp-input-box-input-typescale-medium | sys.typescale.body-large | 400 16px/1.625 |
--tanrei-comp-input-box-input-typescale-small | sys.typescale.body | 400 14px/1.5714 |
--tanrei-comp-input-box-label-floated-typescale-large | sys.typescale.label-small | 500 12px/1.3333 |
--tanrei-comp-input-box-label-floated-typescale-medium | sys.typescale.label-small | 500 12px/1.3333 |
--tanrei-comp-input-box-label-floated-typescale-small | sys.typescale.label-small | 500 12px/1.3333 |
--tanrei-comp-input-box-label-typescale-large | sys.typescale.label | 500 14px/1.4286 |
--tanrei-comp-input-box-label-typescale-medium | sys.typescale.label | 500 14px/1.4286 |
--tanrei-comp-input-box-label-typescale-small | sys.typescale.label | 500 14px/1.4286 |
Fixtures
Canonical renderings. Every platform snapshots each fixture under the same id so the results can be compared.
variant: stacked (38)
stacked-empty
variant: stacked label: Country items: 5 stacked-value
variant: stacked value: jp label: Country items: 5 stacked-open
variant: stacked value: jp label: Country open: true items: 5 Drawn alone in a window of 312 × 472stacked-large
variant: stacked size: large value: jp label: Country items: 5 stacked-small
variant: stacked size: small value: jp label: Country items: 5 stacked-error
variant: stacked label: Country error: true items: 5 stacked-disabled
variant: stacked value: jp label: Country disabled: true items: 5 stacked-placeholder
label: Country placeholder: Choose a country items: 5 stacked-open-none-chosen
label: Country placeholder: Choose a country open: true items: 5 Drawn alone in a window of 312 × 473stacked-error-value
value: ke label: Country error: true items: 5 stacked-read-only
value: jp label: Country readOnly: true items: 5 stacked-option-icon
value: dark label: Theme items: 3 stacked-leading-icon
value: ja label: Language items: 2 slots: leading-icon open-icons
value: dark label: Theme open: true items: 3 Drawn alone in a window of 312 × 473open-supporting-text
value: team label: Plan open: true items: 3 Drawn alone in a window of 312 × 473open-disabled-chosen
value: enterprise label: Plan open: true items: 3 Drawn alone in a window of 312 × 473open-groups
value: Europe/Lisbon label: Time zone open: true items: 3 Drawn alone in a window of 312 × 473open-groups-divider
value: Europe/Lisbon label: Time zone open: true separation: divider items: 3 Drawn alone in a window of 312 × 473open-groups-gap
value: Europe/Lisbon label: Time zone open: true separation: gap items: 3 Drawn alone in a window of 312 × 473open-many
value: corner.small label: Token open: true items: 24 Drawn alone in a window of 312 × 473open-long-options
value: weekly label: Email me open: true items: 3 Drawn alone in a window of 312 × 473long-value
value: all label: Email me items: 3 empty-value-option
value: label: Contrast items: 3 small-label-hidden
size: small value: high label: Contrast items: 3 small-label-hidden-icons
size: small value: light label: Theme items: 3 small-label-hidden-leading-icon
size: small value: en label: Language items: 2 slots: leading-icon small-label-hidden-open
size: small value: light label: Theme open: true items: 3 Drawn alone in a window of 312 × 473small-open
size: small value: jp label: Country open: true items: 5 Drawn alone in a window of 312 × 473open-rtl
value: Asia/Tokyo label: المنطقة الزمنية open: true direction: rtl items: 2 Drawn alone in a window of 312 × 473long-value-rtl
value: all label: Email me direction: rtl items: 3 icon-space-auto
value: mail label: Share to open: true items: 2 Drawn alone in a window of 312 × 473icon-space-always
value: mail label: Share to open: true iconSpace: always items: 2 Drawn alone in a window of 312 × 473icon-space-never
value: mail label: Share to open: true iconSpace: never items: 2 Drawn alone in a window of 312 × 473density-compact
value: mail label: Share to open: true density: compact items: 2 Drawn alone in a window of 312 × 473open-loading
label: Project placeholder: Choose a project open: true status: loading statusText: Loading projects Drawn alone in a window of 312 × 473open-status
value: atlas label: Project open: true statusText: Your 3 most recent projects. Search for… items: 3 Drawn alone in a window of 312 × 473open-status-error
label: Project placeholder: Choose a project open: true status: error statusText: Projects could not be loaded. Check your… Drawn alone in a window of 312 × 473open-empty
label: Project placeholder: Choose a project open: true emptyText: No projects yet Drawn alone in a window of 312 × 473variant: filled (7)
filled-empty
variant: filled label: Country items: 5 filled-value
variant: filled value: jp label: Country items: 5 filled-open
variant: filled value: jp label: Country open: true items: 5 Drawn alone in a window of 312 × 473filled-large
variant: filled size: large value: jp label: Country items: 5 filled-small
variant: filled size: small value: jp label: Country items: 5 filled-error
variant: filled label: Country error: true items: 5 filled-disabled
variant: filled value: jp label: Country disabled: true items: 5 variant: outlined (8)
outlined-empty
variant: outlined label: Country items: 5 outlined-value
variant: outlined value: jp label: Country items: 5 outlined-open
variant: outlined value: jp label: Country open: true items: 5 Drawn alone in a window of 312 × 473outlined-large
variant: outlined size: large value: jp label: Country items: 5 outlined-small
variant: outlined size: small value: jp label: Country items: 5 outlined-error
variant: outlined label: Country error: true items: 5 outlined-disabled
variant: outlined value: jp label: Country disabled: true items: 5 outlined-rtl
variant: outlined value: Europe/Lisbon label: المنطقة الزمنية direction: rtl items: 2 Generated from specs/select.spec.json, tokens/comp/select.tokens.json, tokens/comp/input-box.tokens.json, design/tokens/
<tanrei-option>and<tanrei-option-group>are data: they draw nothing. The label is thelabelattribute or, without one, the element’s text. A group’s title is itslabelattribute.- The options are the select’s children. A
<slot>among its children stands for the elements assigned to it, so an element of your own can pass options on:<tanrei-select label="Plan"><slot></slot></tanrei-select>in its shadow root. The select field does exactly this. - The select is a form-associated element. It takes
name, submitsvalue, resets to thevalueattribute, and reports a missing value to the form while the field around it says it is required andvalueis empty. - A bare select is an inline box; in a field it is a block that fills the field.
changeis a composed, bubbling event.toggleis composed and does not bubble: it is heard on the select, and on a select field around it, and never by an ancestor such as a<details>.- The list is a
popover="manual"element in the top layer. A press outside closes it and also reaches the element under it. - A floating label (
filled,outlined) lies inside the container, so a press on it opens the list. supporting-text,error-text,required,optional,required-mark,required-textandoptional-text, and a child for alabel-iconslot, are the select field’s and the field’s. On a select they draw nothing; a debug build says so once and names<tanrei-select-field>.- Parts:
container,outline,active-indicator,label,requirement-label,value,placeholder,leading-icon,chevron,list,option,option-label,option-supporting-text,option-leading-icon,mark,group,group-title,divider,status. empty-textleft empty is read from the product’s words (setWords({ noOptionsText })).- Before the element is defined, its options are running text. Hide
tanrei-select:not(:defined) > *and reserve the height. - The web has no back gesture of its own, so
togglenever carries the reasonback. readOnlyis thereadonlyattribute.
| You need | Use |
|---|---|
| A label, the box, help and an error | A select field |
| The box alone, where its label stands elsewhere: a bar, a toolbar, a cell of a table | A select, named by its own label |
| Something of your own inside the frame, beside or below the box | A field with a select and your own element in it |
| Aspect | Select | Radio buttons | Menu | Combobox |
|---|---|---|---|---|
| Holds | One value of a closed list | One value of a few | Actions | One value found by typing |
| Shows the choices | After a press | Always | After a press | After a press, filtered |
| Part of a form | Yes | Yes | No | Yes |
- With up to about five short options and room for them, show them all: radio buttons, or tabs for a view.
- If the items do something rather than stand for a value, it is a menu. An option is a value, never a place to go: a list of destinations is a menu or links.
- If the list is long enough that people would rather type than scroll, or takes a value that is not in it, it is a combobox.
- Several values are checkboxes, or a list with multiple selection.
- Give a bare select a
labelthat names what is chosen, in the words of the visible label where there is one. - Use a bare select only where a person who sees the page can tell what it sets: by its place, its glyph, or the value it shows.
- Order options the way people look for them: alphabetically, by number, or the common ones first.
- Group a long list and give each group a short title.
- Use
supportingTexton an option for what its label cannot say in a few words.
- Don’t use a bare select in a form to save the room of a label. Use the select field.
- Don’t write
labelon a select and expect it above the box. A stacked select draws no label: that is the select field’s row. - Don’t use the placeholder as the label. It goes away once something is chosen.
- Don’t navigate, submit or delete on
changewithout saying so. People move through a list to read it. - Don’t set
label,errorordisabledon a select inside a field. Say them on the field, so that the label and the help follow. - Don’t disable an option without a way to learn why. Say it in its supporting text.
- Role
combobox- Labeling
- The accessible name of the select and of its list is the label of the Field that frames it, or its own label where no Field does; a filled or outlined select draws that name floating, and labelHidden only stops the drawing. The placeholder is never the name and never the value. The chosen option's label is the select's value. The description is what the Field gives: its supporting text with, in error, the error text after it and its optional text. The select is exposed as invalid while error is true or its Field is in error, required while its Field says so — whichever mark is drawn, and the mark itself is hidden — read-only while readOnly is true, and expanded while its list is open. An option is named by its label and described by its supporting text, and exposes whether it is the chosen one, whether it is disabled, and its position among the options; a group is named by its title. The status row is a live region: what it says is read when the list opens and when it changes, politely, and at once while the source failed; while the options are loading the list box is busy and nothing is announced. The leading icons, the chevron, the check and the divider are decorative.
- Minimum target
- The hit area meets the minimum target size even when the visual size is smaller. minimum 32px · pointer 44px · spacing 8px · touch 48px
Keyboard
| Keys | Action |
|---|---|
| Tab / Shift+Tab | Moves focus to the select, which is one stop, and on from it. With the list open, closes the list without choosing and moves focus on. |
| Enter, Space, Down Arrow / Up Arrow, list closed | Opens the list; the chosen option is the active one, or the first enabled option where none is chosen. The value does not change. |
| Down Arrow / Up Arrow, list open | Makes the next or previous option active, across groups, stopping at the ends. |
| Home / End, list open | Makes the first or the last option active. |
| Page Down / Page Up, list open | Moves the active option by ten, stopping at the ends. |
| Printable characters | Opens the list while it is closed; makes active the next option whose label begins with the characters typed within a second of each other. The value does not change. |
| Enter / Space, list open | Chooses the active option, emits change if the value changed, and closes the list. On a disabled option, nothing. A Space typed while typing ahead is a character. |
| Escape | Closes the list without choosing; focus stays on the select. With the list closed the select does not act on it. |
Notes
- The select is the bare control: it draws no label row, no requirement mark, no help and no error. In a form it stands in a Field, which is the select field; alone it is for a place where its label stands elsewhere or is plain from where it stands and what it shows — a theme or a language select in a header, a toolbar, a column of a table — and there its own label names it.
- The pattern is the WAI-ARIA select-only combobox: focus stays on the field and the active option is its active descendant on the web, as the search's suggestions are (SRC-14), not moved into the list as the menu moves it. The field keeps its name, its description, its invalid and required states and its focused edge while the person reads the options, and typing keeps going to one place. SwiftUI, Compose, Flutter and React Native have no active descendant: there the list's options are focusable items that assistive technology moves through, and a hardware keyboard's arrow keys move the highlight as on the web.
- The value changes only when the person confirms an option: a press, Enter or Space. Arrow keys and typing on a closed select open the list instead of changing the value unseen, and Tab, Escape and a press outside close it without choosing, as the menu and the docked picker close without committing (MNU-5, DTM-5). A change event may navigate, save or rebuild a page, so it must never follow a key the person pressed for something else.
- A select holds one value from a closed list. Several values are checkboxes, or a list with multiple selection; a list long enough that people would rather type than scroll, or one that accepts a value not in it, is a combobox, which shares this box, this list and these options and adds a text input. A set of actions is a menu: a menu item does something, an option is a value.
- Status: a select whose options come from a source that takes time or can fail says so in its list, as the combobox and the search do, with the same three props: status loading while they are on their way (the list box is busy, and a loading indicator named by statusText stands in the status row), status error with statusText saying what failed, and statusText alone for what the list should add (a count). A select that holds no option says why in emptyText, rather than not opening. The select never fetches, waits or retries: the product sets status, and the words are the product's. The loading and empty states are those of the ARIA listbox and combobox patterns, said in a live region outside the list box, as React Aria's and MUI's lists and Ant Design's select show them.
- With up to about five short options and room for them, a radio group or tabs show every choice at once and take one press; a select hides the choices behind a press and is for many options or little room.
- Overlay: the list is an operated overlay that is not modal. It draws no scrim, traps nothing and makes nothing inert. It is placed, sized, dismissed and moved as a popover's container is, by the popover's rules cited by id, with three rules of its own: focus stays on the field, Tab closes at once, and what it opens is a list box.
- Heights are minimums: at 200% text size the container and the options grow, labels and options wrap, and only the value in the container stays on one line (SEL-51). The list scrolls when it no longer fits; it never scrolls sideways.
- The chosen option is told from the others by its check, never by its fill alone; the fill supports the mark and is not held to a contrast of its own. Content on it keeps at least 4.5:1 in every state, by the menu's tokens.
- An error is never shown by color alone: the invalid state carries it and the error text of the Field that frames the select says it; the line inside a stacked select's edge supports them.
- The hit area of the container meets the minimum target although a small select is 40 high, and options are at least the minimum target tall (WCAG 2.5.8).
- A disabled option stays reachable by the arrow keys and is read as unavailable; a select with a single option, or none, is still a select and says so, rather than turning into text.
- How a state looks is a value decision of the active design system; components read the state's tokens and never compute the effect themselves.
- WCAG 2.5.8 (target size): for touch every option is at least the touch target high in both densities (SEL-37). The compact density is lower than the pointer target for a fine pointer, by design: it is the density of a tool used with a mouse, it stays above the 24 by 24 the criterion asks, and its options reach across the list's whole width. A product that cannot accept that uses regular.
Generated from specs/select.spec.json, tokens/sys/measure.tokens.json
- The trigger is a combobox that is not editable. Its name is its own
label, or the label of the field around it where it has none, and never the placeholder. Its value is the chosen option’s label. - Its description is what its field gives: the help or the error text, and the optional text.
- It is exposed as invalid while in error, as required while its field says so, as read-only, as disabled, and as expanded while its list is open.
- Focus stays on the trigger while the list is open. The active option is its active descendant, so the box keeps its name and its states while the person reads the options.
- The list is a list box named as the select is. Each option has its name, its description, whether it is the chosen one, whether it is disabled, and its position among the options. A group is named by its title.
- The chosen option is told from the others by its check, never by its fill alone.
- The glyphs, the chevron, the check, the divider and the asterisk after a floating label are hidden from assistive technology.
- The select is one stop in the focus order. Tab closes an open list and moves on.
- Escape closes the list and goes no further, so a dialog around the select stays open.
- The trigger’s hit area meets the minimum target although a small select is 40 high, and options are at least the minimum target tall.
- At 200% text the options and the titles wrap. Only the value in the container stays on one line, and the list and assistive technology still give it in full.
- In forced colors the trigger and the list gain an edge, the active option takes the highlight colors, and the chosen option is shown by its check.
- In a right-to-left layout the trigger and each row are mirrored, and each text keeps its own direction. The chevron and the check are not mirrored.
- The status row is a live region outside the list box. What it says is read when the list opens and when it changes: politely, and at once when the source failed. While loading, the list box is marked busy and nothing is announced.
- It has no words of its own. Left empty,
emptyTextis read from the product’s words (noOptionsText).
Known gaps on the web:
- The divider between two groups is not exposed as a separator: a list box holds options and groups only.
- Typing compares labels without regard to case in the way JavaScript does, not by the rules of the label’s language.
- Before the element is defined, the options are text on the page without a control.
- Name every bare select with
label, and give every option a label. A visible text beside the select does not name it. - Where a bare select can be wrong, say what is wrong in words that reach assistive technology: a message of your own tied to the page, or a field around the select.
- Keep option values unique within a select.
- Write
statusTextwhile loading, because it names the loading indicator, and writeemptyTextwhere a select can be left without options.