---
title: "テキストフィールド"
description: "1 行のテキスト、文章に合わせて伸びるか手で大きさを変えられる複数行のテキスト、ロケールの書き方で単位ごとに入力する日付・時刻、書かれたとおりに保持するコードを入力・編集します。必須か任意かを示せるラベル、アイコン、接頭辞と接尾辞、補足テキスト、文字数カウンター、ピッカーボタンを添えられます。"
url: "https://tanrei.chnotchy.com/ja/components/text-field/"
last_updated: 2026-10-26T22:31:32.000Z
---

# テキストフィールド

> 1 行のテキスト、文章に合わせて伸びるか手で大きさを変えられる複数行のテキスト、ロケールの書き方で単位ごとに入力する日付・時刻、書かれたとおりに保持するコードを入力・編集します。必須か任意かを示せるラベル、アイコン、接頭辞と接尾辞、補足テキスト、文字数カウンター、ピッカーボタンを添えられます。

## 概要

> [!NOTE]
> **仕様は改訂済み、各プラットフォームは追従待ち**
>
> このページは、仕様の現在の形のテキストフィールドを説明しています。ラベルをフィールドの上に置く `stacked` バリアントが既定で、`filled` と `outlined` は Material と cupertino のプリセットだけのものです。高さは 32、40、56（ラベルが中で浮き上がる filled と outlined のフィールドは 40、48、56）で、日付と時刻の種類とピッカーボタンがあります。複数行のフィールドは文章に合わせて伸びるか、ハンドルで大きさを変えられ（`resize`）、日付や時刻はセグメントで単位ごとに入力し、必須か任意かを示す `required` と `optional` があります。Web コンポーネントはすでにこのすべてに対応しており、デモもそれを示しています。フィールドはコードも保持できます（`type="code"` と、行の折り返しを決める `wrap`）。Web は実装済みで、ほかのプラットフォームはまだです。テキストフィールドは、このページで説明しているとおり、テキスト入力欄を囲むフィールドです。Web はこれも実装済みで、`required` 属性はなくなりました。SwiftUI、Compose、Flutter、React Native は上の表で「追従待ち」です。追従するまでは以前の版（既定が outlined で高さ 36 と 40、印を描かない `required` フラグがあり、`stacked` バリアント・日付や時刻の種類・ピッカーボタン・リサイズの方式はない）を提供しており、これらの例もその版のものです。

テキストフィールドは、フォームがテキストを求めるところで使います。ラベル、ボックス、1 行の補足、値が違うときの言葉、文字数という、いつもの組み合わせが、そのまま使える形でそろっています。

