跳到主要内容
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
开始使用WebReactSwiftUIJetpack ComposeFlutterReact Native预设你自己的设计系统与 AI 代理一起使用 Tanrei品牌设计令牌设计系统与定制Theme Studio颜色字体排印图标形状层级动效交互状态行为布局无障碍文字颜色令牌字阶令牌形状令牌层级令牌动效令牌状态令牌间距与度量令牌组件按钮主操作按钮分段按钮按钮组带菜单的按钮工具栏复制按钮复选框单选按钮开关纸片(Chip)菜单滑块滑块字段日历DateTimePicker字段组单选组复选框组下拉选择框下拉选择字段菜单栏文件上传文件选择器 进阶放置区文本输入框 进阶文本字段纸片字段字段组合框 进阶组合框字段数字输入框 进阶数字字段颜色输入框 进阶颜色字段一次性验证码输入框 进阶一次性验证码字段嵌入式文本输入框 进阶聊天输入栏卡片分隔线模态层 进阶对话框列表列表行底部面板侧边面板轮播代码块表格弹出框步骤折叠面板描述列表图示应用外壳页头树视图时间线图片预览调整手柄可调整大小的框滚动区域徽标进度指示器计量条数字滚动器加载指示器消息条(Snackbar)工具提示键盘快捷键标签(Tag)行内代码提示块(Callout)头像空状态标签组(Tag group)骨架屏文件项色块跑马灯文本标签页标签页视图应用栏导航栏导航抽屉搜索链接分页上一页/下一页链接步骤条面包屑目录链接卡片FAB 菜单状态Tanrei 是如何构建的参与贡献许可与致谢
首页组件下拉选择框

下拉选择框

让用户从一个封闭的列表里选择一个值——触发器的外观和尺寸与文本输入框的方框相同,显示选中的选项;选项列表从触发器展开,选项可以分组——它没有自己的标签行、帮助文本和错误文本。用于栏、工具栏或表格,或者放进产品自己组合的字段里。

content_copy以 Markdown 格式查看
类别
选择
令牌
comp.select
层级
基础

概览

Web
tanrei-select
schedule
SwiftUI
TanreiSelect
radio_button_unchecked
Jetpack Compose
TanreiSelect
radio_button_unchecked
Flutter
TanreiSelect
radio_button_unchecked
React Native
Select
radio_button_unchecked
不提供的变体
tanrei: filled, outlined

生成自 docs/status/status.json, specs/select.spec.json

单独使用:放在栏里的主题下拉选择框,以及列表上方的排序方式

浅色深色跟随系统最近更新名称大小

下拉选择框就是下拉选择字段的方框,单独拿出来:触发器和它显示的值、它的前置图标和 V 形箭头,以及从它展开的选项列表。它上方没有标签行,没有必填和选填标记,下方也没有帮助文本和错误文本。

在用户读到的标签位于别处的地方,或者从下拉选择框所在的位置和它显示的内容就能看明白的地方,单独使用它:

位置 靠什么让用户知道它设置的是什么 下拉选择框的 label
应用栏里的主题或语言 它的位置、它的字形和它显示的值 “主题”“语言”
工具栏里的排序方式或筛选条件 它下方的列表 “排序方式”
表格某一列里的单元格;表格下方的每页行数下拉选择框 列标题;下拉选择框旁边的文字 列和行:“角色,李华”

单独使用时,下拉选择框需要自己的 label。它就是无障碍名称。stacked 的下拉选择框绝不绘制它;filled 或 outlined 的下拉选择框把它画成浮在方框里的标签。

在表单里,请使用下拉选择字段:把这个下拉选择框放在一个字段里,上方有标签,还有必填或选填标记、帮助文本和错误。

使用它的组件是下拉选择字段。要自己为它加上框架,就把它放进字段:字段为它命名、提供描述、把它标记为必填,并让它进入错误状态。

不设置宽度时,单独使用的下拉选择框的宽度取它最宽的选项所需的宽度,所以选择不同的选项绝不会改变它的宽度。在字段里,它会填满字段。

在 Web 上它是 <tanrei-select>,子元素是 <tanrei-option>。其他平台尚未实现。

用法

选项是下拉选择框的子元素:每个选项有一个值和一个标签。value 指明选中的是哪一个。用户选择另一个选项时,下拉选择框触发 change;用户打开或关闭列表时,触发 toggle。你自己设置 value 或 open 时,两者都不会触发。

<!-- 在应用栏里:小号,由它自己的 label 命名,这个 label 不画出来。 -->
<tanrei-select size="small" label="主题" value="dark">
  <tanrei-option value="light" leading-icon="light_mode">浅色</tanrei-option>
  <tanrei-option value="dark" leading-icon="dark_mode">深色</tanrei-option>
</tanrei-select>

<!-- 表格的一个单元格:列标题就是可见的标签。 -->
<td>
  <tanrei-select name="role-ada" size="small" label="角色,李华" value="editor">
    <tanrei-option value="viewer">查看者</tanrei-option>
    <tanrei-option value="editor">编辑者</tanrei-option>
  </tanrei-select>
</td>

<script type="module">
  import '@tanrei/web/components/select';

  const theme = document.querySelector('tanrei-select');
  // 用户选择了另一个选项。value 已经是新的值。
  theme.addEventListener('change', () => apply(theme.value));
  // 由用户打开或关闭;关闭时带有原因。
  theme.addEventListener('toggle', (e) => console.log(e.detail.open, e.detail.reason));
</script>
import { Option, Select } from '@tanrei/react';

<Select size="small" label="主题" value={theme} onChange={(event) => setTheme(event.currentTarget.value)}>
  <Option value="light" leadingIcon="light_mode">浅色</Option>
  <Option value="dark" leadingIcon="dark_mode">深色</Option>
</Select>

手写在字段里

包住下拉选择框的字段,就是展开写出来的下拉选择字段。下拉选择框自己的 label 留空:由字段为它命名。label、error、disabled 和必填与否写在字段上;name、value、选项以及与选择有关的一切写在下拉选择框上。

