---
title: "颜色输入框"
description: "用户在里面设置一种颜色的那个方框——颜色的样本、可以阅读、键入和粘贴的文本值，以及一个用来请求选择器的按钮——它自己没有标签行、帮助文本和错误文本。用于已经为这种颜色命名的行或列，或者放进产品自己组合的字段里。"
url: "https://tanrei.chnotchy.com/zh-cn/components/color-input/"
last_updated: 2026-10-23T21:17:12.000Z
---

# 颜色输入框

> 用户在里面设置一种颜色的那个方框——颜色的样本、可以阅读、键入和粘贴的文本值，以及一个用来请求选择器的按钮——它自己没有标签行、帮助文本和错误文本。用于已经为这种颜色命名的行或列，或者放进产品自己组合的字段里。

> [!NOTE]
> **进阶**
>
> 大多数产品直接使用包含此组件的[Color field](https://tanrei.chnotchy.com/zh-cn/components/color-field/)。

## 概览

颜色输入框就是[颜色字段](https://tanrei.chnotchy.com/zh-cn/components/color-field/)里的那个方框，单独拿出来：容器、色块、以文本形式呈现的值，以及选择器按钮。它上方没有标签行，下方没有帮助文本和错误文本。

用户读到的标签位于别处时，就单独使用它：

| 位置 | 用户读到的标签 | 输入框的 `label` |
| --- | --- | --- |
| 一列颜色或调色板各个色调中的一行 | 这一行自己的文本 | 列表和行：“主色，色调 40” |
| 表格的单元格 | 列标题 | 列和行：“背景，深色主题” |
| 工具栏 | 旁边的文本或字形 | 这种颜色的用途：“高亮颜色” |

单独使用时，输入框需要它自己的 `label`。它就是无障碍名称。stacked 的输入框绝不会把它画出来；filled 或 outlined 的输入框把它画成浮在方框里的标签。

它是一个**高级**组件：一个不带任何附加功能的控件，供自己动手组合的产品使用。大多数表单需要的是[颜色字段](https://tanrei.chnotchy.com/zh-cn/components/color-field/)，也就是放在字段里的这个输入框，可以直接使用。

**使用它的组件**是[颜色字段](https://tanrei.chnotchy.com/zh-cn/components/color-field/)。要自己用字段框住它，就把它放进[字段](https://tanrei.chnotchy.com/zh-cn/components/field/)，由字段为它命名、描述它、把它标记为必填，并让它进入错误状态。

它是一个块，宽度与所在的位置相同，单独使用和放在字段里都是如此。它没有自己的宽度：想要窄一些的方框，就给它一个窄一些的位置。

在 Web 上它是 `<tanrei-color-input>`。其他平台尚未实现。

### 用法

`value` 是一个井号加六位小写的十六进制数字（`#0f766e`），或者用空字符串表示没有颜色。它绝不会是只键入了一半的文本：输入框里的内容不是颜色时，`value` 为空。

**Web**

```html
<!-- 调色板各个色调中的一行：这一行为色调命名，并给输入框宽度。 -->
<div class="tone-row">
  <span>色调 40</span>
  <tanrei-color-input name="primary-40" label="主色，色调 40" value="#0f766e"
    picker picker-label="选择“主色，色调 40”的颜色"></tanrei-color-input>
</div>

<script type="module">
  import '@tanrei/web/components/color-input';

  const tone = document.querySelector('[name="primary-40"]');
  // 值变了：一种颜色，或者空字符串。它已经是新的值。
  tone.addEventListener('input', () => preview(tone.value));
  // 用户确认了一个有变化的值：焦点离开、按下 Enter，或者选择器关闭。
  tone.addEventListener('change', () => save(tone.value));
  // 确认了一段不是颜色的文本。输入框保留这段文本，并画成错误状态。
  tone.addEventListener('entryerror', (e) => console.log(e.detail.text, e.detail.message));
</script>
```

**React**

```tsx
// 进阶组件（用于构成其他组件的部件）从 advanced 入口导入。
import { ColorInput } from '@tanrei/react/advanced';

<ColorInput label="主色，色调 40" value={tone} picker pickerLabel="选择“主色，色调 40”的颜色"
  onChange={(event) => setTone(event.currentTarget.value)} />
```

### 手写在字段里

用字段包住一个颜色输入框，就是把颜色字段拆开来写。输入框自己的 `label` 留空：由字段为它命名。`label`、`error`、`disabled` 和必填设置写在字段上；`name`、`value`、`invalidText` 和选择器写在输入框上。

```html
<tanrei-field label="品牌色" required required-text="必填"
  supporting-text="六位十六进制数字，例如 #0f766e。">
  <tanrei-color-input name="brand" value="#0f766e"
    invalid-text="请输入六位十六进制数字，例如 #0f766e。"
    picker picker-label="选择颜色"></tanrei-color-input>
</tanrei-field>
```

只有输入框知道的事，由它告诉所在的字段。字段的帮助文本从值的文本开始的位置开始，也就是色块之后。filled 或 outlined 的输入框自己画出字段的标签，让它浮在方框里，所以字段不绘制标签行。确认的文本不是颜色时，字段会进入错误状态，显示输入框的 `invalidText`。

## 规格

### 哪些属于输入框，哪些属于字段

| 方面 | 颜色输入框 | 包着它的字段 |
| --- | --- | --- |
| 绘制 | 方框、色块、以文本形式呈现的值、选择器按钮，以及浮在 filled 或 outlined 方框里的标签 | stacked 输入框上方的标签行、必填或选填标记、帮助文本、错误文本 |
| 保存 | 值、它在表单中的位置、事件、它呈现的选择器 | 什么都不保存 |
| 告诉辅助技术 | 以文本形式呈现的值；选择器按钮，作为一个单独的按钮 | 名称、帮助文本或错误文本、必填、无效、禁用 |

方框本身在单独使用、放在字段里和用在颜色字段里时都一样，只在颜色字段的页面上写一次：[方框的各个部件](https://tanrei.chnotchy.com/zh-cn/components/color-field/#%E5%AD%97%E6%AE%B5)、[值和文本](https://tanrei.chnotchy.com/zh-cn/components/color-field/#%E5%80%BC%E5%92%8C%E6%96%87%E6%9C%AC)，以及[选择器](https://tanrei.chnotchy.com/zh-cn/components/color-field/#%E9%80%89%E6%8B%A9%E5%99%A8)。

三种变体和三种尺寸——`small`（32）、`medium`（40）和 `large`（56）——都是文本输入框的。默认系统只提供 `stacked`。色块位于文本输入框放前置图标的位置，大小也与那个图标相同，所以颜色输入框放在带前置图标的文本输入框下方时，两者的起始边是对齐的。

### 单独使用，没有字段

- **名称**是 `label`。`labelHidden` 让 filled 或 outlined 的输入框不把它画出来。
- **错误**是 `error`，由你来设置：方框画成错误状态，并暴露为无效。输入框没有错误文本。请在它旁边用文字说明哪里不对，或者用字段框住它。
- **确认的文本不是颜色**时，方框画成错误状态，并触发 `entryError`。`invalidText` 会加进输入框的描述；没有任何东西把它画出来。
- **必填**要由字段来说明。单独使用的输入框没有必填设置：表单需要回答的地方，请自己校验。

- Category: Text inputs
- Level: base
- Listing: advanced
- Used in: Color field
- Page: https://tanrei.chnotchy.com/components/color-input/
- Spec: https://github.com/chnotchy/tanrei/blob/main/specs/color-input.spec.json

### Availability

| Design system | Has it | Notes |
| --- | --- | --- |
| Tanrei (`tanrei`) | Yes | Variants left out: `filled`, `outlined`. |
| Cupertino (Apple HIG-inspired) (`cupertino`) | Yes | — |
| M3 Expressive-like (Material 3 Expressive-inspired) (`m3-expressive-like`) | Yes | — |
| M3-like (Material 3-inspired) (`m3-like`) | Yes | — |

### Platforms

- Web: the element `<tanrei-color-input>`, registered by importing `@tanrei/web` (or only its own module, `@tanrei/web/components/color-input`).
- React: `ColorInput` from `@tanrei/react/advanced`.

| Platform | Name | Status |
| --- | --- | --- |
| Web | `tanrei-color-input` | lagging |
| SwiftUI | `TanreiColorInput` | planned |
| Jetpack Compose | `TanreiColorInput` | planned |
| Flutter | `TanreiColorInput` | planned |
| React Native | `ColorInput` | planned |

Names a platform gives in its own idiom:

| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| `value` (prop) | `value` | `hex` | `value` | `controller` | `value` |
| `label` (prop) | `label` | `label` | `label` | `semanticLabel` | `accessibilityLabel` |
| `labelHidden` (prop) | `labelHidden` | `labelsHidden` | `labelHidden` | `labelHidden` | `labelHidden` |
| `error` (prop) | `error` | `error` | `isError` | `isError` | `error` |
| `disabled` (prop) | `disabled` | `disabled` | `enabled` | `enabled` | `disabled` |
| `picker` (prop) | `picker` | `showsPicker` | `onPickerRequest` | `onPickerRequest` | `onPickerRequest` |
| `pickerLabel` (prop) | `pickerLabel` | `pickerLabel` | `pickerContentDescription` | `pickerSemanticLabel` | `pickerAccessibilityLabel` |
| `input` (event) | `input` | `hex` | `onValueChange` | `onChanged` | `onValueChange` |
| `change` (event) | `change` | `onCommit` | `onCommit` | `onCommit` | `onEndEditing` |
| `pickerRequest` (event) | `pickerrequest` | `onPickerRequest` | `onPickerRequest` | `onPickerRequest` | `onPickerRequest` |
| `entryError` (event) | `entryerror` | `onEntryError` | `onEntryError` | `onEntryError` | `onEntryError` |

Statuses: `planned` — In the inventory. Nothing shippable exists yet. `lagging` — An implementation of an earlier spec exists; it has not caught up with the current one. `alpha` — Implemented with tests. The API may still change. `beta` — Feature complete and at parity. Only fixes are expected. `stable` — Covered by semantic versioning. `deprecated` — Still shipped; scheduled for removal.

### Anatomy

- `container`: The input's box: it holds the swatch, the input and the picker button. 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.
- `outline` (optional): The border of an outlined color input, which opens a gap where the floated label crosses it; in a stacked color input, the line drawn inside the container's edge while it is focused or in error, which takes no layout space.
- `active-indicator` (optional): The line along the bottom of a filled color input; its focused state is thicker and colored.
- `label` (optional): In a filled or outlined input, its name drawn inside the box: it rests in the input line, after the swatch, while the input is empty and unfocused, and floats above the value otherwise. A stacked input draws none: the label above it is the row of the Field that frames it.
- `required-asterisk` (optional): After the floating label's text, an asterisk, where the Field that frames the input says the control is required and marks it (CLI-7). Kept from assistive technology.
- `swatch`: A sample of the value at the start of the container, where a text input's leading icon stands: a small square filled with exactly the value's color, with the one hairline inside its edge so that a color close to the box's own still shows where it ends. Empty, it is that edge alone. It is not a control, nothing opens from it, and it is hidden from assistive technology: the value's text says the color.
- `input`: The value as text, typed, pasted and read: a number sign and six hexadecimal digits. The platform's native text element.
- `picker-button` (optional): An icon-only Button of the text variant at the end of the container, showing the palette glyph: it asks for a picker. It is a Button with Button's own tokens, named by pickerLabel.
- `caret`: The text cursor of the input.

### Props

| Prop | Type | Default | Web attribute | Description |
| --- | --- | --- | --- | --- |
| `variant` | `enum`: `stacked`, `filled`, `outlined` | `"stacked"` | `variant` | The text input's three looks, so that a color input 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` | `enum`: `small`, `medium`, `large` | `"medium"` | `size` | 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. These are the text input's three sizes, small for a bar, a toolbar or a dense row of controls. The hit area always meets the minimum target. A design system may add values. |
| `value` | `string` | `""` | `value` | The color the input holds: a number sign and six hexadecimal digits in lowercase ("#0f766e"), an opaque sRGB color; or empty for no color. It is never a half-typed text: while the input holds something that is not a color, value is empty. |
| `label` | `string` | `""` | `label` | Names the input for assistive technology where no Field frames it; a filled or outlined input also draws it, as the label that floats in its box. Inside a Field leave it empty: the Field's label names the input, and is what floats. |
| `labelHidden` | `boolean` | `false` | `label-hidden` | Does not draw the floating label of a filled or outlined input, which stays the accessible name. Inside a Field, hide the label on the Field. A stacked input draws no label, so this changes nothing there. |
| `error` | `boolean` | `false` | `error` | Draws the input 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 input. What a color means is the product's to validate (too light for its background, already taken); the input never sets this itself, and enters an error of its own only for a committed text that is not a color (CLI-20). |
| `disabled` | `boolean` | `false` | `disabled` | Prevents focus, editing, the picker button and form submission. Inside a Field, disable the Field, so that its label follows. |
| `invalidText` | `string` | `""` | `invalid-text` | The words for an input committed with a text that is not a color ("Enter six hexadecimal digits, like #0f766e"), in the product's language: given to the Field that frames the input, which shows them as its error text (CLI-20). While it is empty that Field shows its own errorText. |
| `picker` | `boolean` | `false` | `picker` | Shows the picker button. Activated, it emits pickerRequest, and where the platform has a color picker of its own the input presents that picker unless the product cancelled the request. The input draws no picker surface itself. |
| `pickerLabel` | `string` | `""` | `picker-label` | The accessible name of the picker button ("Choose color"). Required while picker is shown. |

### Events

| Event | Description |
| --- | --- |
| `input` | The person changed the value — by an edit of the text that made it a color, another color or no color, or by a pick in the platform's picker; carries the new value. Not emitted when the product sets value, nor for an edit that leaves value as it was. |
| `change` | The person committed a changed value: focus left the input, the action key was pressed, or the platform's picker closed; carries the value. |
| `pickerRequest` | The person activated the picker button; carries the value. Cancelling it keeps the input from presenting the platform's picker: the product then presents its own surface and sets value to what was picked. |
| `entryError` | The person committed a text that is not empty and is not a color; carries the text as typed and invalidText, the words the input has for it (empty where the product gave none: the Field that frames the input then shows its own errorText). |

### States

`enabled`, `disabled`, `hovered`, `focused`, `error`

### Accessibility

- Role: `textbox`
- Labeling: The accessible name of the input is the label of the Field that frames it, or its own label where no Field does; a filled or outlined input draws that name floating, and labelHidden only stops the drawing. The input's value is the text it shows: the color as a number sign and six hexadecimal digits. Its description is what the Field gives — the supporting text with, in error, the error text after it and the optional text — and, where nothing frames it, invalidText while an entry error lasts. The input is exposed as invalid while error is true, its Field is in error or an entry error lasts; as required while its Field says so, whichever mark is drawn; and as disabled while it or its Field is disabled. The picker button is a separate button named by pickerLabel that opens a dialog. The swatch is decorative and hidden.
- Minimum target size: yes

| Keys | Action |
| --- | --- |
| Tab / Shift+Tab | Moves focus to the input, then to the picker button while it is shown, and on. |
| Text editing keys | Edit the text as in a text input; nothing is refused while typing. |
| Enter (the action key) | In the input: commits the text (change; an entry error for a text that is not a color) and, on the web inside a form, requests its submission. |
| Enter or Space on the picker button | Asks for a picker: emits pickerRequest and, unless cancelled, opens the platform's own color picker where there is one. |

- The color is never told by the swatch alone (WCAG 1.4.1): the value is text, read and typed, and the swatch is a sample of it for those who can see it. A person who cannot tell two samples apart reads two different values.
- A hexadecimal value says little about what the color looks like. Where that matters, the product names the color in the supportingText of the Field that frames the input ("Teal") or beside it; the color input ships no names, which would need a table in every language.
- The color input checks the form of what is typed, not its meaning: a text that is not a color raises an entry error after the person commits, never while they type (CLI-20), and the words for it are shown by the Field that frames the input. Whether a color suits its use — its contrast against a background, a color already taken — is the product's to say with the Field's error and errorText.
- The swatch's edge is the hairline, far below 3:1 against the container. That is within WCAG 1.4.11: the swatch is not a control and is not what identifies the value. The value's text is held to 4.5:1 as a text input's is.
- The picker is the platform's own where it has one, with the platform's own accessibility; where it has none the product's surface is a popover opened by the picker button, which is why the button is exposed as opening a dialog and receives focus back (CLI-28, CLI-29).
- The container extends its hit area to the minimum target (CLI-24), and the picker button is a Button with its own target (BTN-5). The swatch is not a target of its own: a press on it lands in the input.
- Text size: at 200% the container grows with the value's line and the swatch keeps its size (CLI-33); a floating label wraps and is never cut (CLI-15).
- Forced colors (CLI-34, CLI-35): the box and the value take the person's colors; the swatch alone keeps the value's color, because showing that color is what it is for.
- The input has no label row and no message of its own: a Field frames it and draws them (FLD-2 to FLD-11, FLD-25, FLD-26, FLD-28), or the product names it with label where its visible label is elsewhere. Unframed and unnamed, it is reported (CLI-11).
- 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.

### Behaviour

Requirements in EARS form, each with its stable id.

- **CLI-1** THE color input 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 color input's four corners and a filled color input's top corners rounded by comp.input-box.container.shape, a filled color input'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 swatch (CLI-25) at the start, comp.input-box.container.padding-inline-with-icon.medium from the container's start edge; the input (CLI-17) taking the remaining width, comp.input-box.container.gap.medium after the swatch; and WHILE the picker button is shown (CLI-27) that button at the end, the input ending comp.input-box.container.gap.medium before the button's glyph. WHILE the picker button is not shown the input SHALL end comp.input-box.container.padding-inline.medium from the container's end edge. 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). THE color input SHALL be as wide as its layout gives it.
- **CLI-2** THE color input SHALL measure as the text input of the same variant and size measures (TXI-16): the same container height, paddings and corners, the swatch where a text input's leading icon stands and at that icon's size, the same text style and start edge for the value as the input text of a text input that has a leading icon, and the picker button where a date input's picker button stands (TXI-32), so that a color input and a text input side by side share every horizontal line and a color input below a text input shares its start edges. Framed, the two stand under the same label row and over the same supporting row without any token held equal for it, because both rows are the one Field's (FLD-1). The two are drawn from one set of tokens (CLI-45): what a layer changes of the box, it changes for both.
- **CLI-7** WHERE variant is filled or outlined, WHILE the floating label is drawn (CLI-15) and the Field that frames the input gives it an asterisk as its required mark (FLD-25) THE color input 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, colored by its required-asterisk part (comp.input-box.outlined.required-asterisk.color for an outlined input; its error. part WHILE the input is in error), and SHALL keep it from assistive technology, because the required state already says it (CLI-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, as it reaches a text input's (TXI-50). WHILE no Field frames it, or the Field gives no mark, or the label is not drawn, THE color input SHALL draw no mark.
- **CLI-9** WHILE error is true — set by the product, or by the Field that frames it (FLD-8) — or the entry error of CLI-20 lasts THE color input SHALL draw every part of its container, 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 box itself is marked and not only a text below it, and SHALL expose its input as invalid to assistive technology; the swatch SHALL stay as it is drawn (CLI-25). THE color input SHALL never set error itself — what a color means is the product's to validate, and the one error the color input raises by itself is the entry error — 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 input as its description (FLD-7, FLD-11).
- **CLI-11** THE color input 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 the accessible name of its input, whether or not any label is drawn, and the Field's supporting text with, in error, the error text after it is its description; it has no description of its own but the words of an entry error where nothing frames it (CLI-20). WHILE label is not empty THE color input SHALL use it as the accessible name instead, for an input no Field frames or one whose visible label is elsewhere (a table column, a row of a list of colors that names each). WHILE it has either name THE color input SHALL expose that one alone, over a name the product gives it on the element (aria-label or aria-labelledby); 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 color input 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 color input SHALL expose its input 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, SHALL NOT validate because of one, and SHALL raise no entry error for an empty text (CLI-20).
- **CLI-13** THE color input SHALL add to the focus order its input and, WHILE the picker button is shown, that button after it, and no other stop: the swatch is not one (CLI-25). A press on the label row of the Field that frames it moves focus to its input and does nothing else — it asks for no picker and changes no value — and is that Field's (FLD-12).
- **CLI-15** WHERE variant is filled or outlined, WHILE the input is empty and not focused THE color input SHALL rest its label in the input line, after the swatch, in comp.input-box.label.typescale.medium for size medium (and so on), and otherwise SHALL float it as the text input of that variant floats its label (TXI-13): to the top of a filled container, above the input line, 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 color input 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 color input 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 or the swatch. The label it floats SHALL be its accessible name (CLI-11): the label of the Field that frames it, or its own label where none does. WHILE labelHidden is true — its own, or that Field's (FLD-25) — or it has no name THE color input SHALL draw no label. WHERE variant is stacked, or a variant laid out as stacked (CLI-41), THE color input SHALL draw no label at all: the label above it is the Field's row (FLD-2).
- **CLI-16** THE color input SHALL hold as its value an opaque sRGB color written as a number sign and six hexadecimal digits in lowercase, two each for red, green and blue (#0f766e), or the empty string for no color. It SHALL hold no other form: no alpha, no three-digit form (a commit reads three digits as the six they stand for, CLI-19), no color name, no functional notation and no other color space. value, and what input and change carry, SHALL always be one of those two.
- **CLI-17** THE color input SHALL show its value as text in its input, 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 color input; its error. part WHILE the color input is in error), and SHALL let the person edit that text freely, as the text of a text input: it SHALL NOT refuse, replace or reformat a character while the person types, SHALL NOT limit the text's length, and SHALL leave a composition to the platform's native text element (TXI-4). WHEN the user changes the text — by typing, deleting, pasting, cutting, dictating or an input method committing text — THE color input SHALL read the whole text as CLI-18 says and set value to the color it reads, or to the empty string WHILE it reads as no color; WHEN that changes value THE color input SHALL emit input once with the new value, and an edit that leaves value as it was SHALL emit nothing.
- **CLI-18** THE color input SHALL read a text as a color WHEN, with the white space before and after it left out, it is exactly six hexadecimal digits in either case, with or without one number sign before them (0F766E, #0f766e); the color is those digits in lowercase after a number sign. Every other text — an empty one, fewer or more digits, any other character — SHALL read as no color. WHEN the user pastes THE color input SHALL insert the pasted text as a text input does and read the whole text by the same rule, so that a color copied with a space around it, in capitals or without its number sign is taken.
- **CLI-19** WHEN the input loses focus, or the user presses the keyboard's action key, THE color input SHALL commit: WHILE the text reads as a color it SHALL rewrite the text to the value's own form — the number sign added, the digits in lowercase, the white space gone — and WHILE the text, with the white space before and after it left out, is exactly three hexadecimal digits in either case, with or without one number sign before them (#fff, F0A), it SHALL read it as the color whose six digits are each of the three written twice (#ffffff, #ff00aa), as CSS reads that form, make that color value, rewrite the text to it and emit input; only a commit reads three digits, because while the person types they are on their way to six and the swatch must not show a color nobody meant (CLI-18). WHILE the text is not empty and reads as no color it SHALL leave the text as typed and raise the entry error of CLI-20; in every case, WHEN value differs from the value last committed (the value when the input gained focus, or at the last change), it SHALL emit change once with value. On the web, inside a form, the action key SHALL then request the form's submission as a native input does. The submission SHALL be requested through the form's default button (BTN-30): WHILE the form has a submit button — the platform's own or a Button of type submit — the first of them in document order SHALL be pressed, so that its click is emitted and its name and value are sent, and WHILE that button is disabled nothing SHALL be submitted; only a form with no submit button is asked directly.
- **CLI-20** WHEN the color input commits (CLI-19) WHILE its text is not empty and reads as no color THE color input SHALL emit entryError once with the text as typed and invalidText, the words it has for the refusal, and enter an entry error: it SHALL render and be exposed exactly as WHILE error is true (CLI-9), and SHALL report the refusal to the Field that frames it with invalidText as its words, which that Field shows in its supporting row and announces (FLD-28: the Field's errorText while invalidText is empty); WHILE no Field frames it invalidText SHALL be the input's description. The entry error SHALL end with the next edit of the text, WHEN the product sets value (CLI-21) or WHEN a pick gives a color (CLI-29), and THE color input SHALL then withdraw its report; while it lasts, on the web, the color input SHALL report badInput to the form's constraint validation. WHILE invalidText is empty THE color input SHALL still mark itself, and WHILE no Field frames it SHALL in debug builds report an entry error that has no words; framed, the Field reports a refusal it has no words for (FLD-28). THE color input SHALL raise no entry error for an empty text, whether or not its Field says it is required (CLI-11), and none before a commit: a person who is still typing is not in error.
- **CLI-21** WHEN the product sets value THE color input SHALL show it in its input and its swatch, end any entry error, and SHALL NOT emit input, change or entryError. WHEN the product sets a text that CLI-18 reads as a color but that is not in the value's own form (capitals, no number sign) THE color input SHALL hold and show it in the value's own form; WHEN it sets a text that is not empty and reads as no color THE color input SHALL treat it as empty and in debug builds report it. WHEN the product sets value to what value already is THE color input SHALL leave the text as the person typed it, so that a product that echoes the empty value back while a person is half-way through a color does not clear what they entered.
- **CLI-22** WHILE hovered or focused THE color input SHALL render its container, outline, active indicator, floating label and its asterisk, input, caret 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; unlike a button, focus gained by pointer or touch SHALL select the focused state, because a field shows where typing will go however it was focused. THE color input SHALL count as focused WHILE its input has focus, and not WHILE its picker button has it: the button shows its own focus. 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, following its corners, 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 color input SHALL show the state layer on its container, behind the swatch and never over it (CLI-25). WHEN its state changes THE color input 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. Selected text SHALL be highlighted in comp.input-box.selection.color and drawn in comp.input-box.selection.content.color in every variant and state, and the caret drawn in the caret color of its variant (comp.input-box.stacked.caret.color for a stacked color input; its error. part WHILE the color input is in error). The rows around it take their focused colors from the Field that frames it (FLD-13).
- **CLI-23** THE color input SHALL show the focus of its input 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.
- **CLI-24** WHERE the container is shorter than the minimum target THE color input 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. WHEN the user presses anywhere on the color input outside its picker button — the container's padding, the swatch WHILE picker is false, a floating label — THE color input SHALL focus the input (WHILE picker is true a press on the swatch is the picker button's, CLI-25); it SHALL draw no press answer, because a press on a field is not an action. A press on the label row of the Field that frames it focuses the input too, and is that Field's (FLD-12).
- **CLI-25** THE color input SHALL draw its swatch as a square of side comp.input-box.icon.size.medium for size medium (and so on), its corners comp.color-input.swatch.shape, never more than half its side, eased by sys.shape.corner-smoothing. WHILE value is a color the swatch SHALL be filled with exactly that color, in sRGB, and edged inside its size by a line comp.color-input.swatch.outline.width wide in comp.color-input.swatch.outline.color, drawn over the fill, so that a color close to the container's — white in a light field — still shows where it ends. WHILE value is empty — no text, or a text that reads as no color — the swatch SHALL have no fill, the container showing through it, and the same edge, which is then all it draws. THE color input SHALL mix nothing into the swatch's fill: no state layer, no tint of a state or of the error condition, no opacity WHILE the color input is enabled, and no elevation. The swatch SHALL NOT be a control: it takes no focus and has no hover, focus or press answer. WHILE picker is false a press on it focuses the input (CLI-24). WHILE picker is true, WHEN the user presses the swatch with a pointer or by touch THE color input SHALL do what a press on the picker button does (CLI-28) and move no focus to the input, as a label passes a press to its control: the swatch is where the platform's own color input opens its picker, and a person reaches for it there; the keyboard's way, and the one focus stop for it, stays the picker button, so the swatch adds no stop and draws no answer; and THE color input SHALL keep it from assistive technology, because the value's text says the color and a sample alone says it to no one who cannot see it or tell it from another.
- **CLI-26** WHEN value changes — by an edit of the text, by a pick or because the product set it — THE color input SHALL draw the swatch in the new value at once, without a motion, so that the sample and the text never disagree.
- **CLI-27** WHILE picker is true THE color input SHALL draw a picker button at the end of its container: an icon-only Button of the text variant and of the Button size with the field's size name (Button's default size for a field size Button does not have), showing the palette glyph of the system's icon set, its glyph centred as far from the container's end edge as the swatch's centre is from the container's start edge. It SHALL be a Button in every respect — its own tokens, its press, focus ring and target (BTN-5, BTN-8, BTN-9, BTN-10) — named by pickerLabel (an empty pickerLabel reported in debug builds), exposed as opening a dialog, as the opener of a popover is (POP-18; aria-haspopup="dialog" on the web), and a focus stop after the input; WHILE the color input is disabled it SHALL be disabled with it. The palette glyph is not marked as mirrored and is never mirrored. WHILE picker is false THE color input SHALL draw no picker button, SHALL ignore pickerLabel and SHALL end its input as CLI-1 says.
- **CLI-28** WHEN the user activates the picker button THE color input SHALL emit pickerRequest once with value and then, unless the product cancelled that request, present the platform's own color picker WHERE the platform has one, showing value, or the picker's own starting color WHILE value is empty; it SHALL draw no picker surface of its own. WHERE the platform has no color picker of its own, or the product cancelled the request, THE color input SHALL present nothing, change nothing and move no focus: the product presents its own surface — anchored to the color input's container and opened by the picker button, as a popover is to its anchor and its opener (POP-18) — and sets value to what was picked (CLI-21), and that surface returns focus to the picker button when it closes (POP-22).
- **CLI-29** WHILE a platform picker that the color input presented is open, WHEN the person picks a color in it THE color input SHALL take the pick as an edit by the person: it SHALL write the color into its input in the value's own form, draw the swatch in it, end any entry error and emit input once with the new value, for every pick the platform reports, a drag across the picker included. A pick the value cannot hold exactly — one with an alpha, one outside sRGB — SHALL be held as the opaque sRGB color the platform converts it to. WHEN that picker closes THE color input SHALL emit change once WHEN value differs from the value last committed, and focus SHALL be on the picker button, as a popover returns focus to its opener (POP-22). THE color input SHALL leave the picker's look, placement, keys and dismissal to the platform.
- **CLI-30** THE color input SHALL give its input a general keyboard without autocapitalization, autocorrection, spell checking or suggestions, SHALL offer it no autofill, and SHALL expose it as a text field named as CLI-11 says whose value is the text it shows. It SHALL announce nothing of its own WHEN the value changes and SHALL add no name of the color in words: the text is the value, and a product that wants a name beside it ("Teal") gives it in the supportingText of the Field that frames the input, or beside an input nothing frames.
- **CLI-31** WHILE disabled is true — set by the product, or by the Field that frames it (FLD-14) — THE color input SHALL NOT be focusable or editable, nor its picker button, SHALL NOT emit input, change, pickerRequest or entryError, 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 container, the floating label and its asterisk included, from the disabled component tokens of its variant (of its error parts WHILE the color input is in error). It SHALL draw the swatch's fill at sys.state.disabled-content-opacity, as a disabled chip draws its avatar (CHP-6), and keep the swatch's edge.
- **CLI-33** WHILE the text size is enlarged THE color input SHALL grow its container with the input's line, keeping the height of its size as a minimum, SHALL keep the swatch at comp.input-box.icon.size.medium for size medium (and so on) and the picker button at its Button's size, both centred on the line, and SHALL keep the value on one line: WHERE the input is narrower than its text the text SHALL scroll inside the input, the caret kept in view, as in a single-line text input.
- **CLI-34** 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 color input SHALL draw the label that floats in its box and that label's asterisk 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 (CLI-9) and by the words of the Field that frames it, which that Field draws in the platform's text color (FLD-17).
- **CLI-35** WHERE the platform draws with a person's own forced colors THE color input 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, its value in the platform's text color, and a disabled color input in the platform's disabled text color. It SHALL still fill the swatch with the value's own color, kept out of the forced palette, because that color is the content the field shows and not a decoration of it, and SHALL draw the swatch's edge in the platform's text color, so that the sample is bounded on any background.
- **CLI-37** WHERE the layout direction is right-to-left THE color input SHALL mirror its container: the swatch at the right and the picker button at the left, all paddings applied by logical start and end, and the label of a filled or outlined color input floating from the right edge. It SHALL lay out the value left to right in every layout direction, aligned to the field's start edge — the right in a right-to-left layout — as a text input lays out an address or a number (TXI-24), because a number sign and its digits are one left-to-right token whose order a right-to-left context must not change.
- **CLI-38** THE color input 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 color input is shown THE color input SHALL draw itself for the new direction at once, without a motion of its own, keeping its value, its text as typed, its selection and caret, and its focus.
- **CLI-39** WHERE the layout direction is right-to-left THE color input SHALL mirror no drawing of its own: the swatch is never mirrored, and the palette glyph of its picker button is not one the icon set marks as mirrored (CLI-27). What changes with the direction is where its parts stand (CLI-37). The glyph before the label is the Field's, and is mirrored as FLD-20 says.
- **CLI-40** THE color input SHALL lay out the label that floats in its box 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 keeps its own order in a right-to-left layout and reorders nothing beside it; THE color input SHALL still place the label and align its lines by the layout direction, not by the text's own. The value is laid out as CLI-37 says. The texts of the rows around the input are the Field's and are laid out as FLD-21 says.
- **CLI-41** WHEN variant or size names a value that the active design system does not have THE color input 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 color input does not know by name (one a layer added) it SHALL draw no label and draw its edge as a stacked color input does (CLI-15, CLI-22), with that value's own tokens. A required mark it does not know is the Field's to answer (FLD-22).
- **CLI-42** THE color input SHALL have no words of its own and SHALL read none of the product's words: invalidText and pickerLabel are said by the product for the one input they are set on, and WHILE either is empty THE color input SHALL draw and work as CLI-20 and CLI-27 say and SHALL put no word in its place. Its value is a number sign and digits, written the same in every language (CLI-16). The words of a requirement are the Field's (FLD-24).
- **CLI-43** THE color input SHALL say to the Field that frames it what only the input knows, and SHALL say it again WHEN it changes: that it draws the label itself WHILE its variant floats one (CLI-15), so that the Field draws no label row (FLD-25); how far the value's text stands from the input's own start edge — comp.input-box.container.padding-inline-with-icon.medium, the swatch's side comp.input-box.icon.size.medium and comp.input-box.container.gap.medium for size medium (and so on for each size, CLI-1) — and, for its end edge, comp.input-box.container.padding-inline.medium (and so on), whether or not the picker button is shown, so that the Field's supporting row starts at the value's text, after the swatch, and ends at the container's text padding (FLD-26); and an entry error with its words (CLI-20, FLD-28). It SHALL give no count: a color has one length. It SHALL take from that Field its name, its description and its required, invalid and disabled states (CLI-9, CLI-11, CLI-31), whether the label is hidden (CLI-15) and whether an asterisk follows it (CLI-7), so that a Field and a color input placed by hand are drawn, named, described and marked exactly as a color field is.
- **CLI-44** THE color input 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 color input SHALL be complete as a bare control — its box, the swatch, the value as text, the picker button, the label that floats in the box where its variant floats one, its states and its events — and exactly as tall as its container. THE color input SHALL be block-level, framed and bare alike: exactly as wide as the place it stands in — the width its layout gives it, which inside a Field is the Field's whole width (FLD-1), so that the box fills its Field — with no width of its own and never the platform's default width for a text element, as the text input is (TXI-63). A product that wants a narrower box gives it a narrower place.
- **CLI-45** THE color input 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 text of its value, the caret and the highlight of selected text, 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), and the size of its swatch, which is the size of the glyph at the start of a box (comp.input-box.icon.size.medium, and so on), 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 swatch's shape and the hairline around it. WHEN a design system or a product sets a token of the box THE color input SHALL follow it, as the text input, the select, the combobox and the number input do, which read the same tokens; no token is held equal to another for this. WHERE one color input 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 color input SHALL draw itself from the value it finds there.
- **CLI-46** 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 color input 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 color input is invalid — except those the color input owns (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 color input SHALL take it off that element. WHEN the product sets an aria- attribute the color input owns THE color input SHALL report it in debug builds.

### Tokens

As written in the token documents (DTCG): an alias in braces names the token it takes its value from, which each design system resolves. On the web each token is a CSS custom property: `--tanrei` and the name after `tanrei`, joined by hyphens (`tanrei.comp.color-input.…` → `--tanrei-comp-color-input-…`).

#### `tanrei.comp.color-input`

| Token | Type | Value |
| --- | --- | --- |
| `comp.color-input.swatch.shape` | `dimension` | `{tanrei.sys.shape.corner.extra-small}` |
| `comp.color-input.swatch.outline.width` | `dimension` | `{tanrei.sys.border.hairline}` |
| `comp.color-input.swatch.outline.color` | `color` | `{tanrei.sys.color.outline-variant}` |

#### `tanrei.comp.input-box`

| Token | Type | Value |
| --- | --- | --- |
| `comp.input-box.container.shape` | `dimension` | `{tanrei.sys.shape.corner.small}` |
| `comp.input-box.container.shape-bottom` | `dimension` | `{tanrei.sys.shape.corner.none}` |
| `comp.input-box.container.height.small` | `dimension` | `40px` |
| `comp.input-box.container.height.medium` | `dimension` | `48px` |
| `comp.input-box.container.height.large` | `dimension` | `56px` |
| `comp.input-box.container.padding-inline.small` | `dimension` | `{tanrei.sys.space.space150}` |
| `comp.input-box.container.padding-inline.medium` | `dimension` | `{tanrei.sys.space.space200}` |
| `comp.input-box.container.padding-inline.large` | `dimension` | `{tanrei.sys.space.space200}` |
| `comp.input-box.container.padding-inline-with-icon.small` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.input-box.container.padding-inline-with-icon.medium` | `dimension` | `{tanrei.sys.space.space150}` |
| `comp.input-box.container.padding-inline-with-icon.large` | `dimension` | `{tanrei.sys.space.space150}` |
| `comp.input-box.container.padding-block.small` | `dimension` | `8px` |
| `comp.input-box.container.padding-block.medium` | `dimension` | `10px` |
| `comp.input-box.container.padding-block.large` | `dimension` | `14px` |
| `comp.input-box.container.padding-block-with-label.small` | `dimension` | `2px` |
| `comp.input-box.container.padding-block-with-label.medium` | `dimension` | `4px` |
| `comp.input-box.container.padding-block-with-label.large` | `dimension` | `4px` |
| `comp.input-box.container.gap.small` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.input-box.container.gap.medium` | `dimension` | `{tanrei.sys.space.space200}` |
| `comp.input-box.container.gap.large` | `dimension` | `{tanrei.sys.space.space200}` |
| `comp.input-box.label.typescale.small` | `typography` | `{tanrei.sys.typescale.label}` |
| `comp.input-box.label.typescale.medium` | `typography` | `{tanrei.sys.typescale.label}` |
| `comp.input-box.label.typescale.large` | `typography` | `{tanrei.sys.typescale.label}` |
| `comp.input-box.label.floated-typescale.small` | `typography` | `{tanrei.sys.typescale.label-small}` |
| `comp.input-box.label.floated-typescale.medium` | `typography` | `{tanrei.sys.typescale.label-small}` |
| `comp.input-box.label.floated-typescale.large` | `typography` | `{tanrei.sys.typescale.label-small}` |
| `comp.input-box.requirement.asterisk-gap` | `dimension` | `{tanrei.sys.space.space25}` |
| `comp.input-box.input.typescale.small` | `typography` | `{tanrei.sys.typescale.body}` |
| `comp.input-box.input.typescale.medium` | `typography` | `{tanrei.sys.typescale.body-large}` |
| `comp.input-box.input.typescale.large` | `typography` | `{tanrei.sys.typescale.body-large}` |
| `comp.input-box.affix.gap` | `dimension` | `{tanrei.sys.space.space25}` |
| `comp.input-box.icon.size.small` | `dimension` | `{tanrei.sys.icon.small}` |
| `comp.input-box.icon.size.medium` | `dimension` | `{tanrei.sys.icon.medium}` |
| `comp.input-box.icon.size.large` | `dimension` | `{tanrei.sys.icon.medium}` |
| `comp.input-box.selection.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.selection.content.color` | `color` | `{tanrei.sys.color.on-primary}` |
| `comp.input-box.outline.width` | `dimension` | `{tanrei.sys.border.hairline}` |
| `comp.input-box.outline.notch-padding` | `dimension` | `{tanrei.sys.space.space50}` |
| `comp.input-box.active-indicator.width` | `dimension` | `{tanrei.sys.border.hairline}` |
| `comp.input-box.stacked.container.color` | `color` | `{tanrei.sys.color.surface-container-highest}` |
| `comp.input-box.stacked.container.height.small` | `dimension` | `{tanrei.comp.button.container.height.small}` |
| `comp.input-box.stacked.container.height.medium` | `dimension` | `{tanrei.comp.button.container.height.medium}` |
| `comp.input-box.stacked.container.height.large` | `dimension` | `{tanrei.comp.button.container.height.large}` |
| `comp.input-box.stacked.container.padding-block.small` | `dimension` | `4px` |
| `comp.input-box.stacked.container.padding-block.medium` | `dimension` | `6px` |
| `comp.input-box.stacked.container.padding-block.large` | `dimension` | `14px` |
| `comp.input-box.stacked.active-indicator.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.stacked.outline.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.stacked.label.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.stacked.input.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.stacked.placeholder.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.stacked.affix.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.stacked.leading-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.stacked.trailing-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.stacked.caret.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.stacked.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.stacked.state-layer.color` | `color` | `{tanrei.comp.input-box.stacked.input.color}` |
| `comp.input-box.stacked.error.container.color` | `color` | `{tanrei.sys.color.surface-container-highest}` |
| `comp.input-box.stacked.error.active-indicator.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.stacked.error.outline.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.stacked.error.label.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.stacked.error.input.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.stacked.error.placeholder.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.stacked.error.affix.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.stacked.error.leading-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.stacked.error.trailing-icon.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.stacked.error.caret.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.stacked.error.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.stacked.error.state-layer.color` | `color` | `{tanrei.comp.input-box.stacked.error.input.color}` |
| `comp.input-box.filled.container.color` | `color` | `{tanrei.sys.color.surface-container-highest}` |
| `comp.input-box.filled.active-indicator.color` | `color` | `{tanrei.sys.color.outline}` |
| `comp.input-box.filled.outline.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.filled.label.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.input.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.filled.placeholder.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.affix.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.leading-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.trailing-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.caret.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.filled.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.filled.state-layer.color` | `color` | `{tanrei.comp.input-box.filled.input.color}` |
| `comp.input-box.filled.error.container.color` | `color` | `{tanrei.sys.color.surface-container-highest}` |
| `comp.input-box.filled.error.active-indicator.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.filled.error.outline.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.filled.error.label.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.filled.error.input.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.filled.error.placeholder.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.error.affix.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.error.leading-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.filled.error.trailing-icon.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.filled.error.caret.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.filled.error.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.filled.error.state-layer.color` | `color` | `{tanrei.comp.input-box.filled.error.input.color}` |
| `comp.input-box.outlined.container.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.outlined.active-indicator.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.outlined.outline.color` | `color` | `{tanrei.sys.color.outline}` |
| `comp.input-box.outlined.label.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.input.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.outlined.placeholder.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.affix.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.leading-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.trailing-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.caret.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.outlined.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.outlined.state-layer.color` | `color` | `{tanrei.comp.input-box.outlined.input.color}` |
| `comp.input-box.outlined.error.container.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.outlined.error.active-indicator.color` | `color` | `{"colorSpace":"srgb","components":[0,0,0],"alpha":0,"hex":"#000000"}` |
| `comp.input-box.outlined.error.outline.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.outlined.error.label.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.outlined.error.input.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.input-box.outlined.error.placeholder.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.error.affix.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.error.leading-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.input-box.outlined.error.trailing-icon.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.outlined.error.caret.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.input-box.outlined.error.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.input-box.outlined.error.state-layer.color` | `color` | `{tanrei.comp.input-box.outlined.error.input.color}` |
| `comp.input-box.motion.label-duration` | `duration` | `{tanrei.sys.motion.role.state.default}` |
| `comp.input-box.motion.label-easing` | `cubicBezier` | `{tanrei.sys.motion.easing.state}` |
| `comp.input-box.motion.state-duration.small` | `duration` | `{tanrei.sys.motion.role.state.compact}` |
| `comp.input-box.motion.state-duration.medium` | `duration` | `{tanrei.sys.motion.role.state.default}` |
| `comp.input-box.motion.state-duration.large` | `duration` | `{tanrei.sys.motion.role.state.default}` |
| `comp.input-box.motion.state-easing` | `cubicBezier` | `{tanrei.sys.motion.easing.state}` |

Per-state tokens, valued by the design system's pattern rules (not written by hand):

- `comp.input-box.<variant>.<state>.<property>` for the variants `stacked`, `filled`, `outlined`, the states `hovered`, `focused`, `disabled` and the properties `container.color`, `active-indicator.color`, `outline.color`, `label.color`, `input.color`, `placeholder.color`, `affix.color`, `leading-icon.color`, `trailing-icon.color`, `caret.color`, `required-asterisk.color`, `state-layer.color`, `error.container.color`, `error.active-indicator.color`, `error.outline.color`, `error.label.color`, `error.input.color`, `error.placeholder.color`, `error.affix.color`, `error.leading-icon.color`, `error.trailing-icon.color`, `error.caret.color`, `error.required-asterisk.color`, `error.state-layer.color`.
- `comp.input-box.<state>.<property>` for the states `hovered`, `focused`, `disabled` and the properties `opacity`, `outline.width`, `active-indicator.width`.

### Web 上绘制方式的不同之处

- 这个元素是与表单关联的元素。有 `name` 时，它提交 `value`——颜色，或者空字符串——重置为 `value` 属性（attribute）的值，在导航返回时恢复，跟随被禁用的 `<fieldset>`；所在的字段说明它必填而它为空时报告缺少值，输入错误持续期间报告输入有误。
- 这个元素是一个块。`focus()` 会把焦点交给原生的文本元素，绝不会交给选择器按钮；在方框上、这个按钮之外的任何地方按下，会让文本获得焦点；只是显示这个按钮时，按色块会打开选择器。
- `input` 和 `change` 是普通事件：请从元素上读取 `value`。`pickerrequest` 是可以取消的 CustomEvent，带有 `detail.value`；`entryerror` 是 CustomEvent，带有 `detail.text` 和 `detail.message`，后者就是 `invalidText`。它们的类型是 `TanreiColorInputPickerRequestDetail` 和 `TanreiColorInputEntryErrorDetail`。
- 部件：`container`、`outline`、`active-indicator`、`label`、`requirement-label`、`swatch`、`input`、`picker-button`。它没有插槽。
- 这个元素没有自己的文字，也不读取产品的任何文字：`label`、`invalidText` 和 `pickerLabel` 在你给出之前都是空的。
- 浏览器的选择器因浏览器而异：在 Chromium 中是页面里的一个弹出层，在 Firefox 和 Safari 中是系统的颜色窗口。它的外观、位置和按键都由浏览器决定。
- 用全角字符写的文本（`＃０ｆ７６６ｅ`）不会读作颜色。
- 在元素完成定义之前，它没有尺寸。请预留方框的高度。

## 指南

### 颜色输入框、颜色字段，还是手写的字段

| 你需要 | 使用 |
| --- | --- |
| 标签、方框、帮助文本和错误 | [颜色字段](https://tanrei.chnotchy.com/zh-cn/components/color-field/) |
| 只要方框，放在已经为这种颜色命名的行或列里 | 颜色输入框，由它自己的 `label` 命名 |
| 在框架里面、方框旁边或下方放你自己的东西 | 一个[字段](https://tanrei.chnotchy.com/zh-cn/components/field/)，里面放一个颜色输入框和你自己的元素 |
| 只显示、不编辑的颜色 | 一个样本，旁边用文本写出它的值或名称 |

### 推荐

- 给单独使用的输入框一个 `label`，把这一行和它所在的位置合起来表达的意思说出来：“主色，色调 40”。
- 选择器按钮也要按它所在的行来命名：“选择‘主色，色调 40’的颜色”。否则，一个有很多行的页面上会有许多同名的按钮。
- 让单独使用的输入框靠所在的位置得到宽度：网格的一列、一个单元格、工具栏里有宽度的一项。
- 只要你需要的是方框上方的标签、帮助文本和错误，就使用颜色字段。

### 避免

- 不要为了省下标签的位置，在表单里使用单独的输入框。哪里都看不到标签的输入框，对每个看到它的人来说都只能靠猜。
- 不要只靠色块来说明颜色。值是文本；分辨不出两个样本的人，读到的是两个不同的值。
- 不要在字段里的输入框上设置 `label`、`error` 或 `disabled`。把它们写在字段上，标签和帮助文本才会随之变化。
- 不要把颜色字段放进字段。它本身已经是字段；字段框住的控件是颜色输入框。

## 无障碍

### 组件负责的部分

- 它是一个文本框，值就是它显示的文本；设置 `picker` 时，还有一个单独的按钮，以 `pickerLabel` 命名，并暴露为会打开一个对话框。
- 它的名称是它自己的 `label`；它没有 `label` 时，是包着它的字段的标签。
- 它的描述是字段给它的内容：帮助文本或错误文本。没有字段框住它时，在输入错误持续期间，`invalidText` 是它的描述。
- 处于错误状态时，它暴露为无效；所在的字段说明它必填时，暴露为必填；禁用时暴露为禁用。
- 色块是装饰性的，对辅助技术隐藏：说明颜色的是值的文本。
- 值变化时不朗读任何内容，它也不会给颜色加上名称。
- 焦点由方框自己的边缘来显示，无论来自键盘还是指针。点击区域满足最小目标。
- 在强制颜色模式下，方框会多出一道边线，并采用用户的颜色。只有色块保留值的颜色；它的边线是文本颜色。
- 在从右到左的布局中，方框会镜像：色块在右，选择器按钮在左。值本身仍然从左到右书写。
- 缺少名称会在调试构建中报告。

### 需要你做的事

- 用 `label` 为每个单独使用的输入框命名。它旁边可见的文本并不能为它命名。
- 只要设置了 `picker`，就给出 `pickerLabel`。
- 单独使用的输入框可能出错的地方，要用辅助技术读得到的文字说明哪里不对：写上 `invalidText`，并在页面上放一条你自己的消息——或者用字段框住输入框，由字段把这些文字画出来。