**構成要素**は、[フィールド](https://tanrei.chnotchy.com/ja/components/field/)と[テキスト入力欄](https://tanrei.chnotchy.com/ja/components/text-input/)です。フィールドは枠で、ラベル、必須または任意の印、補足、エラー、文字数を受け持ちます。テキスト入力欄はコントロールで、ボックスと、その中で入力するものすべてを受け持ちます。テキストフィールドが自分で描くものはなく、トークンもありません。プロパティはどれも 2 つのどちらかのもので、そのまま渡されます。

置かれた場所の幅いっぱいに広がるブロックで、ボックスもその幅いっぱいになります。自分の幅は持ちません。フィールドを狭くするには、置く場所を狭くします。

### 使い方

**Web**

```html
<form>
  <tanrei-text-field name="email" label="メールアドレス" type="email" autocomplete="email" required required-text="必須"
    supporting-text="サインインにだけ使います。"></tanrei-text-field>
  <tanrei-text-field name="company" label="会社名" optional optional-text="任意"></tanrei-text-field>
  <!-- 文章に合わせて 3 行から 8 行まで伸び、それを超えるとスクロールします。 -->
  <tanrei-text-field name="message" label="メッセージ" multiline min-rows="3" max-rows="8"></tanrei-text-field>
  <!-- ハンドルを使って、手で大きさを変えます。 -->
  <tanrei-text-field name="notes" label="メモ" multiline resize="manual" resize-label="メモの大きさを変える"></tanrei-text-field>
</form>

<!-- ツールバーに置くボックスだけのもの：自身の label で名前を付けたテキスト入力欄です。テキストフィールドをインポートすると定義されます。 -->
<tanrei-text-input label="検索" type="search" placeholder="コンポーネントを検索">
  <span slot="leading-icon">search</span>
</tanrei-text-input>

<!-- コード：ページの向きにかかわらず左から右へ、コード用の書体で描きます。自動修正もスペルチェックもしません。 -->
<tanrei-text-field label="パターン" type="code" value="tanrei.comp.button.*.container.color"></tanrei-text-field>
<tanrei-text-field label="Webhook の本文" type="code" multiline min-rows="4" supporting-text="JSON"></tanrei-text-field>
<!-- 折り返しません。コードの 1 行は 1 行のままで、長い行はフィールドの中で横にスクロールします。 -->
<tanrei-text-field label="行" type="code" multiline no-wrap></tanrei-text-field>

<!-- ロケールの順に単位ごとに入力し、2026-09-27 の形で保持する日付。ピッカーボタン付き。 -->
<tanrei-text-field id="start" label="開始日" type="date" min="2026-01-01" disabled-dates="2026-05-01 2026-12-25"
  invalid-text="日付を最後まで入力してください。" unavailable-text="2026 年以降の営業日を選んでください。"
  picker picker-label="開始日を選択" segment-empty-text="未入力"></tanrei-text-field>
<tanrei-date-time-picker id="start-picker" presentation="docked" anchor="start" label="開始日"></tanrei-date-time-picker>

<script type="module">
  import '@tanrei/web/components/text-field';
  const email = document.querySelector('[name="email"]');
  email.addEventListener('input', () => draft(email.value)); // 編集のたびに
  email.addEventListener('change', () => save(email.value)); // 値が変わった状態でフォーカスが外れたか、Enter を押したとき
  // 検証は利用者側で行います。フィールドが自分で error を設定することはありません。
  email.error = submitted && !isValid(email.value);
  email.errorText = 'メールアドレスを最後まで入力してください。';

  // フィールドは依頼するだけです。ピッカーを表示し、値を書き戻すのはプロダクト側です。
  const start = document.querySelector('#start');
  const picker = document.querySelector('#start-picker');
  start.addEventListener('pickerrequest', (e) => {
    Object.assign(picker, { value: e.detail.value, min: e.detail.min, max: e.detail.max, disabledDates: start.disabledDates, open: true });
  });
  picker.addEventListener('change', () => (start.value = picker.value));
  picker.addEventListener('close', () => (picker.open = false));
</script>
```

**React**

```tsx
import { TextField } from '@tanrei/react';

<TextField
  label="メールアドレス"
  type="email"
  autocomplete="email"
  value={email}
  onInput={(e) => setEmail(e.currentTarget.value)}
  onChange={() => save(email)}
  required
  requiredText="必須"
  error={submitted && !isValid(email)}
  errorText="メールアドレスを最後まで入力してください。"
/>

<TextField label="メッセージ" multiline minRows={3} maxRows={8} value={message}
  onInput={(e) => setMessage(e.currentTarget.value)} />

// 日付：値は ISO 8601 で、空のセグメントがあるあいだは空です。
<TextField
  label="開始日"
  type="date"
  min="2026-01-01"
  value={start}
  invalidText="日付を最後まで入力してください。"
  unavailableText="2026 年以降の日付を選んでください。"
  segmentEmptyText="未入力"
  picker
  pickerLabel="開始日を選択"
  onChange={(e) => setStart(e.currentTarget.value)}
  onEntryError={(e) => report(e.detail.reason)}
  onPickerRequest={() => setPickerOpen(true)}
/>
```

**SwiftUI**

```swift
import Tanrei

TanreiTextField("メールアドレス", text: $email, type: .email,
                supportingText: "サインインにだけ使います。",
                errorText: "メールアドレスを最後まで入力してください。",
                error: submitted && !isValid(email),
                textContentType: "email", // HTML の自動入力名
                onCommit: save,
                // 実行はここに渡します。外側の .onSubmit は onCommit より先に呼ばれます。
                onSubmit: signIn)

// 複数行は縦方向の axis と、行数の範囲を指定した lineLimit で表します。
TanreiTextField("メッセージ", text: $message, axis: .vertical, lineLimit: 3...8)
```

**Compose**

```kotlin
import androidx.compose.foundation.text.KeyboardActions
import io.github.chnotchy.tanrei.components.TanreiTextField
import io.github.chnotchy.tanrei.components.TanreiTextFieldType

var email by remember { mutableStateOf("") }
TanreiTextField(
    value = email,
    onValueChange = { email = it },
    label = "メールアドレス",
    type = TanreiTextFieldType.Email,
    contentType = "email", // HTML の自動入力名
    supportingText = "サインインにだけ使います。",
    isError = submitted && !isValid(email),
    errorText = "メールアドレスを最後まで入力してください。",
    onCommit = { save(it) },
    keyboardActions = KeyboardActions(onDone = { signIn() }),
)

// singleLine は仕様の multiline の逆です。
TanreiTextField(value = message, onValueChange = { message = it }, label = "メッセージ", singleLine = false, minLines = 3)
```

**Flutter**

```dart
import 'package:tanrei/tanrei.dart';

TanreiTextField(
  controller: email, // または initialValue: '…'
  labelText: 'メールアドレス',
  type: TanreiTextFieldType.email,
  autofillHints: TanreiTextField.autofillHintsFromHtml('email'),
  helperText: 'サインインにだけ使います。',
  isError: submitted && !isValid(email.text),
  errorText: 'メールアドレスを最後まで入力してください。',
  onCommit: save,
  onSubmitted: (String value) => signIn(),
);

TanreiTextField(labelText: 'メッセージ', multiline: true, minLines: 3);
```

**React Native**

```tsx
import { TextField } from '@tanrei/react-native';

<TextField
  label="メールアドレス"
  type="email"
  autoComplete="email"
  value={email}
  onChangeText={setEmail}
  supportingText="サインインにだけ使います。"
  error={submitted && !isValid(email)}
  errorText="メールアドレスを最後まで入力してください。"
  onEndEditing={save}
  onSubmitEditing={signIn}
/>
<TextField label="メッセージ" multiline minRows={3} value={message} onChangeText={setMessage} />
```

### 1 つ下の階層

同じフィールドを 2 つの構成要素から自分で書いても、見た目は同じで、支援技術にとっても、同じ名前、説明、状態を持つ同じテキストボックスになります。

```html
<tanrei-field label="メールアドレス" required required-text="必須"
  supporting-text="サインインにだけ使います。">
  <tanrei-text-input name="email" type="email" autocomplete="email"></tanrei-text-input>
</tanrei-field>
```

テキストフィールドにない並べ方が必要なときに、1 つ下の階層で書きます。

| 必要なもの | 使うもの |
| --- | --- |
| ラベル、ボックス、補足、エラー、文字数 | テキストフィールド |
| 枠の中、ボックスの横か下に置く自分の要素 | [フィールド](https://tanrei.chnotchy.com/ja/components/field/)に、[テキスト入力欄](https://tanrei.chnotchy.com/ja/components/text-input/)と自分の要素を入れる |
| ボックスだけ。ラベルは別の場所にある（ツールバーの絞り込み、テーブルのセル、ラベルを横に置いた行） | [テキスト入力欄](https://tanrei.chnotchy.com/ja/components/text-input/)を単独で使い、入力欄の `label` で名前を付ける |

1 つ下の階層で変わるのは、何をどこに指定するかです。`name` と `value` はテキスト入力欄に指定し、フォームに加わるコントロールもイベントが届く先も入力欄になります。`label`、`error`、`disabled`、必須の指定はフィールドに指定したままにします。フィールドがコントロールに伝えるので、入力欄の側には指定しません。

テキストフィールドが行い、フィールドとテキスト入力欄を自分で書いたものが行わないことが 1 つあります。文字数を合わせ続けることです。自分で書く場合、`counter` を付けたら、値が変わるたびに入力欄の文字数をフィールドに渡します。

```js
field.countText = input.countText;
input.addEventListener('input', () => (field.countText = input.countText));
```

## 仕様

### それぞれの構成要素が受け持つもの

| 観点 | フィールドから | テキスト入力欄から |
| --- | --- | --- |
| プロパティ | `label`、`labelHidden`、`supportingText`、`errorText`、`error`、`disabled`、`required`、`optional`、`requiredMark`、`requiredText`、`optionalText` | それ以外のすべて。`variant`、`size`、`type`、`value`、`placeholder`、`readOnly`、複数行とリサイズ、`wrap`、`maxLength`、`counter`、接頭辞と接尾辞、`autocomplete`、日付と時刻に関するすべて |
| イベント | — | `input`、`change`、`action`、`pickerRequest`、`entryError` |
| スロット | `label-icon` | `leading-icon`、`trailing-icon` |
| トークン | `comp.field.*` | ボックスは `comp.input-box.*`（すべての入力ボックスが読みます）。テキスト入力欄だけが描く部分は `comp.text-input.*` |

テキスト入力欄は、自分の `label`、`labelHidden`、`error`、`disabled` を持っています。テキストフィールドが設定するのは、それらではありません。テキストフィールドの `label` はフィールドのもので、入力欄に名前を付け、Filled と Outlined のフィールドではボックスの中で浮き上がるラベルになります。

### トークン

テキストフィールドに自分のトークンはありません。ボックスは[テキスト入力欄のトークン](https://tanrei.chnotchy.com/ja/components/text-input/#%E4%BB%95%E6%A7%98)を、行（ラベル、印、補足、エラー、文字数）は[フィールドのトークン](https://tanrei.chnotchy.com/ja/components/field/#%E4%BB%95%E6%A7%98)を、どこで設定されていても読みます。下の表にあるのはフィールドのトークンです。1 つだけ変えるには、構成要素のトークンをその要素に設定します。

```html
<tanrei-text-field label="メールアドレス"
  style="--tanrei-comp-field-label-gap: 12px"></tanrei-text-field>
```

- Category: Text inputs
- Level: composed
- Listing: default
- Built from: Field, Text input
- Page: https://tanrei.chnotchy.com/components/text-field/
- Spec: https://github.com/chnotchy/tanrei/blob/main/specs/text-field.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-text-field>`, registered by importing `@tanrei/web` (or only its own module, `@tanrei/web/components/text-field`).
- React: `TextField` from `@tanrei/react`.

| Platform | Name | Status |
| --- | --- | --- |
| Web | `tanrei-text-field` | lagging |
| SwiftUI | `TanreiTextField` | lagging |
| Jetpack Compose | `TanreiTextField` | lagging |
| Flutter | `TanreiTextField` | lagging |
| React Native | `TextField` | lagging |

Names a platform gives in its own idiom:

| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| `value` (prop) | `value` | `text` | `value` | `controller` | `value` |
| `label` (prop) | `label` | `label` | `label` | `labelText` | `label` |
| `labelHidden` (prop) | `labelHidden` | `labelsHidden` | `labelHidden` | `labelHidden` | `labelHidden` |
| `placeholder` (prop) | `placeholder` | `prompt` | `placeholder` | `hintText` | `placeholder` |
| `supportingText` (prop) | `supportingText` | `supportingText` | `supportingText` | `helperText` | `supportingText` |
| `error` (prop) | `error` | `error` | `isError` | `isError` | `error` |
| `disabled` (prop) | `disabled` | `disabled` | `enabled` | `enabled` | `disabled` |
| `required` (prop) | `required` | `isRequired` | `required` | `required` | `required` |
| `optional` (prop) | `optional` | `isOptional` | `optional` | `optional` | `optional` |
| `multiline` (prop) | `multiline` | `axis` | `singleLine` | `multiline` | `multiline` |
| `minRows` (prop) | `minRows` | `lineLimit` | `minLines` | `minLines` | `minRows` |
| `maxRows` (prop) | `maxRows` | `lineLimit` | `maxLines` | `maxLines` | `maxRows` |
| `resizeLabel` (prop) | `resizeLabel` | `resizeLabel` | `resizeContentDescription` | `resizeSemanticLabel` | `resizeAccessibilityLabel` |
| `wrap` (prop) | `noWrap` | `wrap` | `softWrap` | `softWrap` | `wrap` |
| `counter` (prop) | `counter` | `counter` | `counter` | `showCounter` | `counter` |
| `counterText` (prop) | `counterText` | `counterText` | `counterText` | `counterSemanticText` | `counterAccessibilityText` |
| `counterNoLimitText` (prop) | `counterNoLimitText` | `counterNoLimitText` | `counterNoLimitText` | `counterNoLimitSemanticText` | `counterNoLimitAccessibilityText` |
| `prefixText` (prop) | `prefixText` | `prefix` | `prefix` | `prefixText` | `prefix` |
| `suffixText` (prop) | `suffixText` | `suffix` | `suffix` | `suffixText` | `suffix` |
| `autocomplete` (prop) | `autocomplete` | `textContentType` | `contentType` | `autofillHints` | `autoComplete` |
| `picker` (prop) | `picker` | `onPickerRequest` | `onPickerRequest` | `onPickerRequest` | `onPickerRequest` |
| `pickerLabel` (prop) | `pickerLabel` | `pickerLabel` | `pickerContentDescription` | `pickerSemanticLabel` | `pickerAccessibilityLabel` |
| `input` (event) | `input` | `text` | `onValueChange` | `onChanged` | `onChangeText` |
| `change` (event) | `change` | `onCommit` | `onCommit` | `onCommit` | `onEndEditing` |
| `action` (event) | `action` | `onSubmit` | `keyboardActions` | `onSubmitted` | `onSubmitEditing` |
| `pickerRequest` (event) | `pickerrequest` | `onPickerRequest` | `onPickerRequest` | `onPickerRequest` | `onPickerRequest` |
| `entryError` (event) | `entryerror` | `onEntryError` | `onEntryError` | `onEntryError` | `onEntryError` |
| `resizeEnd` (event) | `resizeend` | `onResizeEnd` | `onResizeEnd` | `onResizeEnd` | `onResizeEnd` |

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

- `field`: The Field: the label row with its glyph and its requirement mark, and the supporting row with the help, the error text and the count the text field gives it (TXF-67). In a look whose input floats the label it draws no label row.
- `text-input`: The text input, as the Field's one control: the box and everything typed in it, the icons, the prefix and the suffix, the segments of a date, the picker button, the resize handle, and the label that floats in the box in the looks that float one.

### Props

| Prop | Type | Default | Web attribute | Description |
| --- | --- | --- | --- | --- |
| `variant` | `enum`: `stacked`, `filled`, `outlined` | `"stacked"` | `variant` | 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, marked by a tinted box and a bottom line, or by a border. Use one style per form. A design system may leave some out (the Tanrei system ships stacked only) or add values. (From Text input.) |
| `size` | `enum`: `small`, `medium`, `large` | `"medium"` | `size` | Minimum height of a single-line input's container: stacked 32 / 40 / 56, as tall as a Button of the same size, so that an input 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. Medium and large are for forms; small is for an input in an app bar, a toolbar or a dense row of controls, beside a small select or button. The hit area always meets the minimum target. (From Text input.) |
| `type` | `enum`: `text`, `email`, `password`, `number`, `search`, `tel`, `url`, `code`, `date`, `time`, `date-time` | `"text"` | `type` | What the value is: chooses the keyboard, autocorrection, obscured entry and semantics. code is text written for a machine rather than for a reader — a snippet of configuration, a pattern, an identifier, a query, a key: a left-to-right run in the code face that the platform does not correct, capitalise, check for spelling or complete, on one line or, with multiline, on many. date, time and date-time are entered segment by segment in the locale's numeric form and held as ISO 8601. The value is a string for every type. (From Text input.) |
| `value` | `string` | `""` | `value` | The text in the field; for date, time and date-time, the ISO 8601 form of what its segments give (2026-09-27, 14:30, 2026-09-27T14:30), or empty while a segment is empty or the value is not available. (From Text input.) |
| `label` | `string` | `""` | `label` | Names the control, visibly and to assistive technology. Required, unless the control names itself (a checkbox or a switch with its own label): then leave it empty and the field adds only help and an error. (From Field: Names the text input. In a filled or outlined look the input draws it, floating in its box, and the Field draws no label row (FLD-25, TXI-13).) |
| `labelHidden` | `boolean` | `false` | `label-hidden` | Does not draw the label, which stays the accessible name. For a control whose visible label is elsewhere (a table column, a toolbar). (From Field: Hides the label wherever it would be drawn: the Field's row, or the input's floating label (FLD-25).) |
| `placeholder` | `string` | `""` | `placeholder` | A hint shown while the value is empty and the label is not in the input line (above, floated or hidden). Never a replacement for label. In a code field it is an example of the code and is drawn as code (TXI-53, TXI-54); a hint in words belongs in the supportingText of its Field. Ignored by a date, time or date-time field, whose empty segments each show their own. (From Text input.) |
| `supportingText` | `string` | `""` | `supporting-text` | Help shown below the control and given to assistive technology as the control's description. While the field is in error it gives way to errorText, unless the design system keeps both (comp.field.error-text.placement append). (From Field.) |
| `errorText` | `string` | `""` | `error-text` | Shown while error is true, in supportingText's place — or below it, which then stays, where the design system's comp.field.error-text.placement is append: what is wrong and how to put it right, in words. (From Field.) |
| `error` | `boolean` | `false` | `error` | Puts the field in the error condition: the error text is drawn, the control is exposed as invalid and, where it has an error condition of its own, drawn in it. What a value means is the product's to validate; the field never sets this itself. (From Field: The Field puts its text input in the input's own error condition (FLD-8, TXI-11).) |
| `disabled` | `boolean` | `false` | `disabled` | Disables the control and draws the frame as disabled. Disable a framed control here, not on the control, so that its label follows. (From Field: The Field disables its text input (FLD-14, TXI-5); the text field is then left out of its form (TXF-63).) |
| `readOnly` | `boolean` | `false` | `readonly` | The value can be focused, selected and copied, but not changed. (From Text input.) |
| `required` | `boolean` | `false` | `required` | What the form asks of the control: true exposes it as required and draws the required mark beside the label. It never validates: a missing answer is the product's to report with error. (From Field: The Field exposes its text input as required (FLD-10, TXI-23).) |
| `optional` | `boolean` | `false` | `optional` | Draws the optional tag after the label and adds optionalText to the control's description, because assistive technology has no state for it. Ignored while required is true: a control is never both. (From Field.) |
| `requiredMark` | `enum`: `auto`, `tag`, `asterisk`, `none` | `"auto"` | `required-mark` | How this control shows that it is required: tag is a small filled Tag with requiredText, asterisk is "*" after the label, none draws nothing and keeps the semantics. Auto takes comp.field.requirement.mark, which follows sys.behavior.requirement-mark, the system's one answer. Ignored unless required is true. (From Field.) |
| `requiredText` | `string` | `""` | `required-text` | The text of the required tag, in the product's language ("Required", "必須"). The product's: Tanrei has no word for it. Empty reads the product's words; with neither, FLD-24 says what is drawn. (From Field.) |
| `optionalText` | `string` | `""` | `optional-text` | The text of the optional tag, in the product's language ("Optional", "任意"); also read to assistive technology as part of the description. The product's: Tanrei has no word for it. Empty reads the product's words. (From Field.) |
| `multiline` | `boolean` | `false` | `multiline` | Accepts line breaks and shows several lines, sized as resize says. Ignored for password, date, time and date-time. (From Text input.) |
| `minRows` | `number` | `2` | `min-rows` | The fewest lines a multi-line field shows: the height of an empty auto-resized field, the starting and smallest height of a manually resized one, and the height of a fixed one. (From Text input.) |
| `maxRows` | `number` | `0` | `max-rows` | The most lines an auto-resized field grows to before it scrolls, and the tallest a manually resized one can be made; 0 is no limit. Ignored while resize is fixed. (From Text input.) |
| `resize` | `enum`: `auto`, `manual`, `fixed` | `"auto"` | `resize` | How a multi-line field gets its height. auto: it follows the number of lines between minRows and maxRows, with an animated change, and has no handle. manual: it starts at minRows lines and the person resizes it with the handle in its bottom end corner. fixed: it stays at minRows lines, with no handle. Ignored unless multiline. (From Text input.) |
| `resizeAxis` | `enum`: `vertical`, `both` | `"vertical"` | `resize-axis` | What the person can resize by hand: the height only, or the height and the width. Ignored unless resize is manual. (From Text input.) |
| `resizeLabel` | `string` | `""` | `resize-label` | The accessible name of the resize handle, in the product's language ("Resize"). The product's: Tanrei has no word for it. Empty reads the product's words. Required while the handle is shown. (From Text input.) |
| `wrap` | `boolean` | `true` | — | Whether a multi-line code field continues a line that does not fit on following rows (true), or keeps every line of the code on one row and scrolls sideways inside its container (false) — for code whose lines are read as rows: a table of values, a log, a file in which a line break means something. The value is the same either way. Ignored unless type is code and multiline is true: prose always wraps, and a single line always scrolls in its line. (From Text input.) |
| `maxLength` | `number` | `0` | `maxlength` | The most user-perceived characters the user can enter; 0 is no limit. Ignored for date, time and date-time. (From Text input.) |
| `counter` | `boolean` | `false` | `counter` | Keeps the character count (and maxLength, when set): part of the input's description and announced when typing pauses (TXI-19). The input draws none: a text field gives the count to its Field, which draws it at the end of its supporting row, and a product that frames the input by hand gives it to the Field's countText. (From Text input: The input keeps the count, the text field gives it to its Field, and the Field draws it (TXI-19, TXF-67, FLD-27).) |
| `counterText` | `string` | `""` | `counter-text` | The count said to assistive technology while counter is true and maxLength is greater than 0: the whole sentence in the product's language with three places the input fills — {count} with the characters entered, {limit} with maxLength and {remaining} with how many more can be entered ("{remaining} characters left", "{limit}文字中{count}文字"). Empty reads the product's words; with neither, the count is said as the digits it is drawn in ("12/40"). A sentence that is right for every number, or a prop set per count where the language needs another form for one. (From Text input.) |
| `counterNoLimitText` | `string` | `""` | `counter-no-limit-text` | The count said to assistive technology while counter is true and maxLength is 0: the whole sentence with one place, {count}, filled with the characters entered ("{count} characters", "{count}文字"). Empty reads the product's words; with neither, the count is said as its digits. (From Text input.) |
| `prefixText` | `string` | `""` | `prefix-text` | Fixed text before the value, not part of it. In a code field it is code: drawn in the code face at the left of the value in every layout direction (TXI-53). (From Text input.) |
| `suffixText` | `string` | `""` | `suffix-text` | Fixed text after the value, not part of it. In a code field it is code: drawn in the code face at the right of the value in every layout direction (TXI-53). (From Text input.) |
| `autocomplete` | `string` | `""` | `autocomplete` | The autofill hint, as an HTML autofill field name (email, current-password, new-password, one-time-code, tel, postal-code, …); each platform maps it to its own content type. A code field left without one asks the platform to offer no remembered entry (TXI-55). (From Text input.) |
| `locale` | `string` | `""` | `locale` | A BCP 47 language tag ("en-US", "ja-JP") for how a date, time or date-time field shows and reads its value: the order of the year, month and day segments, the separators, the clock, the digits and the names of the units. Empty uses the platform's current locale. The calendar is the Gregorian calendar in every locale. (From Text input.) |
| `min` | `string` | `""` | `min` | The earliest value a date, time or date-time field accepts from the person, in its ISO 8601 form; empty for no limit. (From Text input.) |
| `max` | `string` | `""` | `max` | The latest value a date, time or date-time field accepts from the person, in its ISO 8601 form; empty for no limit. (From Text input.) |
| `disabledDates` | `strings` | `` | `disabled-dates` | Dates inside min and max that a date or date-time field does not accept from the person, as ISO 8601 calendar dates — the same list the product gives its DateTimePicker. Ignored by a time field. (From Text input.) |
| `invalidText` | `string` | `""` | `invalid-text` | The words for a date, time or date-time input committed with some segments filled and others empty: given to the Field that frames it, which shows them as its error text (TXI-30). (From Text input: Shown by the Field, in its supporting row (FLD-28).) |
| `unavailableText` | `string` | `""` | `unavailable-text` | The words for a date, time or date-time input committed with a value before min, after max or, for a date, listed in disabledDates: given to the Field that frames it, which shows them as its error text (TXI-30). (From Text input: Shown by the Field, in its supporting row (FLD-28).) |
| `segmentEmptyText` | `string` | `""` | `segment-empty-text` | What assistive technology reads as the value of a date or time segment that holds nothing yet, in the product's language ("Empty"). The product's: Tanrei has no word for it. Empty reads the product's words; with neither, an empty segment has no value text. (From Text input.) |
| `picker` | `boolean` | `false` | `picker` | Shows the picker button in a date, time or date-time field. The field presents no picker itself: it emits pickerRequest, and the product presents a DateTimePicker in the matching mode and sets value to what was picked. (From Text input.) |
| `pickerLabel` | `string` | `""` | `picker-label` | The accessible name of the picker button ("Choose date"). Required while picker is shown. (From Text input.) |

### Events

| Event | Description |
| --- | --- |
| `input` | The user changed the value; carries the new value. Emitted for every change, not when the product sets value. A date, time or date-time field emits it when its value changes — when its last segment is filled, a filled field is edited, or it is emptied — not for a segment that leaves the value empty. (From Text input.) |
| `change` | The user committed a changed value (focus left the field, or the action key was pressed in a single-line field); carries the value. (From Text input.) |
| `action` | The user pressed the keyboard's action key (Enter, Return, Go, Search, Done) in a single-line field. (From Text input.) |
| `pickerRequest` | The user activated the picker button of a date, time or date-time field; carries the value, type, min and max. The product presents the picker and sets value to the picked value. (From Text input.) |
| `entryError` | The user committed a date, time or date-time field that is half-filled or whose value is not available; carries the reason (invalid: some segments are empty; unavailable: before min, after max or a listed date) and the text of the row of segments as drawn. The entry error itself is shown by the Field that frames the input (TXI-30). (From Text input.) |
| `resizeEnd` | The person finished changing the size of a field resized by hand: a drag of its handle ended, or a key or an activation of the handle changed the size (TXI-69). Carries lines, the number of lines the input shows, and width, the field's width in pixels WHILE resizeAxis is both (null otherwise). Not emitted when a change of resize, resizeAxis, minRows or maxRows gives the size back. (From Text input.) |

### Slots

| Slot | Description |
| --- | --- |
| `leading-icon` | An icon at the start of the input. (From Text input.) |
| `trailing-icon` | An icon at the end of the input, or the product's own icon-only Button (clear, reveal password). A date or time field's picker button comes after it. (From Text input.) |
| `label-icon` | A glyph before the label. (From Field.) |

### States

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

### Accessibility

- Role: `textbox`
- Labeling: The text field adds nothing to what its members expose, and the one thing it coordinates — the drawn count (TXF-67) — is kept from assistive technology, which hears the count from the input: the Field gives the text input its name, its description and its required, invalid and disabled states (FLD-8, FLD-10, FLD-11, FLD-14), and the text input carries them on its own element (TXI-23). Assistive technology meets one text box — or, for a date or a time, one group of spin buttons — named by label, exactly as it meets a Field around a text input written by hand.
- Minimum target size: yes

| Keys | Action |
| --- | --- |
| Every key | The text input's (its accessibility block; TXI-3, TXI-41, TXI-43 to TXI-45, TXI-59). The text field handles no key and adds no focus stop (TXF-64). |

- One level down is the same thing: a Field with a text input in it. Every fixture has that pair as its twin, and the accessibility tree of the two is the same (TXF-62). What the pair by hand does not do by itself is keep the Field's count text equal to the input's count as the person types (TXF-67): the product writes it at each input.
- On the web the two members stand in the text field's own shadow root, so the form meets the text field and not the input (TXF-63); focus passes through to the input (TXF-64).

### Behaviour

Requirements in EARS form, each with its stable id.

- **TXF-62** THE text field SHALL be a Field that frames one text input, and nothing else. It SHALL give label, labelHidden, supportingText, errorText, error, disabled, required, optional, requiredMark, requiredText and optionalText to its Field and every other prop to its text input, each unchanged; SHALL hold the label-icon slot for its Field and the leading-icon and trailing-icon slots for its text input; and SHALL emit its text input's events as its own, each once. THE text field SHALL be as wide as the place it stands in, and its text input SHALL fill it: it is a block with no width of its own, as its Field is (FLD-1), around a control that fills the width it is given (TXI-63). It SHALL draw no part of its own, have no token of its own and hold no state of its own but the count it carries from one member to the other (TXF-67): what it shows, how it behaves and what it exposes to assistive technology SHALL be exactly what that Field and that text input do when a product places the one in the other (FLD-1 to FLD-28; TXI-1 to TXI-63), with the Field given the input's count — the label row and its requirement mark (FLD-2 to FLD-6), or the label that floats in the box with the Field's row left out (FLD-25, TXI-13, TXI-50); the supporting row, the error and the count (FLD-7 to FLD-9, FLD-26, FLD-27, TXI-19, TXF-67); the name, the description and the states (FLD-10, FLD-11, FLD-14, TXI-23); and a date's entry error shown in the Field's row (FLD-28, TXI-30).
- **TXF-63** WHERE the platform has forms (the web) THE text field SHALL be the one control the form sees: it SHALL take name, submit its text input's value, reset to the value it was first given, restore it on navigation, report to the form's constraint validation exactly what its text input would report there (TXI-23, TXI-30), and be left out WHILE disabled is true or a disabled group of the form holds it; its text input and its Field SHALL take no part in the form themselves. WHEN the action key asks for the form's submission (TXI-3) the form asked SHALL be the text field's own.
- **TXF-64** WHEN the text field is given focus — by the product, by a press on any part of it that FLD-12 or TXI-8 gives to the input, or by a form that reports its validity — THE text field SHALL pass the focus to its text input (in a date, time or date-time field, to the segment TXI-43 names) and SHALL add no stop to the focus order: the stops inside it are its text input's, in that input's order (an interactive slot element, the picker button, the resize handle). WHILE focus is anywhere inside it THE text field SHALL count as focused for its Field (FLD-13).
- **TXF-65** IF the text field is placed in a Field THE text field SHALL in debug builds report that it is a field already and that the bare control is the text input, and both SHALL still render (FLD-15). WHILE it stands in a field group THE text field SHALL follow that group's disabled, through its own Field, and take nothing else from the group (FGR-20).
- **TXF-66** THE text field SHALL have no words of its own and SHALL read none: requiredText and optionalText are read by its Field (FLD-24), resizeLabel and segmentEmptyText by its text input (TXI-52), each from the instance's prop and then from the product's words.
- **TXF-67** WHILE counter is true THE text field SHALL give its Field, as its count text (FLD-27), the count its text input keeps (TXI-19), as the input writes it, and SHALL give the new one in the same frame in which the value changes — by typing, by a paste, because the product set value or maxLength — without a motion and without an announcement of its own: the input describes and announces its count itself (TXI-19) and the Field keeps the drawn text from assistive technology (FLD-27). WHILE counter is false THE text field SHALL give its Field no count text. THE text field SHALL NOT count, format or write a digit itself.
- **TXF-68** 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 text field 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 text field is invalid — except those the text field owns (aria-label, aria-labelledby, aria-disabled), which it SHALL move and not expose: its name is label (TXF-62). 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 text field SHALL take it off that element. WHEN the product sets an aria- attribute the text field owns THE text field 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.field.…` → `--tanrei-comp-field-…`).

#### `tanrei.comp.field`

| Token | Type | Value |
| --- | --- | --- |
| `comp.field.label.typescale` | `typography` | `{tanrei.sys.typescale.label}` |
| `comp.field.label.gap` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.field.label-icon.size` | `dimension` | `{tanrei.sys.icon.small}` |
| `comp.field.label-icon.gap` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.field.requirement.mark` | `number` | `{tanrei.sys.behavior.requirement-mark}` |
| `comp.field.requirement.gap` | `dimension` | `{tanrei.sys.space.space100}` |
| `comp.field.requirement.asterisk-gap` | `dimension` | `{tanrei.sys.space.space25}` |
| `comp.field.supporting-text.typescale` | `typography` | `{tanrei.sys.typescale.label-small}` |
| `comp.field.supporting-text.gap` | `dimension` | `{tanrei.sys.space.space50}` |
| `comp.field.counter.gap` | `dimension` | `{tanrei.sys.space.space200}` |
| `comp.field.motion.state-duration` | `duration` | `{tanrei.sys.motion.role.state.default}` |
| `comp.field.motion.state-easing` | `cubicBezier` | `{tanrei.sys.motion.easing.state}` |
| `comp.field.standard.label.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.field.standard.label-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.supporting-text.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.counter.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.field.standard.error.label.color` | `color` | `{tanrei.sys.color.on-surface}` |
| `comp.field.standard.error.label-icon.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.error.supporting-text.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.error.counter.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.error.required-asterisk.color` | `color` | `{tanrei.sys.color.primary}` |
| `comp.field.standard.error.value-text.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.value-text.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.field.standard.error-text.color` | `color` | `{tanrei.sys.color.error}` |
| `comp.field.value-text.typescale` | `typography` | `{tanrei.sys.typescale.label}` |
| `comp.field.value-text.gap` | `dimension` | `{tanrei.sys.space.space200}` |
| `comp.field.error-text.gap` | `dimension` | `{tanrei.sys.space.space50}` |
| `comp.field.error-text.placement` | `number` | `0` |

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

- `comp.field.<variant>.<state>.<property>` for the variants `standard`, the states `focused`, `disabled` and the properties `label.color`, `label-icon.color`, `supporting-text.color`, `required-asterisk.color`, `error.label.color`, `error.label-icon.color`, `error.supporting-text.color`, `error.required-asterisk.color`, `counter.color`, `error.counter.color`, `value-text.color`, `error.value-text.color`, `error-text.color`.

### Web での描き方の違い

- 要素のシャドウルートにあるのは、`<tanrei-text-input>` を入れた `<tanrei-field>` だけです。要素をインポートすると、2 つとも定義されます。
- 要素は、置かれた場所の幅いっぱいに広がるブロックです。ブラウザーのテキスト入力欄と同じ幅のインラインのボックスではありません。
- 要素はフォームに関連付けられます。`name` と `form` を受け取り、値を送信します（無効の間は何も送信しません）。リセットすると `value` 属性の値に戻り、ページの移動で復元され、無効な `<fieldset>` に従い、`required` が設定されていて値が空の間は、値がないことを報告します。2 つの構成要素がフォームに項目を加えることはありません。1 行のフィールドで Enter を押すと、要素のフォームの既定のボタンが押されます。`action` をキャンセルすると押されません。
- `input`、`change`、`action`、`pickerrequest`、`entryerror` は、テキスト入力欄のイベントです。イベントのターゲットはこの要素で、どのリスナーでも `event.target.value` が新しい値です。detail の型は入力欄のもので、`TanreiTextInputActionDetail`、`TanreiTextInputPickerRequestDetail`、`TanreiTextInputEntryErrorDetail` です。
- パーツ: `field` と `text-input` が 2 つの構成要素です。構成要素のパーツは、1 つ下の階層と同じ名前です。`label-icon`、`supporting-text`、`counter` と、`container`、`outline`、`active-indicator`、`input`、`segment`、`prefix`、`suffix`、`leading-icon`、`trailing-icon`、`picker-button`、`resize-handle` です。`label` と `requirement-label` は、どちらの構成要素が描いていても、描かれているほうを指します。`field-label`、`text-input-label`、`field-requirement-label`、`text-input-requirement-label` は、片方の構成要素のものを指します。
- 要素そのものはフォーカスを受け取らず、ロールもありません。`focus()` は入力欄に移ります。ラベルの行、ボックス、浮き上がるラベルを押すと、入力欄にフォーカスが移ります。補足、エラーテキスト、文字数、行とボックスの間のすき間を押しても、何も起こりません。
- 名前は `label` で付けます。ラベルを描かないときは `label-hidden` を併せて指定します。`aria-label` はここでは何にも名前を付けないので、使いません。
- 属性は構成要素のものと同じです。`supporting-text`、`error-text`、`min-rows`、`no-wrap` などのケバブケースで、`readonly`、`maxlength`、`autocomplete`、`min`、`max` は HTML の名前のままです。`required` 属性はありません。`required` と書きます。
- 要素が定義されるまでは、大きさがありません。高さを確保してください。

## ガイドライン

### 使う場面

名前、メールアドレス、検索語、メッセージなど、利用者が値を打ち込むときに使います。決まった選択肢から選ぶ場合は、選ぶためのコントロールを使います。ラベルがほかの場所にあり、ボックスだけが必要なところ（ツールバーの絞り込み、テーブルのセルなど）では、[テキスト入力欄](https://tanrei.chnotchy.com/ja/components/text-input/)を使います。

### バリアントとサイズの選び方

1 つのフォームの中では、バリアントをそろえます。

| バリアント | 見た目 | 提供するシステム |
| --- | --- | --- |
| Stacked（既定） | 縁のない色付きのボックスの上にラベルを置きます。フォーカスするとボックスの縁の内側に線を引き、何も動かしません。エラーのときは、フォーカスしていなくても細い線を引きます。 | すべてのシステム。Tanrei システムが提供するのはこれだけです。 |
| Filled | 色の付いたボックスと下線。ラベルは中で浮き上がります。 | Material と cupertino のプリセット |
| Outlined | 枠線。フォーカスすると太くなり、ラベルは枠線の上に浮き上がります。 | Material と cupertino のプリセット |

既定値はプリセットを含むすべてのシステムで stacked です。Material の浮き上がるラベルを使いたい Material のプロダクトは、フィールドの `variant` を `outlined` か `filled` に設定します。Tanrei システムでも `filled` と `outlined` は動作しますが、開発時に警告が出ます。

`medium`（40）は密度を上げたフィールド、`large`（56）は標準のフィールドです。絞り込みや密度の高い設定画面には `medium`、サインインや購入手続きのフォームには `large` が向いています。`small`（32）は小さなセレクトと同じ高さで、アプリバー、ツールバー、コントロールが密に並ぶ行に置くフィールドのためのものです。入力の文字は 14 になります。既定のシステムでは、ほかの 2 つのサイズでは入力の文字は 16 です。高さは最小値で、文字を大きくするとフィールドも大きくなり、当たり判定はいつも最小ターゲットを満たします。

### ラベルとプレースホルダー

- すべてのフィールドに `label` が必要です。Stacked ではボックスの上に先頭から置かれ、動いたり大きさが変わったりしません。長いラベルは切らずに折り返します。`label-icon` スロットで、ラベルの前にグリフを置けます。Filled と Outlined では、空で未フォーカスのあいだはフィールドの中に置かれ、フォーカスされるか値が入ると上に浮き上がります。
- `labelHidden` はラベルを描かなくしますが、アクセシブルネームとしては残り、補足とエラーもボックスの下に残ります。ボックスだけが必要なところ（ツールバーの絞り込み、セル、ラベルを横に置いた行）では、代わりに、自身の `label` を持つ[テキスト入力欄](https://tanrei.chnotchy.com/ja/components/text-input/)を使います。
- プレースホルダーは入力例などのヒントです。フィールドが空で、ラベルが入力の行にないときに表示されます。Stacked ではいつもそうで、ほかのバリアントではラベルが浮き上がっているか隠れているときだけです。名前にはなりません。

### 必須と任意

`required` と `optional` は、フォームがそのフィールドに何を求めるかを表します。`required` は答えが必要なこと、`optional` は空のままでもよいことです。フィールドが必須でも任意でもあることはありません。両方を設定すると必須になります。

- **必須** は、フィールドを必須として伝え、ラベルの横に必須の印を描きます。
- **任意** は、任意のタグを描き、その言葉をフィールドの説明に加えます。
- **印** は、既定では Stacked のラベルのあとに置く小さなタグです。必須は Filled、任意は Tonal です。言葉は `requiredText` と `optionalText` で、プロダクトのものです。フィールドに書くか、`setWords()` でプロダクト全体に一度だけ渡します。Tanrei は言葉を同梱しません。`requiredText` がなければタグの位置にアスタリスクを描き、`optionalText` がなければタグを描きません。ラベルの行を高くすることはなく、ラベルの横に収まらないときは、次の行に折り返します。
- **アスタリスクにする。** 必須のフィールドがどの印を描くかは、すべてのフォームに共通するデザインシステムの 1 つの選択 `sys.behavior.requirement-mark` で決まります。既定のシステムではタグ、Material のプリセットではアスタリスク、cupertino では何も描きません。`requiredMark` を使うと、フィールドごとに変えられます（`tag`、`asterisk`、または意味だけを残して何も描かない `none`）。Filled と Outlined のフィールドは、タグを求められてもアスタリスクを描きます。
- **浮き上がるラベルでの任意。** Filled と Outlined のフィールドには、浮き上がるラベルの横に任意のタグを置く場所がありません。代わりに、ラベルのあとに `optionalText` を語として描き（「Company Optional」「会社名 （任意）」）、ラベルと一緒に置かれ、浮き上がります。語は、フォームで見せるとおりに、かっこ付きでもなしでも書きます。
- **検証はしません。** 値がないからといって、フィールドが `error` を設定することはありません。`error` と `errorText` は利用者側で設定します。

フォームの中で少ないほうに印を付けます。ほとんどが必須なら任意の項目に、その逆なら必須の項目に付けます。アスタリスクを使うなら（Filled と Outlined のフィールドは、必須のフィールドにいつも使います）、その意味をフォームの中、上のほうで一度書いておきます（「\* 必須」）。アスタリスクは言葉ではなく慣習で、誰もが知っているわけではありません。

必須のフィールドには印が表示されます。印がいらない箇所では、`requiredMark` を `none` にします。

### 補足テキストとエラーテキスト

- `supportingText` はフィールドの下に出す 1 行の補足で、フィールドの説明（description）としても読み上げられます。
- 検証は利用者側で行います。値に問題があれば `error` を設定し、`errorText` を書きます。フィールドがエラーの間、エラーテキストは補足テキストの位置に代わって出るので、エラーのフォームが通常より高くなることはありません。補足に書いたことがまだ大事なら、エラーテキストでも伝えます。補足を見せ続けるデザインシステムは `comp.field.error-text.placement` を `append` にします。このときエラーテキストは補足の下に描かれ、エラーの間、フィールドは 1 行分高くなります（[フィールド](https://tanrei.chnotchy.com/ja/components/field/#%E8%A3%9C%E8%B6%B3%E3%81%A8%E3%82%A8%E3%83%A9%E3%83%BC)を参照）。
- 間違っていることだけでなく、直し方を書きます。「無効です」ではなく「メールアドレスを最後まで入力してください。」です。
- エラーは入力の途中ではなく、フィールドから離れたときか送信したときに出します。

### カウンター

- `maxLength` で入力できる文字数を制限します。数えるのは見た目の文字なので、絵文字や濁点付きの文字も 1 文字です。貼り付けた文字列は収まる長さに切り詰められます。プログラムから設定した値は切り詰めません。
- `counter` を指定すると、補足の行の末尾に文字数を表示します。`maxLength` があれば上限も並べます（「12/40」）。
- カウンターは、表示名や短い自己紹介のように、上限が利用者にとって意味を持つときだけ出します。

### 入力の種類

`type` でキーボード、自動修正、伏せ字入力が決まります。値はどの種類でも文字列です。

| 種類 | キーボードと振る舞い |
| --- | --- |
| `text` | 通常のキーボード。 |
| `email` | メールアドレス用のキーボード。自動大文字化と自動修正はしません。 |
| `password` | 伏せ字の安全な入力。自動修正はしません。常に 1 行です。 |
| `number` | 小数点付きの数字キーボード。値を数値に変換、丸め、検証することはありません。 |
| `search` | 通常のキーボードで、確定キーが「検索」になります。 |
| `tel` | 電話番号用のキーパッド。 |
| `url` | URL 用のキーボード。自動大文字化と自動修正はしません。 |
| `code` | 通常のキーボード。テキストには何も手を加えません。自動修正、自動大文字化、スペルチェック、文字の置き換えはしません。1 行、または `multiline` で複数行。[コード](https://tanrei.chnotchy.com/ja/components/text-field/#%E3%82%B3%E3%83%BC%E3%83%89)を参照してください。 |
| `date`、`time`、`date-time` | 日付、時刻、またはその両方を、ロケールの数字の書き方で単位ごとに入力します。常に 1 行です。[日付と時刻](https://tanrei.chnotchy.com/ja/components/text-field/#%E6%97%A5%E4%BB%98%E3%81%A8%E6%99%82%E5%88%BB)を参照してください。 |

`autocomplete` に HTML の自動入力名（`email`、`current-password`、`one-time-code` など）を指定すると、プラットフォームが自動入力できるようになります。各プラットフォームはそれを自分のコンテンツタイプに対応付けます。長さの決まった短いコードをマス目で描くのは、[ワンタイムコード入力欄](https://tanrei.chnotchy.com/ja/components/one-time-code-input/)です。

### 複数行

`multiline` を付けると、改行を受け付け、複数の行を表示します。高さの決まり方は `resize` で選びます。

| `resize` | 高さ | ハンドル | `minRows` と `maxRows` |
| --- | --- | --- | --- |
| `auto`（既定） | 行数に合わせて、短いモーションで変わります | なし | 最少と最多の行数です。最多を超えるとスクロールします。0 は上限なしです |
| `manual` | 利用者が決めた高さです。`minRows` から始まります | 末尾側の下の角 | いちばん小さい高さと、いちばん大きい高さです |
| `fixed` | `minRows` 行 | なし | `maxRows` は無視されます |

- **Auto** は、コメント、メッセージ、説明など、ほとんどのフィールドに向いています。長い文章でフォームの残りが押し出されては困る場所では、`maxRows` を設定します。
- **Manual** は、メモ、長い説明、コードのように、利用者が読み返して整える文章に向いています。`resizeAxis` は既定で `vertical` です。`both` にすると、レイアウトが許す範囲で、フィールドの幅も変えられます。決めた大きさは保たれ、文章はその中で折り返してスクロールします。
- **Fixed** は、住所の欄のように、レイアウトを動かしたくないフォームに向いています。
- **ハンドルはドラッグなしでも使えます。** 末尾側の下の角にある小さいサイズの[ハンドル](https://tanrei.chnotchy.com/ja/components/handle/)で、ポインターがフィールドの上に来るか、フィールドにフォーカスがあるまでは見えません。`resizeLabel` を名前とするフォーカスが止まる位置です。矢印キーで 1 行ずつ、Page Up と Page Down で `minRows` 行ずつ変わり、Home と End でいちばん小さい高さといちばん大きい高さになります。クリック、Enter、Space では、高さが文章に合い、すでに合っていれば `minRows` に戻ります。`resizeAxis` が `both` のときは、同じクリックでフィールドが置かれた場所いっぱいの幅になり、次のクリックで元の幅に戻ります。ポインターでも、ドラッグせずに幅を変えられます。線は小さくても、ターゲットは最小ターゲットです。人が大きさを変えると、フィールドは表示している行数（縦横に変えられるときは幅も）を添えて `resizeend` を発火します。その人が選んだ大きさを保存しておけます。無効なフィールドにはハンドルがありません。読み取り専用のフィールドは、読むために広さが必要なことがあるので、ハンドルを残します。
- **文章より上は動きません。** 複数行のフィールドの 1 行目は、1 行のフィールドが行を描く位置にあり、アイコン、接頭辞、接尾辞はその行の中央に揃います。2 行目以降はその下に続くので、高さが変わっても、ラベルと 1 行目はその場に留まります。

### コード

`type="code"` は、人が読む文章ではなく機械が読むテキストのための種類です。パターン、識別子、キー、クエリ、JSON や YAML の断片などに使います。フィールドそのものは同じで、ラベル、補足、エラー、カウンター、サイズ、フォームでの振る舞いは変わりません。入力行だけが、コードを書かれたとおりに保持します。

- **どの言語でも左から右。** 値、キャレット、プレースホルダー、接頭辞、接尾辞は、左から始まる 1 本の左から右の行です。右から左のページでも、コードの最初の文字が何であっても変わりません。空のフィールド、`{` だけのフィールド、最初の文字列がアラビア語のフィールドは、同じように配置されます。それ以外はページに従います。ラベル、アイコン、補足テキストまたはエラーテキスト、カウンター、ハンドルです。右から左の文字で書かれた文字列やコメントは、その行の中で配置され、まわりの並びを変えません。
- **コード用の書体を、入力のサイズで。** 書体はコードのロールのものです。サイズと行の高さは入力のままなので、コードのフィールドは隣のテキストフィールドと同じ高さになります。空白はすべて保たれ、タブ文字は次のタブ位置まで進み、2 つの文字が合字になることはありません。意味による色分けはしません。
- **手を加えない。** 値の修正、大文字化、スペルチェック、翻訳、補完をしないこと、まっすぐな引用符を曲がった引用符に変えないことを、プラットフォームに求めます。貼り付けはプレーンテキストで、1 文字ずつそのままです。値の前後を削ること、解析、検証はしません。正しい形かどうかは、利用者側が `error` と `errorText` で伝えます。
- **1 行でも複数行でも。** パターンやキーには 1 行が向いています。`multiline` では、既定で、収まらない行が次の行に折り返します。値が持つ改行は、コード自身の改行だけです。
- **折り返さない行。** `wrap` を false にすると（Web では `no-wrap`）、コードの 1 行が 1 行のまま描かれます。値の表、ログ、改行に意味があるファイルに向いています。長い行はフィールドの中で横にスクロールし、何の幅も広げません。`wrap` を読むのは複数行のコードのフィールドだけです。文章は常に折り返し、1 行のフィールドは常にその行の中でスクロールします。
- **Tab はフォーカスを移します。** キーの意味は変わりません。Tab で字下げはされず、Enter で字下げが足されることもなく、かっこが自動で閉じられることもありません。タブ文字は、貼り付けるかプロダクトが設定すれば入り、そのまま保たれます。
- **エディターではありません。** シンタックスの色分け、行番号、折りたたみ、行ごとのエラー表示はありません。入力せずに見せるコードには[コードブロック](https://tanrei.chnotchy.com/ja/components/code-block/)を使います。エディターが必要なら、プロダクトが選んだものを組み込みます。
- **プレースホルダーはコードの例**で、コードとして描かれます。言葉で書くヒントは `supportingText` に入れます。フィールドが何を求めているか（「JSON」など）を伝えるのも、そこです。

右から左のフィールドでは、コードの値は左から始まります。一方、`email`、`url`、`tel`、`number`、`password` の値は、左から右に並んだうえで、フィールドの始端である右に寄ります。ほかのプラットフォームは、コードの種類をまだ実装していません。

### 日付と時刻

`date`、`time`、`date-time` のフィールドは、入力の行にセグメントを並べます。月、日、年、時、分と、12 時間制では午前・午後が、それぞれ 1 つのセグメントです。ロケールの順に、ロケールの区切りで並びます。フィールドは、値をタイムゾーンのない ISO 8601 の形で保持します。

| 種類 | 値 | en-US での行 | ja-JP での行 |
| --- | --- | --- | --- |
| `date` | `2026-09-27` | 09/27/2026 | 2026/09/27 |
| `time` | `14:30` | 2:30 PM | 14:30 |
| `date-time` | `2026-09-27T14:30` | 09/27/2026, 2:30 PM | 2026/09/27 14:30 |

- **ロケール。** `locale`、または各プラットフォームのロケールが、並び順、区切り、時計の方式、数字を決めます。空のセグメントはそれぞれのプレースホルダー（「mm」「dd」「yyyy」）を表示するので、行はいつも書き方を示します。各セグメントの幅は、プレースホルダーといちばん広い値のうち広いほうなので、入力しても消しても行は動きません。これらの種類では、`placeholder`、`prefixText`、`suffixText`、`maxLength`、`multiline` は無視されます。
- **移動。** Tab は各セグメントを順に通り、そのあとピッカーボタンに進みます。左右の矢印キーは、どのレイアウト方向でも、その側のセグメントに移ります。数字で書く日付と同じく、行は左から右に書かれるからです。セグメントを押すと、そこにフォーカスが移ります。フォーカスのあるセグメントは選択したテキストとして描かれ、キャレットはありません。
- **増減。** 上下の矢印キーは、セグメントを 1 ずつ変え、端では反対の端に戻ります。空のセグメントでは、今日の日付か現在の時刻から始まります。Page Up と Page Down はもっと大きく動かし、Home と End は範囲の端に移します。
- **数字を打つ。** 数字を打つとセグメントが埋まり、それ以上桁が続かなくなったとき、または区切りの文字を打ったときに、フォーカスが次へ進みます。Backspace は 1 桁消し、空のセグメントでは前に戻ります。年は 4 桁必要です。存在しない日付は入力できません。月を選び直して日がその月の末日を超えると、その月の末日になります。
- **貼り付け。** 日付や時刻の全体をどのセグメントに貼り付けても、行全体が埋まります。ロケールの順に並んだ 3 つの数字のまとまりとして読み、最初のまとまりが 4 桁なら年として読むので、貼り付けた ISO の日付はどのロケールでも通ります。コピーと切り取りは、行全体を対象にします。
- **値** は、すべてのセグメントが埋まり、その結果が選べる値になるまで空です。`input` は値が変わったとき、つまり最後のセグメントが埋まったとき、埋まった行を編集したとき、空にしたときに発火します。値が空のままになるセグメントの入力では発火しません。`change` は、値が変わった状態で、フォーカスがフィールドのセグメントとピッカーボタンから外れたとき、または確定キーを押したときに発火します。セグメントの間を移るだけでは、何も確定しません。
- **入力エラー。** 一部だけ埋まったまま離れると、フィールドは `invalidText` を表示します。`min` より前、`max` より後、または `disabledDates` にある値のまま離れると、`unavailableText` を表示します。どちらもエラーとして描かれ、理由（`invalid` か `unavailable`）を付けて `entryError` を発火します。次に編集するとこのエラーは消えます。フィールドが自分で設定する唯一のエラーで、利用者側の `error` プロパティには触れません。
- **プログラムから設定した値** は、そのまま表示し、検証せず、何も発火しません。

フィールドの時計の方式は、ロケールのものです。DateTimePicker と違って、12 時間制か 24 時間制かの設定も、分の刻みもありません。

**ピッカーボタン。** `picker` を付けると、フィールドの末尾にアイコンだけのボタンを描きます。日付なら `calendar_month`、時刻なら `schedule` で、名前は `pickerLabel` です。押すと、値、種類、`min`、`max` を付けて `pickerRequest` を発火します。行が一部だけ埋まっているときは空の値を送り、入力エラーは出しません。このあとピッカーが値の全体を返すからです。フィールド自身は何も表示しません。プロダクト側で、対応するモードの [DateTimePicker](https://tanrei.chnotchy.com/ja/components/date-time-picker/) を開き（ドック型ならフィールドに固定し）、ピッカーの `change` からフィールドの `value` を設定します。ピッカーには、フィールドと同じ `min`、`max`、`disabledDates` を渡します。ピッカーが閉じると、フォーカスはボタンに戻ります。ボタンはフィールドと一緒に無効になり、読み取り専用のあいだは描かれません。

### 推奨

- 通貨記号や単位など、値に含めない固定のテキストは `prefixText` と `suffixText` に入れます。
- 入力を消す、パスワードを表示するといったアクションは、名前を付けた自前のアイコンだけのボタンとして末尾のスロットに置きます。フィールドに組み込まれているアクションはピッカーボタンだけで、利用者側のアクションのあとに並びます。
- 数行にわたりうるテキストには `multiline` を使います。既定では `minRows` から `maxRows` まで伸び、それを超えるとスクロールします。
- フォームが求めるフィールドには `required` を設定します。そのうえで、送信時に何が足りないかを `errorText` で伝えます。
- 機械が読む値（パターン、キー、設定の断片）には `type="code"` を使い、何を書くべきかをラベルと `supportingText` で伝えます。

### 非推奨

- プレースホルダーをラベルの代わりにしない。入力を始めた途端に消えてしまいます。
- カード番号や郵便番号など、量ではない値に `number` を使わない。`text` と適切な `autocomplete` を使います。
- 1 つのフォームでバリアントを混ぜない。
- テキストフィールドをフィールドの中に入れない。すでにフィールドで、デバッグビルドがそのことを報告します。フィールドが囲むコントロールは、テキスト入力欄です。
- 日付を `text` のフィールドと自前の解析で扱わない。`date` を使えば、ロケールの並び順、セグメントのキー操作、入力エラーが付いてきます。
- `required` を設定したうえで、ラベルにも「（必須）」と書かない。印がすでに伝えており、スクリーンリーダーでは 2 度読まれます。
- 手で大きさを変えるフィールドを、長い文章を見る唯一の手段にしない。開いた時点で役に立つだけの `minRows` を設定します。
- 変えられない値を見せるためだけにフィールドを無効にしない。`readOnly` にすれば、フォーカス、選択、コピーはできます。
- `::part(input)` や自前のスタイルで、テキストフィールドに外からコード用の書体を当てない。要素に `dir` や `spellcheck` を足すこともしない。`type="code"` を使います。書体だけを変えたフィールドは、見た目は正しくても、入力された文字を修正してしまいます。
- コードのフィールドをエディターとして使わない。長いコードを見せるために無効にもしない。無効なフィールドにはキャレットがなく、スクロールできません。`readOnly` にするか、コードブロックを使います。

## アクセシビリティ

### コンポーネントが担うこと

- 編集はプラットフォーム自身のテキスト要素で行うので、IME、音声入力、自動入力、選択、クリップボードは、そのプラットフォームのほかの入力欄と同じように動きます。日付と時刻は明示された例外で、セグメントの行はフィールド自身のものです。
- 日付や時刻のフィールドは、ラベルを名前とするグループです。各セグメントは、単位とラベルを名前とするスピンボタンで（「month, Start date」）、範囲と値を伝えます。空のセグメントは `segmentEmptyText` を読み上げます。
- 必須のフィールドは、必須として伝えます。印そのものは支援技術から隠すので、2 度読まれることはありません。任意の言葉は、フィールドの説明と一緒に読み上げられます。
- サイズ変更のハンドルは、`resizeLabel` を名前とするフォーカスが止まる位置で、行数を値とするセパレーターとして伝えます。矢印キー、Home、End で大きさを変えられるので、ドラッグに頼る操作はありません。ヒット領域は最小ターゲットを満たします。
- ラベルは、描かれていても隠されていても、アクセシブルネームになります。プレースホルダーが名前になることはありません。
- 表示中の補足テキストまたはエラーテキストと、文字数が、フィールドの説明になります。表示中の接頭辞と接尾辞も説明に含まれるので、単位や通貨は値と一緒に読み上げられます。
- 無効状態を伝えます。invalid、必須、読み取り専用の状態も、プラットフォームにあれば伝えます。日付や時刻のフィールドの入力エラーは、`error` とまったく同じように伝えます。
- ピッカーボタンは `pickerLabel` を名前とするボタンで、ダイアログを開くことを伝え、タブ順では最後のセグメントのあとに来ます。
- フォーカス中にエラーが現れるか変わると、1 度だけ控えめに読み上げます。文字数は、入力が 1 秒止まったときに表示どおり（「12/40」）1 度だけ読み上げ、キーを打つたびには読みません。
- 検索フィールドは、プラットフォームに検索ボックスのロールがあれば、そのロールで伝えます。
- コードのフィールドは、ほかの種類と同じテキストボックスで、`multiline` なら複数行のテキストボックスです。コードを保持していることを伝えるロール、ロールの説明、言葉はなく、値のどの部分もスペルの誤りとして示されません。Tab では必ずフィールドを離れます。行を折り返さない場合は、キャレットの移動でスクロールし、フォーカスはスクロールしないコンテナに示されます。
- フォーカスは、キーボードでもポインターでも、フィールド自身の縁で示します。Stacked ではボックスの内側の線、Filled と Outlined では太く色の付いた縁です。ヒット領域は最小ターゲットを満たします。
- ラベルかボックスを押すと、フォーカスが入力欄に移ります。何かが操作されることはありません。補足、エラーテキスト、文字数を押しても、何も起こりません。
- 名前、説明、状態は、フィールドが囲んだコントロールに結び付けるのとまったく同じように結び付けられます。1 つ下の階層で自分で書いたフィールドも、同じテキストボックスになります。
- ラベルがない場合は、デバッグビルドで報告します。

プラットフォームごとの既知の制約：

- SwiftUI、Compose、Flutter、React Native は、印を描かない `required` フラグのままで、リサイズの方式とハンドル、セグメントはありません。
- Web：月や分に最初の数字が入ると、セグメントの行が数ピクセルずれます。ロケールの最後のセグメントが 2 桁の場合、`input` が 2 回発火することがあります。Android の画面上のキーボードでは、空のセグメントで Backspace を押しても前に戻れません。狭いフィールドより幅の広い行は、スクロールせずにはみ出します。
- 一部だけ埋まった日付の行は、すでに空文字列である `value` に空文字列を設定しても、空にできません。
- Web：フォーカススタイルが glow のとき、Filled のフィールドの光彩は、角のない下側の角まで丸くなります。
- Web：IME の変換と、自動入力されたときのラベルは、Chromium でしかテストしていません。
- Web：行を折り返さない読み取り専用のコードのフィールドは、ブラウザーが編集しないテキストをスクロールするのと同じキーでスクロールします。Firefox ではキャレットで、Chrome では矢印キーで、Safari では Option と矢印キーです。Firefox では、ページの向きが変わると、このフィールドのスクロール位置が左端に戻ります。
- SwiftUI、Compose、Flutter、React Native は、コードの種類をまだ実装していません。これらでは `type` に `code` がなく、フィールドに `wrap` もありません。
- SwiftUI には invalid、必須、読み取り専用の状態がありません。これらは描画や動作には反映されますが、読み上げられません。エラーは説明に含まれるテキストとしてしか伝わりません。
- SwiftUI は IME の変換中の状態を知ることができません。そのため `maxLength` は変換中の文字にも適用され、変換中にプログラムから変えた値も確定まで待たずに適用されます。
- SwiftUI：選択したテキストのハイライトは、フィールドの選択色ではなく、キャレットの色合いになります。
- SwiftUI は、読み取り専用のフィールドの値を、テキストフィールドではなく選択できるテキストとして描きます。
- SwiftUI が `.labelsHidden()` を読めるのは iOS 18 と macOS 15 からです。それより前のバージョンでは、ラベルは描かれたままです。
- React Native は IME の変換中の状態を知ることができません。変換中にプログラムから変えた値や `maxLength` による制限はすぐに適用され、変換が途中で確定してしまうことがあります。
- React Native には invalid、必須、読み取り専用の状態がありません。エラーは説明に含まれるテキストとしてしか伝わらず、必須と読み取り専用は読み上げられません。
- React Native：読み上げ、自動入力、種類ごとのキーボードはモックでしか試しておらず、実機ではまだ確かめていません。
- iOS の React Native では、キャレットと選択範囲のハイライトの色合いが 1 つしかありません。フィールドはキャレットの色を優先するので、ハイライトはフィールドの選択色ではなく、キャレットの色を薄めたものになります。
- React Native：Filled のフィールドの光彩は上側の角にしか沿わず、プレースホルダーとキャレットの色は、状態のモーションに沿って変わらずに一度に切り替わります。
- Flutter は検索フィールドを、検索ボックスのロールではなく、検索用の入力種別として伝えます。
- Flutter は Android などのタッチ画面で、選択ハンドルと選択ツールバーを描きません。長押しとキーボードショートカットでの選択とコピーはでき、iOS ではシステムのメニューが出ます。
- Flutter：先頭のスロットに置いた操作できる要素は、フォーカス順で入力欄の後ではなく前に来ます。
- Compose には必須の状態がないため、代わりに状態の説明として「Required」を読み上げます。
- Compose：`maxLength` と文字数は見た目どおりの文字単位で数えますが、絵文字の連結や国旗はテストしていません。テストは Robolectric 上の JVM で動き、その文字の規則がこれらを知らないためです。
- Compose：TalkBack によるエラーと文字数の読み上げ、実際の IME、実際の自動入力サービスは、実機ではまだ試していません。

### 利用者側で行うこと

- 値の内容を表すラベルを書き、隠す場合も必ず設定します。
- 直し方が分かるエラーテキストを書き、`error` は利用者側で設定します。SwiftUI と React Native では、そのテキストがエラーを耳で知る唯一の手段です。日付や時刻のフィールドでは、`invalidText` と `unavailableText` も書き、`segmentEmptyText` を渡します。これがなければ、空のセグメントは値なしとして読み上げられます。
- ピッカーボタンの `pickerLabel` をプロダクトの言語で設定し、`pickerRequest` を受けてピッカーを開きます。
- `required` を設定し、`requiredText` と `optionalText` を渡します。フィールドに書くか、`setWords({ requiredText, optionalText })` でプロダクト全体に一度だけ渡します。`resizeLabel` と `segmentEmptyText` も同じように渡せます。Web では Tanrei は言葉を同梱しません。足りない言葉は開発時に報告されます。[プロダクトの言葉](https://tanrei.chnotchy.com/ja/foundations/words/)を参照してください。SwiftUI、Compose、Flutter、React Native が追従するまで、これらのフィールドは印を描かず、SwiftUI と React Native は必須の状態を読み上げません。これらのプラットフォームでは、必須であることをラベルに書きます。
- サイズ変更のハンドルに、`resizeLabel` で名前を付けます。これがなくてもハンドルは描かれて動作しますが、名前を持ちません。
- 単位や通貨は値に含めず、`prefixText` か `suffixText` に入れます。そうすれば入力する必要がなく、値と一緒に読み上げられます。
- コードのフィールドでは、何を書くべきかを `label` と `supportingText` で伝え（「Webhook の本文」「JSON」）、何が誤っているかを `errorText` で伝えます。フィールドはその言語を知りません。
- 末尾のアイコンボタンには、プロダクトの言語で名前を付けます。
- Compose では「Invalid」と「Required」という言葉がライブラリの文字列リソース（`tanrei_invalid`、`tanrei_required`）から来ます。翻訳するには、アプリのリソースで上書きします。
- Web では、iOS Safari が 16 px より小さい文字の入力欄に入るとページを拡大します。既定のシステムのフィールドは、どちらのサイズでも 16 px の文字を使います。レイヤでそれより小さくすると、拡大がまた起こります。