<tanrei-field label="国家/地区" required required-text="必填"
  supporting-text="发票将寄往该国家/地区。">
  <tanrei-select name="country" placeholder="请选择国家/地区">
    <tanrei-option value="jp">日本</tanrei-option>
    <tanrei-option value="ke">肯尼亚</tanrei-option>
  </tanrei-select>
</tanrei-field>

里面放着下拉选择框的字段:画面与下拉选择字段相同

日本肯尼亚

只有下拉选择框自己知道的事,由它告诉所在的字段。它的帮助文本从值开始的位置起笔,越过内边距和前置图标。filled 或 outlined 的下拉选择框自己绘制字段的标签,浮在方框里,所以字段不绘制标签行。列表打开期间,无论下拉选择框有没有焦点,它都画成聚焦的样子,所以字段在这段时间里也把标签和帮助文本画成聚焦的样子。

规格

单独使用的下拉选择框:分组、带辅助文本和一个禁用选项、处于错误状态、只读,以及禁用

东京首尔里斯本柏林免费版团队版企业版日本肯尼亚日本肯尼亚日本肯尼亚

触发器

部件 它是什么
值 选中选项的标签,保持在一行之内,放不下时以省略号截断
占位符 尚未选择时显示的提示。它绝不是标签,也绝不是一个选项
前置图标 leading-icon 插槽里的字形;没有时,采用选中选项自己的字形
V 形箭头 表明这个方框会打开一个列表。它不会转动。只读的下拉选择框不画它
浮动标签 只在 filled 或 outlined 的下拉选择框里:它的名称,尚未选择时停在输入行里,其余时候浮在值的上方。包住下拉选择框的字段说它必填时,后面跟一个星号

三种变体就是文本输入框的那三种。默认系统只提供 stacked;filled 和 outlined 留给以这种方式绘制文本输入框的系统。一个表单只用一种样式。

small(32)、medium(40)和 large(56)是文本输入框的高度。small 用于栏、工具栏或一排紧凑的控件,在那里小号选择框和小号文本输入框高度一致。

哪些属于下拉选择框,哪些属于字段

  • 下拉选择框绘制方框、值、占位符、图标、列表及其选项,以及浮在 filled 或 outlined 方框里的标签。它保存值、在表单中的位置和事件。它告诉辅助技术选中的是哪个选项、列表是否打开、哪个是活动选项,以及它是只读的。
  • 包住它的字段绘制 stacked 下拉选择框上方的标签行、必填或选填标记、帮助文本和错误文本。它什么都不保存。它把名称交给下拉选择框,把帮助文本或错误文本作为描述交给它,并告诉它是否必填、无效或禁用。

单独使用、没有字段时:

  • 名称是 label。labelHidden 让 filled 或 outlined 的下拉选择框不再绘制它;对 stacked 的下拉选择框没有任何影响。
  • 错误是 error,由你来设置:方框画成错误状态,并暴露为无效。下拉选择框没有错误文本。请在它旁边用文字说明哪里错了,或者给它加上框架。
  • 必填要由字段来说明。单独使用的下拉选择框没有必填与否的设置:表单需要回答的地方,请自己校验。

列表

列表在容器下方相距 comp.select.list.offset 处打开,起始边与容器对齐;下方没有空间时,在容器上方打开。它的宽度是各选项排成一行所需的宽度:不窄于容器和 list.min-width(160);容器比 list.max-width(320)窄时,不宽于 list.max-width。这样较长的标签或辅助文字用更少的行就能读完,而容器的宽度不变。需要更宽的选项会换行。它的高度等于各选项加起来的高度,上限是 list.max-height(320),超过时滚动。列表打开时,选中的选项位于视野之内。

部件 它是什么
选项 一行,带有标签,还可以有一行辅助文本和一个字形。选中的那一个带有低调的填充和一个对勾
组 一串选项,上方可以有一个标题。组与组之间用分隔线或间隙分开,由设计系统或 separation 决定
禁用的选项 可以用方向键到达,让用户知道它存在,但绝不会被选中

选项的字形位于标签前的一列中。没有字形的选项是否保留这一列的空间,由 iconSpace 决定。auto(默认)在同一区段中另有选项带字形时保留这一列,使该区段的标签从同一条线开始;在没有字形的区段中不保留。区段是一个组,或组外相邻的一串选项。always 在每个选项中都保留,never 只在带字形的选项中设这一列。菜单也有同样的 iconSpace,取值和默认值都相同。

density 决定一个选项占多大空间:regular(默认,高 48)或 compact(高 32,图标 20,即菜单的紧凑行),后者适用于用鼠标操作、列表很长的工具。文字大小不变,无论密度如何,容器都由 size 决定。触摸时选项绝不低于触摸目标。

值为空字符串的选项与其他选项一样是一个选择,用于“无”或“不限”。下拉选择框自己没有回到“尚未选择”的办法:允许不作选择的地方,请提供一个表明这一点的选项。

加载中、失败和没有可选项

如果选项来自需要时间或可能失败的来源,请在列表里说明。status、statusText 和 emptyText 就是组合框和搜索的属性,取值和含义都相同。

你设置的 列表在选项之后显示的
status="loading" 和 statusText 一个小的加载指示器,以 statusText 命名(“正在加载项目”)。列表标记为忙碌,不播报任何内容。已有的选项仍然可以选择
status="error" 和 statusText 以错误颜色显示的文本,立即朗读(“无法加载项目”)
只有 statusText 该文本,礼貌地朗读(“你最近的 3 个项目”)
emptyText,在没有选项时 代替选项显示的文本(“还没有项目”)

这一状态行在选项滚动时始终可见。它不是选项:按它不会有任何反应,方向键也不会移到它上面。列表打开时它已显示的内容,会在列表打开时朗读。

下拉选择框自己从不获取、等待或重试。开始时把 status 设为 loading,选项到达后提供选项并改回 idle;失败时设为 error,并用文字说明失败。

没有选项、没有状态也没有 emptyText 的下拉选择框不会打开。请给它 emptyText,或用 setWords() 一次性提供产品的 noOptionsText,让打开它的人知道为什么没有可选项。

加载中、失败和没有可选项

打开、移动和选择

输入 发生什么
按下容器;Enter、Space、下方向键或上方向键 打开列表。活动选项是选中的那一个;没有选中的选项时,是第一个启用的选项
一个可打印的字符 打开列表,并让下一个以所键入内容开头的选项成为活动选项
下方向键、上方向键 下一个或上一个选项,可以跨组,到头时停住
Home、End;Page Down、Page Up 第一个或最后一个选项;向下或向上移动十个选项
Enter 或 Space;按下某个选项 选择活动选项并关闭列表
Escape;Tab;在外部按下;按下容器 关闭列表,不选择任何选项

只有用户在打开的列表中确认某个选项(按下它,或者按 Enter 或 Space)时,值才会改变。在关闭的下拉选择框上按方向键或键入字符,会打开列表,而不是在用户看不到的情况下改变值,因为一次 change 可能会保存数据、跳转或重建页面。

toggle 带有关闭的原因:select、escape、outside、focus-out、anchor-hidden、press 或 tab。

构成

部件 说明
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 · 可选 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 · 可选 The line along the bottom of a filled select; its focused state is thicker and colored.
4 label · 可选 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 · 可选 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 · 可选 A hint shown in the line while nothing is chosen; never a label.
8 leading-icon · 可选 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 · 可选 A line below an option's label that says more about the choice. It is shown in the list only.
14 option-leading-icon · 可选 A glyph before an option's label. Its column is kept in every row of a list where any option has one.
15 mark · 可选 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 · 可选 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 · 可选 The heading above a group's options. It names the group, takes no focus and is not a choice.
18 divider · 可选 The hairline between two groups while the separation is divider. Under gap there is none.
19 status · 可选 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.

属性

属性 类型 默认值 说明
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.

插槽

插槽 说明
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.

事件

事件 说明
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.

条目

该组件重复排列的条目种类。测试样例按顺序列出条目;各平台以各自的惯用方式为每个种类命名。

option

One choice of the select: a label, the value it stands for, and optionally a supporting line and a glyph.

属性 类型 默认值 说明
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.
规格 Web SwiftUI Jetpack Compose Flutter React Native
条目 tanrei-option TanreiSelectOption TanreiSelectOption TanreiSelectOption SelectOption
label · 属性 label title text label label
value · 属性 value tag value value value
supportingText · 属性 supportingText supportingText supportingText supportingText supportingText
leadingIcon · 属性 leadingIcon leadingIcon leadingIcon leadingIcon leadingIcon
disabled · 属性 disabled disabled enabled enabled disabled
备注 <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 · 分组 — 包含条目

A run of related options, optionally titled. The select separates a group from what is next to it by a divider or a gap.

属性 类型 默认值 说明
title string "" A short heading drawn above the group's options, which also names the group; empty draws none and leaves the group unnamed.
规格 Web SwiftUI Jetpack Compose Flutter React Native
条目 tanrei-option-group TanreiSelectSection TanreiSelectGroup TanreiSelectGroup SelectOptionGroup
title · 属性 label title title title title
备注 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.

状态

行为

以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。

ID 需求
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.

各平台名称对照

对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。

规格 Web SwiftUI Jetpack Compose Flutter React Native
组件 tanrei-select TanreiSelect TanreiSelect TanreiSelect Select
variant · 属性 variant variant variant variant variant
size · 属性 size size size size size
value · 属性 value selection value value value
label · 属性 label label label labelText label
labelHidden · 属性 labelHidden labelsHidden labelHidden labelHidden labelHidden
placeholder · 属性 placeholder prompt placeholder hintText placeholder
error · 属性 error error isError isError error
disabled · 属性 disabled disabled enabled enabled disabled
readOnly · 属性 readOnly readOnly readOnly readOnly readOnly
open · 属性 open isPresented expanded controller open
separation · 属性 separation separation separation separation separation
iconSpace · 属性 iconSpace iconSpace iconSpace iconSpace iconSpace
density · 属性 density density density density density
status · 属性 status status status status status
statusText · 属性 statusText statusText statusText statusText statusText
emptyText · 属性 emptyText emptyText emptyText emptyText emptyText
change · 事件 change selection onValueChange onChanged onValueChange
toggle · 事件 toggle isPresented onExpandedChange onOpenChanged onOpenChange
备注 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.

令牌

令牌 引用 解析值(浅色)
--tanrei-comp-select-disabled-opacity — 1
--tanrei-comp-select-divider-color sys.color.outline-variant #000000 · 8%
--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 — #000000 · 0%
--tanrei-comp-select-filled-disabled-option-label-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-option-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-option-state-layer-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-option-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-selected-option-container-color — #1b1b1b · 0%
--tanrei-comp-select-filled-disabled-selected-option-label-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-selected-option-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-selected-option-mark-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-selected-option-state-layer-color — #1b1b1b · 38%
--tanrei-comp-select-filled-disabled-selected-option-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-select-filled-focused-option-container-color — #1b1b1b · 10%
--tanrei-comp-select-filled-focused-option-label-color — #1b1b1b
--tanrei-comp-select-filled-focused-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-filled-focused-option-state-layer-color — #1b1b1b
--tanrei-comp-select-filled-focused-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-filled-focused-selected-option-container-color — #262626 · 10%
--tanrei-comp-select-filled-focused-selected-option-label-color — #262626
--tanrei-comp-select-filled-focused-selected-option-leading-icon-color — #262626
--tanrei-comp-select-filled-focused-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-filled-focused-selected-option-state-layer-color — #262626
--tanrei-comp-select-filled-focused-selected-option-supporting-text-color — #262626
--tanrei-comp-select-filled-hovered-option-container-color — #1b1b1b · 8%
--tanrei-comp-select-filled-hovered-option-label-color — #1b1b1b
--tanrei-comp-select-filled-hovered-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-filled-hovered-option-state-layer-color — #1b1b1b
--tanrei-comp-select-filled-hovered-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-filled-hovered-selected-option-container-color — #262626 · 8%
--tanrei-comp-select-filled-hovered-selected-option-label-color — #262626
--tanrei-comp-select-filled-hovered-selected-option-leading-icon-color — #262626
--tanrei-comp-select-filled-hovered-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-filled-hovered-selected-option-state-layer-color — #262626
--tanrei-comp-select-filled-hovered-selected-option-supporting-text-color — #262626
--tanrei-comp-select-filled-option-container-color — #000000 · 0%
--tanrei-comp-select-filled-option-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-select-filled-option-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-select-filled-option-state-layer-color comp.select.filled.option.label.color #1b1b1b
--tanrei-comp-select-filled-option-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-select-filled-pressed-option-container-color — #1b1b1b · 10%
--tanrei-comp-select-filled-pressed-option-label-color — #1b1b1b
--tanrei-comp-select-filled-pressed-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-filled-pressed-option-state-layer-color — #1b1b1b
--tanrei-comp-select-filled-pressed-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-filled-pressed-selected-option-container-color — #262626 · 10%
--tanrei-comp-select-filled-pressed-selected-option-label-color — #262626
--tanrei-comp-select-filled-pressed-selected-option-leading-icon-color — #262626
--tanrei-comp-select-filled-pressed-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-filled-pressed-selected-option-state-layer-color — #262626
--tanrei-comp-select-filled-pressed-selected-option-supporting-text-color — #262626
--tanrei-comp-select-filled-selected-option-container-color sys.color.secondary-container #262626 · 0%
--tanrei-comp-select-filled-selected-option-label-color sys.color.on-secondary-container #262626
--tanrei-comp-select-filled-selected-option-leading-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-select-filled-selected-option-mark-color sys.color.primary #1b1b1b
--tanrei-comp-select-filled-selected-option-state-layer-color comp.select.filled.selected-option.label.color #262626
--tanrei-comp-select-filled-selected-option-supporting-text-color sys.color.on-secondary-container #262626
--tanrei-comp-select-focused-opacity — 1
--tanrei-comp-select-group-title-color sys.color.on-surface-variant #4b4b4b
--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 #ffffff
--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 — #000000 · 0%
--tanrei-comp-select-outlined-disabled-option-label-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-option-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-option-state-layer-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-option-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-selected-option-container-color — #1b1b1b · 0%
--tanrei-comp-select-outlined-disabled-selected-option-label-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-selected-option-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-selected-option-mark-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-selected-option-state-layer-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-disabled-selected-option-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-select-outlined-focused-option-container-color — #1b1b1b · 10%
--tanrei-comp-select-outlined-focused-option-label-color — #1b1b1b
--tanrei-comp-select-outlined-focused-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-outlined-focused-option-state-layer-color — #1b1b1b
--tanrei-comp-select-outlined-focused-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-outlined-focused-selected-option-container-color — #262626 · 10%
--tanrei-comp-select-outlined-focused-selected-option-label-color — #262626
--tanrei-comp-select-outlined-focused-selected-option-leading-icon-color — #262626
--tanrei-comp-select-outlined-focused-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-outlined-focused-selected-option-state-layer-color — #262626
--tanrei-comp-select-outlined-focused-selected-option-supporting-text-color — #262626
--tanrei-comp-select-outlined-hovered-option-container-color — #1b1b1b · 8%
--tanrei-comp-select-outlined-hovered-option-label-color — #1b1b1b
--tanrei-comp-select-outlined-hovered-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-outlined-hovered-option-state-layer-color — #1b1b1b
--tanrei-comp-select-outlined-hovered-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-outlined-hovered-selected-option-container-color — #262626 · 8%
--tanrei-comp-select-outlined-hovered-selected-option-label-color — #262626
--tanrei-comp-select-outlined-hovered-selected-option-leading-icon-color — #262626
--tanrei-comp-select-outlined-hovered-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-outlined-hovered-selected-option-state-layer-color — #262626
--tanrei-comp-select-outlined-hovered-selected-option-supporting-text-color — #262626
--tanrei-comp-select-outlined-option-container-color — #000000 · 0%
--tanrei-comp-select-outlined-option-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-select-outlined-option-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-select-outlined-option-state-layer-color comp.select.outlined.option.label.color #1b1b1b
--tanrei-comp-select-outlined-option-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-select-outlined-pressed-option-container-color — #1b1b1b · 10%
--tanrei-comp-select-outlined-pressed-option-label-color — #1b1b1b
--tanrei-comp-select-outlined-pressed-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-outlined-pressed-option-state-layer-color — #1b1b1b
--tanrei-comp-select-outlined-pressed-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-outlined-pressed-selected-option-container-color — #262626 · 10%
--tanrei-comp-select-outlined-pressed-selected-option-label-color — #262626
--tanrei-comp-select-outlined-pressed-selected-option-leading-icon-color — #262626
--tanrei-comp-select-outlined-pressed-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-outlined-pressed-selected-option-state-layer-color — #262626
--tanrei-comp-select-outlined-pressed-selected-option-supporting-text-color — #262626
--tanrei-comp-select-outlined-selected-option-container-color sys.color.secondary-container #262626 · 0%
--tanrei-comp-select-outlined-selected-option-label-color sys.color.on-secondary-container #262626
--tanrei-comp-select-outlined-selected-option-leading-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-select-outlined-selected-option-mark-color sys.color.primary #1b1b1b
--tanrei-comp-select-outlined-selected-option-state-layer-color comp.select.outlined.selected-option.label.color #262626
--tanrei-comp-select-outlined-selected-option-supporting-text-color sys.color.on-secondary-container #262626
--tanrei-comp-select-pressed-opacity — 1
--tanrei-comp-select-separation sys.behavior.separation 0
--tanrei-comp-select-stacked-disabled-option-container-color — #000000 · 0%
--tanrei-comp-select-stacked-disabled-option-label-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-option-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-option-state-layer-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-option-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-selected-option-container-color — #1b1b1b · 0%
--tanrei-comp-select-stacked-disabled-selected-option-label-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-selected-option-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-selected-option-mark-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-selected-option-state-layer-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-disabled-selected-option-supporting-text-color — #1b1b1b · 38%
--tanrei-comp-select-stacked-focused-option-container-color — #1b1b1b · 10%
--tanrei-comp-select-stacked-focused-option-label-color — #1b1b1b
--tanrei-comp-select-stacked-focused-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-stacked-focused-option-state-layer-color — #1b1b1b
--tanrei-comp-select-stacked-focused-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-stacked-focused-selected-option-container-color — #262626 · 10%
--tanrei-comp-select-stacked-focused-selected-option-label-color — #262626
--tanrei-comp-select-stacked-focused-selected-option-leading-icon-color — #262626
--tanrei-comp-select-stacked-focused-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-stacked-focused-selected-option-state-layer-color — #262626
--tanrei-comp-select-stacked-focused-selected-option-supporting-text-color — #262626
--tanrei-comp-select-stacked-hovered-option-container-color — #1b1b1b · 8%
--tanrei-comp-select-stacked-hovered-option-label-color — #1b1b1b
--tanrei-comp-select-stacked-hovered-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-stacked-hovered-option-state-layer-color — #1b1b1b
--tanrei-comp-select-stacked-hovered-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-stacked-hovered-selected-option-container-color — #262626 · 8%
--tanrei-comp-select-stacked-hovered-selected-option-label-color — #262626
--tanrei-comp-select-stacked-hovered-selected-option-leading-icon-color — #262626
--tanrei-comp-select-stacked-hovered-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-stacked-hovered-selected-option-state-layer-color — #262626
--tanrei-comp-select-stacked-hovered-selected-option-supporting-text-color — #262626
--tanrei-comp-select-stacked-option-container-color — #000000 · 0%
--tanrei-comp-select-stacked-option-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-select-stacked-option-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-select-stacked-option-state-layer-color comp.select.stacked.option.label.color #1b1b1b
--tanrei-comp-select-stacked-option-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-select-stacked-pressed-option-container-color — #1b1b1b · 10%
--tanrei-comp-select-stacked-pressed-option-label-color — #1b1b1b
--tanrei-comp-select-stacked-pressed-option-leading-icon-color — #4b4b4b
--tanrei-comp-select-stacked-pressed-option-state-layer-color — #1b1b1b
--tanrei-comp-select-stacked-pressed-option-supporting-text-color — #4b4b4b
--tanrei-comp-select-stacked-pressed-selected-option-container-color — #262626 · 10%
--tanrei-comp-select-stacked-pressed-selected-option-label-color — #262626
--tanrei-comp-select-stacked-pressed-selected-option-leading-icon-color — #262626
--tanrei-comp-select-stacked-pressed-selected-option-mark-color — #1b1b1b
--tanrei-comp-select-stacked-pressed-selected-option-state-layer-color — #262626
--tanrei-comp-select-stacked-pressed-selected-option-supporting-text-color — #262626
--tanrei-comp-select-stacked-selected-option-container-color sys.color.secondary-container #262626 · 0%
--tanrei-comp-select-stacked-selected-option-label-color sys.color.on-secondary-container #262626
--tanrei-comp-select-stacked-selected-option-leading-icon-color sys.color.on-secondary-container #262626
--tanrei-comp-select-stacked-selected-option-mark-color sys.color.primary #1b1b1b
--tanrei-comp-select-stacked-selected-option-state-layer-color comp.select.stacked.selected-option.label.color #262626
--tanrei-comp-select-stacked-selected-option-supporting-text-color sys.color.on-secondary-container #262626
--tanrei-comp-select-status-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-select-status-error-color sys.color.error #cd262f
--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 #848484
--tanrei-comp-input-box-filled-affix-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-filled-caret-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-filled-container-color sys.color.surface-container-highest #eeeeee
--tanrei-comp-input-box-filled-disabled-active-indicator-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-affix-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-caret-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-container-color — #eeeeee
--tanrei-comp-input-box-filled-disabled-error-active-indicator-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-affix-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-caret-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-container-color — #eeeeee
--tanrei-comp-input-box-filled-disabled-error-input-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-label-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-disabled-error-placeholder-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-required-asterisk-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-state-layer-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-error-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-input-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-disabled-placeholder-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-required-asterisk-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-filled-error-active-indicator-color sys.color.error #cd262f
--tanrei-comp-input-box-filled-error-affix-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-filled-error-caret-color sys.color.error #cd262f
--tanrei-comp-input-box-filled-error-container-color sys.color.surface-container-highest #eeeeee
--tanrei-comp-input-box-filled-error-input-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-filled-error-label-color sys.color.error #cd262f
--tanrei-comp-input-box-filled-error-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-filled-error-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-error-placeholder-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-filled-error-required-asterisk-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-filled-error-state-layer-color comp.input-box.filled.error.input.color #1b1b1b
--tanrei-comp-input-box-filled-error-trailing-icon-color sys.color.error #cd262f
--tanrei-comp-input-box-filled-focused-active-indicator-color — #0070ac
--tanrei-comp-input-box-filled-focused-affix-color — #4b4b4b
--tanrei-comp-input-box-filled-focused-caret-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-container-color — #eeeeee
--tanrei-comp-input-box-filled-focused-error-active-indicator-color — #cd262f
--tanrei-comp-input-box-filled-focused-error-affix-color — #4b4b4b
--tanrei-comp-input-box-filled-focused-error-caret-color — #cd262f
--tanrei-comp-input-box-filled-focused-error-container-color — #eeeeee
--tanrei-comp-input-box-filled-focused-error-input-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-error-label-color — #cd262f
--tanrei-comp-input-box-filled-focused-error-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-filled-focused-error-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-focused-error-placeholder-color — #4b4b4b
--tanrei-comp-input-box-filled-focused-error-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-error-state-layer-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-error-trailing-icon-color — #cd262f
--tanrei-comp-input-box-filled-focused-input-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-label-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-filled-focused-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-focused-placeholder-color — #4b4b4b
--tanrei-comp-input-box-filled-focused-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-state-layer-color — #1b1b1b
--tanrei-comp-input-box-filled-focused-trailing-icon-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-active-indicator-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-affix-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-caret-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-container-color — #dddddd
--tanrei-comp-input-box-filled-hovered-error-active-indicator-color — #cd262f
--tanrei-comp-input-box-filled-hovered-error-affix-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-error-caret-color — #cd262f
--tanrei-comp-input-box-filled-hovered-error-container-color — #dddddd
--tanrei-comp-input-box-filled-hovered-error-input-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-error-label-color — #cd262f
--tanrei-comp-input-box-filled-hovered-error-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-error-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-hovered-error-placeholder-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-error-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-error-state-layer-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-error-trailing-icon-color — #cd262f
--tanrei-comp-input-box-filled-hovered-input-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-label-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-hovered-placeholder-color — #4b4b4b
--tanrei-comp-input-box-filled-hovered-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-state-layer-color — #1b1b1b
--tanrei-comp-input-box-filled-hovered-trailing-icon-color — #4b4b4b
--tanrei-comp-input-box-filled-input-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-filled-label-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-filled-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-filled-outline-color — #000000 · 0%
--tanrei-comp-input-box-filled-placeholder-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-filled-required-asterisk-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-filled-state-layer-color comp.input-box.filled.input.color #1b1b1b
--tanrei-comp-input-box-filled-trailing-icon-color sys.color.on-surface-variant #4b4b4b
--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 — #000000 · 0%
--tanrei-comp-input-box-outlined-affix-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-outlined-caret-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-outlined-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-disabled-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-outlined-disabled-affix-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-caret-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-disabled-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-outlined-disabled-error-affix-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-caret-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-disabled-error-input-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-label-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-outline-color — #1b1b1b · 12%
--tanrei-comp-input-box-outlined-disabled-error-placeholder-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-required-asterisk-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-state-layer-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-error-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-input-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-outline-color — #1b1b1b · 12%
--tanrei-comp-input-box-outlined-disabled-placeholder-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-required-asterisk-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-outlined-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-outlined-error-affix-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-outlined-error-caret-color sys.color.error #cd262f
--tanrei-comp-input-box-outlined-error-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-error-input-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-outlined-error-label-color sys.color.error #cd262f
--tanrei-comp-input-box-outlined-error-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-outlined-error-outline-color sys.color.error #cd262f
--tanrei-comp-input-box-outlined-error-placeholder-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-outlined-error-required-asterisk-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-outlined-error-state-layer-color comp.input-box.outlined.error.input.color #1b1b1b
--tanrei-comp-input-box-outlined-error-trailing-icon-color sys.color.error #cd262f
--tanrei-comp-input-box-outlined-focused-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-outlined-focused-affix-color — #4b4b4b
--tanrei-comp-input-box-outlined-focused-caret-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-focused-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-outlined-focused-error-affix-color — #4b4b4b
--tanrei-comp-input-box-outlined-focused-error-caret-color — #cd262f
--tanrei-comp-input-box-outlined-focused-error-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-focused-error-input-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-error-label-color — #cd262f
--tanrei-comp-input-box-outlined-focused-error-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-outlined-focused-error-outline-color — #cd262f
--tanrei-comp-input-box-outlined-focused-error-placeholder-color — #4b4b4b
--tanrei-comp-input-box-outlined-focused-error-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-error-state-layer-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-error-trailing-icon-color — #cd262f
--tanrei-comp-input-box-outlined-focused-input-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-label-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-outlined-focused-outline-color — #0070ac
--tanrei-comp-input-box-outlined-focused-placeholder-color — #4b4b4b
--tanrei-comp-input-box-outlined-focused-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-state-layer-color — #1b1b1b
--tanrei-comp-input-box-outlined-focused-trailing-icon-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-outlined-hovered-affix-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-caret-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-hovered-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-outlined-hovered-error-affix-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-error-caret-color — #cd262f
--tanrei-comp-input-box-outlined-hovered-error-container-color — #000000 · 0%
--tanrei-comp-input-box-outlined-hovered-error-input-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-error-label-color — #cd262f
--tanrei-comp-input-box-outlined-hovered-error-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-error-outline-color — #cd262f
--tanrei-comp-input-box-outlined-hovered-error-placeholder-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-error-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-error-state-layer-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-error-trailing-icon-color — #cd262f
--tanrei-comp-input-box-outlined-hovered-input-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-label-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-outline-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-placeholder-color — #4b4b4b
--tanrei-comp-input-box-outlined-hovered-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-state-layer-color — #1b1b1b
--tanrei-comp-input-box-outlined-hovered-trailing-icon-color — #4b4b4b
--tanrei-comp-input-box-outlined-input-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-outlined-label-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-outlined-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-outlined-outline-color sys.color.outline #848484
--tanrei-comp-input-box-outlined-placeholder-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-outlined-required-asterisk-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-outlined-state-layer-color comp.input-box.outlined.input.color #1b1b1b
--tanrei-comp-input-box-outlined-trailing-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-requirement-asterisk-gap sys.space.space25 2px
--tanrei-comp-input-box-selection-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-selection-content-color sys.color.on-primary #ffffff
--tanrei-comp-input-box-stacked-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-stacked-affix-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-stacked-caret-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-stacked-container-color sys.color.surface-container-highest #eeeeee
--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 — #000000 · 0%
--tanrei-comp-input-box-stacked-disabled-affix-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-caret-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-container-color — #eeeeee
--tanrei-comp-input-box-stacked-disabled-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-stacked-disabled-error-affix-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-caret-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-container-color — #eeeeee
--tanrei-comp-input-box-stacked-disabled-error-input-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-label-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-outline-color — #000000 · 0%
--tanrei-comp-input-box-stacked-disabled-error-placeholder-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-required-asterisk-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-state-layer-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-error-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-input-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-leading-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-outline-color — #000000 · 0%
--tanrei-comp-input-box-stacked-disabled-placeholder-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-required-asterisk-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-disabled-trailing-icon-color — #1b1b1b · 38%
--tanrei-comp-input-box-stacked-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-stacked-error-affix-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-stacked-error-caret-color sys.color.error #cd262f
--tanrei-comp-input-box-stacked-error-container-color sys.color.surface-container-highest #eeeeee
--tanrei-comp-input-box-stacked-error-input-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-stacked-error-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-stacked-error-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-stacked-error-outline-color sys.color.error #cd262f
--tanrei-comp-input-box-stacked-error-placeholder-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-stacked-error-required-asterisk-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-stacked-error-state-layer-color comp.input-box.stacked.error.input.color #1b1b1b
--tanrei-comp-input-box-stacked-error-trailing-icon-color sys.color.error #cd262f
--tanrei-comp-input-box-stacked-focused-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-stacked-focused-affix-color — #4b4b4b
--tanrei-comp-input-box-stacked-focused-caret-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-container-color — #eeeeee
--tanrei-comp-input-box-stacked-focused-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-stacked-focused-error-affix-color — #4b4b4b
--tanrei-comp-input-box-stacked-focused-error-caret-color — #cd262f
--tanrei-comp-input-box-stacked-focused-error-container-color — #eeeeee
--tanrei-comp-input-box-stacked-focused-error-input-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-error-label-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-error-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-stacked-focused-error-outline-color — #cd262f
--tanrei-comp-input-box-stacked-focused-error-placeholder-color — #4b4b4b
--tanrei-comp-input-box-stacked-focused-error-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-error-state-layer-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-error-trailing-icon-color — #cd262f
--tanrei-comp-input-box-stacked-focused-input-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-label-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-stacked-focused-outline-color — #0070ac
--tanrei-comp-input-box-stacked-focused-placeholder-color — #4b4b4b
--tanrei-comp-input-box-stacked-focused-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-state-layer-color — #1b1b1b
--tanrei-comp-input-box-stacked-focused-trailing-icon-color — #4b4b4b
--tanrei-comp-input-box-stacked-hovered-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-stacked-hovered-affix-color — #4b4b4b
--tanrei-comp-input-box-stacked-hovered-caret-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-container-color — #dddddd
--tanrei-comp-input-box-stacked-hovered-error-active-indicator-color — #000000 · 0%
--tanrei-comp-input-box-stacked-hovered-error-affix-color — #4b4b4b
--tanrei-comp-input-box-stacked-hovered-error-caret-color — #cd262f
--tanrei-comp-input-box-stacked-hovered-error-container-color — #dddddd
--tanrei-comp-input-box-stacked-hovered-error-input-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-error-label-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-error-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-stacked-hovered-error-outline-color — #cd262f
--tanrei-comp-input-box-stacked-hovered-error-placeholder-color — #4b4b4b
--tanrei-comp-input-box-stacked-hovered-error-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-error-state-layer-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-error-trailing-icon-color — #cd262f
--tanrei-comp-input-box-stacked-hovered-input-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-label-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-leading-icon-color — #4b4b4b
--tanrei-comp-input-box-stacked-hovered-outline-color — #000000 · 0%
--tanrei-comp-input-box-stacked-hovered-placeholder-color — #4b4b4b
--tanrei-comp-input-box-stacked-hovered-required-asterisk-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-state-layer-color — #1b1b1b
--tanrei-comp-input-box-stacked-hovered-trailing-icon-color — #4b4b4b
--tanrei-comp-input-box-stacked-input-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-stacked-label-color sys.color.on-surface #1b1b1b
--tanrei-comp-input-box-stacked-leading-icon-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-stacked-outline-color — #000000 · 0%
--tanrei-comp-input-box-stacked-placeholder-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-input-box-stacked-required-asterisk-color sys.color.primary #1b1b1b
--tanrei-comp-input-box-stacked-state-layer-color comp.input-box.stacked.input.color #1b1b1b
--tanrei-comp-input-box-stacked-trailing-icon-color sys.color.on-surface-variant #4b4b4b
--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

测试样例

作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。

生成自 specs/select.spec.json, tokens/comp/select.tokens.json, tokens/comp/input-box.tokens.json, design/tokens/

Web 上绘制方式的不同之处

  • <tanrei-option> 和 <tanrei-option-group> 是数据:它们什么都不画。选项的标签是 label 属性(attribute);没有这个属性时,是元素的文本。组的标题是它的 label 属性(attribute)。
  • 选项是下拉选择框的子元素。它的子元素中的 <slot> 代表分配给这个插槽的元素,所以你自己的元素可以把选项转交给它:在自己的 shadow root 里写 <tanrei-select label="套餐"><slot></slot></tanrei-select>。下拉选择字段正是这样做的。
  • 下拉选择框是与表单关联的元素。它接受 name,提交 value,重置时回到 value 属性(attribute)的值,并在包住它的字段说它必填且 value 为空时向表单报告缺少值。
  • 单独使用的下拉选择框是一个行内的方框;在字段里,它是一个填满字段的块。
  • change 是一个 composed 并且冒泡的事件。toggle 是 composed 的,但不冒泡:在下拉选择框上能监听到,在包住它的下拉选择字段上也能监听到,而 <details> 这样的祖先元素绝不会收到。
  • 列表是顶层(top layer)中的 popover="manual" 元素。在外部按下会关闭它,这次按下也会传到它下方的元素。
  • 浮动标签(filled、outlined)位于容器内部,所以按下它会打开列表。
  • supporting-text、error-text、required、optional、required-mark、required-text 和 optional-text,以及给 label-icon 插槽的子元素,属于下拉选择字段和字段。写在下拉选择框上,它们什么都不绘制;调试构建会报告一次,并指出 <tanrei-select-field>。
  • 部件: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-text 为空时,从产品的文字(setWords({ noOptionsText }))读取。
  • 在元素定义之前,它的选项是页面上的普通文本。请隐藏 tanrei-select:not(:defined) > *,并预留高度。
  • Web 没有自己的返回手势,所以 toggle 绝不会带有原因 back。
  • readOnly 对应 readonly 属性(attribute)。

指南

下拉选择框、下拉选择字段,还是手写的字段

你需要 使用
标签、方框、帮助文本和错误 下拉选择字段
只要方框,用在它的标签位于别处的地方:栏、工具栏、表格的单元格 下拉选择框,由它自己的 label 命名
在框架里面、方框的旁边或下方放你自己的东西 一个字段,里面放一个下拉选择框和你自己的元素

下拉选择框、单选按钮、菜单还是组合框

方面 下拉选择框 单选按钮 菜单 组合框
里面是什么 封闭列表中的一个值 少数几个值中的一个 操作 通过键入找到的一个值
何时显示选项 按下之后 始终 按下之后 按下之后,经过筛选
是否属于表单 是 是 否 是
  1. 选项不超过五个左右、都很短,并且有地方放时,把它们全部显示出来:用单选按钮;如果切换的是视图,用标签页。
  2. 如果各项是执行操作,而不是代表一个值,那是菜单。选项是一个值,绝不是一个去处:一列目的地应该是菜单或链接。
  3. 如果列表长到用户宁愿键入也不愿滚动,或者要接受列表之外的值,那是组合框。
  4. 要选多个值时,用复选框,或者用可以多选的列表。

推荐

  • 给单独使用的下拉选择框一个说明选择的是什么的 label;有可见的标签时,用词与它相同。
  • 只有当看得见页面的用户能够分辨它设置的是什么时——凭它的位置、它的字形,或者它显示的值——才单独使用下拉选择框。
  • 按用户查找的方式排列选项:按字母顺序、按数字顺序,或者把常用的放在前面。
  • 给长列表分组,并给每个组一个简短的标题。
  • 选项的标签用几个词说不清楚的内容,写进这个选项的 supportingText。

避免

  • 不要为了省下标签的位置而在表单里单独使用下拉选择框。请使用下拉选择字段。
  • 不要在下拉选择框上写了 label,就指望它出现在方框上方。stacked 的下拉选择框不绘制标签:那是下拉选择字段的那一行。
  • 不要把占位符当作标签。一旦选择了某个选项,它就消失了。
  • 不要在没有说明的情况下,在 change 时跳转、提交或删除。用户在列表中移动,是为了阅读它。
  • 不要在字段里的下拉选择框上设置 label、error 或 disabled。请把它们写在字段上,这样标签和帮助文本才会随之变化。
  • 不要禁用一个选项,却不让用户知道原因。请把原因写在它的辅助文本里。

无障碍

角色
combobox
标注
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 32px · pointer 44px · spacing 8px · touch 48px

键盘操作

按键 动作
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.

备注

  • 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.

生成自 specs/select.spec.json, tokens/sys/measure.tokens.json

组件负责的部分

  • 触发器是一个不可编辑的组合框。它的名称是它自己的 label;它没有 label 时,是包住它的字段的标签;绝不是占位符。它的值是选中选项的标签。
  • 它的描述是它的字段给的:帮助文本或错误文本,以及选填的文字。
  • 处于错误状态时,它暴露为无效;它的字段说它必填时,暴露为必填;只读时暴露为只读;禁用时暴露为禁用;列表打开时,暴露为已展开。
  • 列表打开期间,焦点留在触发器上。活动选项是触发器的活动后代,所以用户阅读选项时,方框仍然保有自己的名称和状态。
  • 列表是一个列表框,它的名称与下拉选择框的名称相同。每个选项都有自己的名称、描述、是否选中、是否禁用,以及它在所有选项中的位置。组以它的标题命名。
  • 选中的选项靠对勾与其他选项区分,绝不只靠填充。
  • 字形、V 形箭头、对勾、分隔线以及浮动标签后面的星号对辅助技术隐藏。
  • 下拉选择框在焦点顺序中是一个停靠点。Tab 会关闭打开的列表,并继续前进。
  • Escape 关闭列表,并且不再向外传递,所以包含下拉选择框的对话框保持打开。
  • 小号下拉选择框高 40,但触发器的点击区域仍然达到最小目标;每个选项的高度也不小于最小目标。
  • 文本放大到 200% 时,选项和标题会换行。只有容器里的值保持在一行之内,而列表和辅助技术仍然给出完整的值。
  • 在强制颜色模式下,触发器和列表会多出一道边线,活动选项采用系统的高亮颜色,选中的选项靠对勾来表示。
  • 在从右到左的布局中,触发器和每一行都会镜像,每段文本保持自己的方向。V 形箭头和对勾不镜像。
  • 状态行是列表框之外的实时区域。它显示的内容会在列表打开时和变化时朗读:平时礼貌地朗读,来源失败时立即朗读。加载中列表框标记为忙碌,不播报任何内容。
  • 它自己不带任何文字。emptyText 为空时,从产品的文字(noOptionsText)读取。

Web 上的已知差距:

  • 两个组之间的分隔线没有作为分隔符暴露:列表框只能包含选项和组。
  • 键入查找按 JavaScript 的方式不区分大小写地比较标签,而不是按标签所用语言的规则。
  • 在元素定义之前,选项是页面上的文本,没有对应的控件。

需要你做的事

  • 用 label 为每个单独使用的下拉选择框命名,并为每个选项提供标签。下拉选择框旁边的可见文字并不能为它命名。
  • 单独使用的下拉选择框可能出错的地方,请用辅助技术读得到的文字说明哪里错了:一条与页面关联的、你自己的消息,或者包住下拉选择框的一个字段。
  • 在同一个下拉选择框里,让各个选项的值保持唯一。
  • 加载中请写 statusText,因为它是加载指示器的名称;可能没有选项的下拉选择框,请写 emptyText。