メインコンテンツへスキップ
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
はじめにWebReactSwiftUIJetpack ComposeFlutterReact Nativeプリセット自分のデザインシステムを作るAI エージェントと Tanrei を使うブランドデザイントークンデザインシステムとカスタマイズTheme Studioカラータイポグラフィアイコン形状エレベーションモーションインタラクション状態振る舞いレイアウトアクセシビリティ言葉カラートークンタイプスケールトークン形状トークンエレベーショントークンモーショントークン状態トークンスペースと寸法のトークンコンポーネントボタンメインアクションボタンセグメントボタンボタングループメニュー付きボタンツールバーコピーボタンチェックボックスラジオボタンスイッチチップメニュースライダースライダーフィールドカレンダーDateTimePickerフィールドグループラジオグループチェックボックスグループセレクトセレクトフィールドメニューバーファイルアップロードファイルピッカー 上級者向けドロップゾーンテキスト入力欄 上級者向けテキストフィールドチップフィールドフィールドコンボボックス 上級者向けコンボボックスフィールド数値入力欄 上級者向け数値フィールドカラー入力欄 上級者向けカラーフィールドワンタイムコード入力欄 上級者向けワンタイムコードフィールド埋め込みテキスト入力欄チャットコンポーザーカード区切り線モーダルレイヤー 上級者向けダイアログリストリスト行ボトムシートサイドシートカルーセルコードブロックテーブルポップオーバーステップアコーディオン説明リスト図アプリシェルページヘッダーツリービュータイムライン画像プレビューハンドルサイズ変更できる枠スクロールエリアバッジプログレスインジケーターメーターナンバーティッカーローディングインジケータースナックバーツールチップキーボードショートカットタグインラインコードコールアウトアバター空の状態タググループスケルトンファイル項目色見本マーキーテキストタブタブビューアプリバーナビゲーションバーナビゲーションドロワー検索リンクページネーション前後のリンクステッパーパンくずリスト目次リンクカードFAB メニューステータスTanrei の作り方コントリビューションライセンスと帰属
ホームコンポーネント埋め込みテキスト入力欄

埋め込みテキスト入力欄

ほかのものが描くボックスのための、入力部分だけのコンポーネントです。テキスト、プレースホルダー、キャレット、選択範囲を、1 行か、動きを伴って伸び縮みする複数行で描きます。コンテナも、縁も、ラベルも、フォーカスの表示も描きません。ボックスを描き、フォーカスを示すのは、これを持つ側です。

content_copyMarkdown で表示

上級者向け

ほとんどのプロダクトでは、このコンポーネントを部品として含むChat composerを配置します。

カテゴリ
テキスト入力
トークン
comp.embedded-text-input
レベル
基礎
使用先
Chat composer

概要

Web
tanrei-embedded-text-input
schedule
SwiftUI
TanreiEmbeddedTextInput
radio_button_unchecked
Jetpack Compose
TanreiEmbeddedTextInput
radio_button_unchecked
Flutter
TanreiEmbeddedTextInput
radio_button_unchecked
React Native
EmbeddedTextInput
radio_button_unchecked

生成元 docs/status/status.json, specs/embedded-text-input.spec.json

ページが描いたボックスの中の埋め込みテキスト入力欄。何行か入力すると、ボックスが高さに追従します。フォーカスはボックスの縁で示されます。

埋め込みテキスト入力欄は、コントロールの半分です。文字を入力する場所だけで、まわりには何もありません。塗りも、縁も、余白も、ラベルも、メッセージもありません。残りの半分がホルダー、つまり入力部分を囲むボックスです。チャットコンポーザーのように Tanrei のコンポーネントがホルダーになることも、利用者側のマークアップがホルダーになることもあります。

ほとんどのプロダクトでは、これを直接置くことはありません。ボックスがテキスト以外のものも持つときに使います。アクションと並んだメッセージ、検索バー、その場で編集するタイトルなどです。

次の場合は、ほかのコンポーネントを使います。

フォーカスはホルダーが示します

埋め込みテキスト入力欄は、デザインシステムのフォーカスの様式が何であっても、フォーカスの表示を描きません。キャレットはその代わりになりません。細い線で、テキストを選択している間は描かれず、どのボックスに入力しているのかも伝えないからです。ボックスにフォーカスを示さないホルダーは、WCAG 2.4.7 を満たしません。Web では、:focus-within のルールが 1 つあれば足ります。

Web では <tanrei-embedded-text-input> です。ほかのプラットフォームでは、まだ実装されていません。

使い方

ボックス、その余白、フォーカスの表示は利用者側のものです。3 つの宣言で用意できます。

<div class="capsule">
  <tanrei-embedded-text-input label="メッセージ" placeholder="メッセージを入力"
                              multiline max-rows="6"></tanrei-embedded-text-input>
</div>
.capsule {
  background: var(--tanrei-sys-color-surface-container);
  border-radius: var(--tanrei-sys-shape-corner-large);
  padding: var(--tanrei-sys-space-space150) var(--tanrei-sys-space-space200);
  outline: var(--tanrei-sys-border-thick) solid transparent;
  outline-offset: calc(-1 * var(--tanrei-sys-border-thick));
  cursor: text;
}
.capsule:focus-within { outline-color: var(--tanrei-sys-color-focus-ring); }
@media (forced-colors: active) {
  .capsule { outline-color: CanvasText; }
  .capsule:focus-within { outline-color: Highlight; }
}
// 余白を押すことは、テキストを押すことです。
capsule.addEventListener('mousedown', (event) => {
  if (event.target !== capsule) return;
  event.preventDefault();
  input.focusAt(event.clientX, event.clientY);
});
// 上級者向けのコンポーネント(ほかのコンポーネントの部品)は、advanced のエントリーから読み込みます。
import { EmbeddedTextInput } from '@tanrei/react/advanced';

<div className="capsule">
  <EmbeddedTextInput label="メッセージ" placeholder="メッセージを入力" multiline maxRows={6}
                     onAction={(event) => send(event.detail.value)} />
</div>

仕様

ホルダーが担うこと

項目 ホルダーが用意するもの 入力欄がホルダーに渡すもの
ボックス 見えるボックス、またはボックスとして読める場所 テキストの背後には何も描かず、ホルダーの塗りがそのまま見えます
フォーカス 入力欄にフォーカスがある間、どの方法でフォーカスしたかを問わず、ボックスに示すフォーカスの表示 focusin と focusout、および要素の :focus-within
状態 エラーの印と、無効なボックスの見た目 error は無効な値であることを支援技術に伝えるだけで、何も描きません。disabled はテキストを無効な内容として描きます
余白 テキストのまわりの余白と、その余白を押した操作の引き渡し 自身の余白はなく、最小ターゲットを満たす反応範囲と focusAt(x, y) を持ちます
名前 名前と、メッセージの内容 label(必須)と description。どちらもテキストで渡します
アクション アクションが何であるか。送信、追加、検索、保存など action イベント。入力欄はそれ以上何もしません
強制カラー システムの色で描いたボックスの縁とフォーカスの表示 システムの色で描いた自身のテキスト、プレースホルダー、選択範囲、キャレット

高さ

multiline を指定すると、入力欄はちょうど行の分の高さになります。範囲は minRows(既定は 1)から maxRows(既定は 0 で、上限なし)までです。maxRows を超えた分は、入力欄の中でスクロールします。

  • 値の変化で行数が変わると、高さは comp.embedded-text-input.motion.resize-duration の時間をかけて変わります。一度に何行増えても、動きは 1 回です。
  • レイアウト上の入力欄の高さは、どのフレームでもアニメーション中の高さです。内容に合わせて大きさが決まるホルダーは、コードを書かなくても追従します。
  • 最初に描かれたとき、幅や文字サイズが変わったとき、テキストの上でポインターを押している間、モーションを減らす設定のときは、高さはすぐに変わります。
  • rowschange は、動きが始まる前に、これから表示する行数を伝えます。rows はその数をいつでも返します。

Enter

入力欄 Enter Shift+Enter Ctrl+Enter、Command+Enter
1 行 action — action
複数行、enterKey="line-break"(既定) 改行 改行 action
複数行、enterKey="action" action 改行 action

インプットメソッドの Enter は、候補を選ぶか、テキストを確定します。action は発行されません。画面上のキーボードの改行キーは、複数行の入力欄では常に改行します。

action は値を運ぶだけで、入力欄はそれ以上何もしません。値、選択範囲、フォーカスはそのまま残ります。

構成要素

パーツ 説明
1 input The platform's own text element, which holds the value, the selection and any input-method composition, drawn with no box of the platform's or of Tanrei's. It is the whole component: as wide as its layout gives it and exactly as tall as its lines.
2 placeholder · 任意 A hint in the empty input, where the first character will be typed; never the name.
3 caret The insertion point, and the only thing the input itself shows of having focus.
4 selection · 任意 The highlight behind selected text and the color of the text on it.

プロパティ

プロパティ 型 既定値 説明
type text | search | email | url | tel "text" What the value is: chooses the keyboard, autocorrection and autocapitalization, and nothing else. It never validates and never obscures. search makes the action key of an on-screen keyboard read as search. The types that need a box around them are the Text input's: a password (its reveal action), a number (its steppers), a date or a time (its segments and its picker button), code (its face and its sideways scrolling).
value string "" The text.
label string "" The accessible name. Required: the embedded text input draws no label and asks no Field for one, so this is the only name it has. Its holder gives it — a composed component from its own label, a product from the words that stand beside its box. Never drawn.
description string "" The accessible description, as text: what a help line, an error text or a count that its holder draws says about what is typed. Never drawn.
placeholder string "" A hint drawn while the value is empty. Never the name.
multiline boolean false False: one line that scrolls sideways inside itself. True: lines that wrap, and a height that follows them between minRows and maxRows, changed with a motion.
minRows number 1 The fewest lines a multi-line input is tall. 1 by default — a holder's typing part begins as one line and grows — where the Text input, which is a box, begins at 2.
maxRows number 0 The most lines a multi-line input is tall; beyond it the text scrolls inside. 0 is no limit. A holder that stands at the bottom of a screen sets one.
enterKey line-break | action "line-break" What the Enter key of a hardware keyboard does in a multi-line input. line-break: it begins a new line. action: it emits action and inserts nothing, and Shift+Enter begins a new line. Under both, Ctrl+Enter (and Command+Enter on Apple platforms) emits action, the return key of an on-screen keyboard begins a new line, and an input method's Enter does neither. A single-line input ignores it: its action key always emits action. What action means — send, add, search, save — is the holder's.
error boolean false Exposes the input as invalid. It draws nothing: marking an error is the drawing of the holder's box.
required boolean false Exposes the input as required and, on the web in a form of its own, reports a missing value. It draws nothing and validates nothing else.
disabled boolean false Not focusable and not editable; the text and the placeholder read as disabled content. The disabled look of the box is its holder's.
readOnly boolean false Focusable, selectable and copyable, not editable. Drawn as an enabled input.
autocomplete string "" The platform's autofill hint for the value (an HTML autocomplete token; mapped to the platform's content type where it has one). Empty: the platform decides.

イベント

イベント 説明
input The person changed the value; carries the new value. Emitted for every change, never when value is set.
change The person committed a changed value: focus left the input, or action was emitted. Carries the value.
action The person asked for the action of whatever holds the input: the action key of a single-line input; in a multi-line input, Enter where enterKey is action, and Ctrl+Enter or Command+Enter always. Carries the value. The input does nothing more: it does not empty itself, does not move focus and, where it has several lines, asks no form to submit. Cancelable where the platform has that.
rowsChange The number of lines the input shows is about to change, or it was drawn for the first time; carries the number of lines it is going to show and whether text scrolls inside it. Emitted once per change, before the motion begins, so that a holder can change with it — a corner that is round for one line and less round for several, actions centred on one line and set at the bottom of several.
focusChange The input gained or lost focus; carries true or false. It is the state the holder reads to draw the focus indication on its own box, which the input never draws.

状態

振る舞い

EARS 記法による要件です。各プラットフォームの実装は、この ID に対応するテストで検証されます。要件の原文は仕様ファイルのものをそのまま掲載しています。

ID 要件
ETI-1 THE embedded text input SHALL draw only what is typed and what stands for it: the value in comp.embedded-text-input.input.typescale and comp.embedded-text-input.standard.input.color, the placeholder while it is empty (ETI-22), the caret in comp.embedded-text-input.standard.caret.color and the selection highlighted in comp.embedded-text-input.selection.color under comp.embedded-text-input.selection.content.color. It SHALL draw no container, fill, edge, corner, shadow, label, requirement mark, help, error text, count, icon or state layer, and no focus indication (ETI-11), in any state; it SHALL have no padding of its own, be as wide as its layout gives it and exactly as tall as its lines (ETI-7), and leave whatever its holder draws behind it to show through.
ETI-2 WHEN the person changes the value — by typing, deleting, pasting, cutting, dictating, autofill or an input method committing text — THE embedded text input SHALL emit input once per change with the new value; WHEN value is set by its holder or the product THE embedded text input SHALL show it and SHALL NOT emit input or change, and WHILE it has focus SHALL place the caret at the end of the new value unless the value is the one it already shows.
ETI-3 WHEN the input loses focus, or emits action (ETI-6), with a value different from the one last committed (the value when it gained focus, or at the last change) THE embedded text input SHALL emit change once with the value, before the action.
ETI-4 WHILE an input method is composing text THE embedded text input SHALL leave the composition, and every key pressed during it, to the platform's native text element: it SHALL NOT write the value back into the element, trim or reformat it, and SHALL NOT emit change; the Enter that chooses a candidate or commits the composition SHALL NOT emit action, SHALL insert no line break of the input's own and SHALL NOT be counted as a request for the action — also where the platform reports that key only after it has reported the composition ended; a committed composition SHALL always produce an input, a value set during a composition SHALL be applied when the composition ends, and the first Enter pressed after the composition has ended SHALL be read as ETI-6 says.
ETI-5 WHILE multiline is false THE embedded text input SHALL show its value on one line that never wraps: text wider than the input SHALL scroll sideways inside it, by pointer, touch and the caret, which is kept in view, and SHALL NOT widen the input; a line break in pasted, dropped or dictated text SHALL be taken as the platform's own single-line element takes it. WHILE multiline is true THE embedded text input SHALL wrap its text at its own width, break a run with no space that is wider than a line between any two characters, never scroll sideways, and count every line the text wraps onto as a line (ETI-7).
ETI-6 WHEN the person presses the keyboard's action key in a single-line input THE embedded text input SHALL emit action with the value, after any change it causes (ETI-3), and, on the web, WHILE it is a control of a form itself (ETI-19), SHALL then request that form's submission through its default button (BTN-30) unless action was cancelled. WHILE multiline is true: where enterKey is line-break, Enter on a hardware keyboard SHALL begin a new line and emit nothing; where enterKey is action, Enter with no modifier SHALL emit action and insert nothing, and Shift+Enter SHALL begin a new line; under both values Ctrl+Enter and, on Apple platforms, Command+Enter SHALL emit action and insert nothing; the return key of an on-screen keyboard SHALL begin a new line under both values, and the input SHALL ask that keyboard for a return key; and a multi-line input SHALL never request a form's submission. THE embedded text input SHALL do nothing else when it emits action — it SHALL keep its value, its selection and its focus — because what the action is belongs to its holder. WHEN enterKey names a value it does not know it SHALL behave as line-break and in debug builds report the unknown value; WHILE readOnly is true no key SHALL emit action.
ETI-7 WHILE multiline is true THE embedded text input SHALL be exactly as tall as its lines of text, each one line height of its text style — never fewer than minRows lines and never more than maxRows lines; no upper limit while maxRows is 0, exactly minRows lines while maxRows is smaller than minRows, and a minRows below 1 is 1. WHILE the text has more lines than maxRows it SHALL scroll inside the input and keep the caret in view, and WHILE every line fits it SHALL show no scrollbar. THE height SHALL follow every cause of a changed number of lines — typing, deleting, pasting, a value that is set, a changed width, enlarged text. WHILE multiline is false THE embedded text input SHALL be exactly one line tall and ignore minRows, maxRows and enterKey.
ETI-8 WHEN the number of lines a multi-line input shows changes because its value changed — by the person, or because value was set — THE embedded text input SHALL change its height from the one shown to the new one over comp.embedded-text-input.motion.resize-duration with comp.embedded-text-input.motion.resize-easing, as one motion: however many lines arrive or leave at once — forty pasted lines, a value emptied after it was sent — the height SHALL go from the one shown straight to the end height, which maxRows bounds, and never through a height for each line; a change that arrives while a motion is running SHALL continue from the height then shown to the new end height, and changes SHALL never be queued one after another. At every frame of the motion the first line SHALL stay where it is at the input's top edge, the text SHALL keep its size and its line height, the line that holds the caret SHALL stay in view, and no scrollbar SHALL be shown WHILE the end height holds every line.
ETI-9 THE embedded text input SHALL make a change of its height at once, without the motion of ETI-8, WHEN it is first drawn or drawn again after it was not shown; WHEN the height changes because its width, its text size, its text style, minRows, maxRows or multiline changed; WHILE a pointer or a touch is held down in its text, so that text under a selection being dragged never moves away from the finger; and WHERE motion is reduced.
ETI-10 THE embedded text input SHALL change the height it takes in its layout at every frame of a motion — the height its holder and whatever follows it are laid out against, not a drawing moved or scaled over a box that already has the end height — so that a holder that sizes itself to its content follows the motion with no jump at its start or its end, and SHALL add no motion of its own to anything outside itself. THE embedded text input SHALL emit rowsChange once when it is first drawn and once for every later change of the number of lines it shows, before any motion of that change begins, with the number it is going to show and whether text scrolls inside it; it SHALL NOT emit it for a height that changes with the text size alone.
ETI-11 THE embedded text input SHALL show that it has focus by its caret alone and SHALL draw no focus indication of its own — no ring, no line and no glow, whatever sys.behavior.focus-style is — because the box around it is its holder's, and the indication is drawn on the box. THE embedded text input SHALL report its focus so that the holder can draw it: it SHALL emit focusChange with true WHEN its text element gains focus — by the keyboard, a pointer, a touch or a script alike, because a place that is typed in shows where typing will go however it was focused — and with false WHEN it loses it, and on the web its host SHALL match :focus-within for exactly that time.
ETI-12 THE embedded text input SHALL take a press anywhere in an area at least sys.target.touch high for touch input and sys.target.pointer high for fine pointers — its own lines, and beyond them equally above the first and below the last where they are shorter than that — as a press on its text, without changing its drawn size or its layout; a platform that cannot tell the input type SHALL use sys.target.touch. A press there SHALL give the input focus and place the caret, SHALL NOT be answered with a pressed state, a ripple or any feedback, because a press on a place to type is not an action, and THE embedded text input SHALL offer its holder a way to give it focus, so that a press on the holder's box outside its other controls can be passed on to it.
ETI-13 THE embedded text input SHALL be exposed to assistive technology as a text box — a multi-line one WHILE multiline is true — whose accessible name is label and whose description is description, invalid WHILE error is true, required WHILE required is true, read-only WHILE readOnly is true and disabled WHILE disabled is true. It SHALL never use placeholder as the name; IF label is empty it SHALL still render and work, and in debug builds SHALL report that it has no accessible name. THE embedded text input SHALL ask no Field for a name, a description or a state: it is not a control a Field frames — its holder is — and every one of them reaches it as a prop.
ETI-14 WHILE error is true THE embedded text input SHALL draw exactly what it draws while error is false — the same text, placeholder and caret colors — and SHALL NOT set error itself or validate anything: an error is marked on the holder's box and said in the holder's or a Field's words.
ETI-15 WHILE disabled is true THE embedded text input SHALL NOT be focusable or editable, SHALL NOT emit input, change, action or focusChange, SHALL expose the disabled state to assistive technology, SHALL be left out of form submission on the web, and SHALL draw its value and its placeholder from the disabled tokens of those parts.
ETI-16 WHILE readOnly is true and disabled is false THE embedded text input SHALL stay focusable, selectable and copyable, SHALL NOT let the person change the value, SHALL NOT emit input, change or action, SHALL expose the read-only state to assistive technology, SHALL be submitted with its form on the web, and SHALL draw as an enabled input.
ETI-17 THE embedded text input SHALL give its text element the keyboard and the semantics of type: text a general keyboard; search a general keyboard whose action key reads as search; email an email keyboard without autocapitalization or autocorrection; url a URL keyboard without autocapitalization or autocorrection; tel a phone keypad — in a multi-line input too, as far as the platform lets a multi-line element choose a keyboard. It SHALL NOT validate the value against the type. WHEN type names a value it does not know it SHALL behave as text and in debug builds report the unknown value.
ETI-18 THE embedded text input SHALL pass autocomplete to the platform as its autofill hint, mapped to the platform's content type where it has one, and WHEN the platform autofills it SHALL emit input with the filled value.
ETI-19 WHERE the platform has a form element (the web) THE embedded text input SHALL be a control of the form it stands in: it SHALL submit its value under its name, nothing while disabled or while it has no name, reset to the value it was first given, be restored when the page is, follow a disabled fieldset, and report a missing value WHILE required is true and it is empty. WHILE it stands in the shadow tree of a holder it has no form — the holder is the form's control and carries the value — and it SHALL do none of this.
ETI-20 THE embedded text 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. It SHALL lay out the typed text by its own direction: the base direction of the value's first strong character and, WHILE the value is empty or holds no strong character, the layout direction, so that the caret of an empty input and its placeholder stand at the start edge; a single-line value SHALL be aligned to the start edge of its own direction, in a multi-line input each paragraph SHALL take the direction and the alignment of its own first strong character, and the values of email, url and tel inputs SHALL be laid out left-to-right, aligned to the start edge. The placeholder SHALL be laid out as text of its own direction, isolated from what is around it, and aligned to the start edge. WHEN the layout direction changes while the input is shown it SHALL draw itself for the new direction at once, keeping its value, its selection, its caret and its focus.
ETI-21 THE embedded text input SHALL let every key the person presses in it reach its holder before the input itself acts on it, and SHALL NOT act on a key the holder has cancelled where the platform lets a key be cancelled; and it SHALL let its holder read where the selection begins and ends and set it, and replace the selected text. With these a holder does what only it can know: an arrow or Backspace at the very start of the text that moves to something before it, text an action of the holder's inserts at the caret. THE embedded text input SHALL itself give no key a meaning beyond those of ETI-6: Tab and Shift+Tab SHALL move focus out of it and never insert a character, and Escape SHALL do nothing.
ETI-22 WHILE the value is empty and no input method is composing THE embedded text input SHALL show placeholder in its text style and comp.embedded-text-input.standard.placeholder.color, starting where the first character will be typed; in a multi-line input a placeholder longer than a line SHALL wrap as text does and the input SHALL be as tall as the placeholder's lines, within minRows and maxRows, so that it is never cut; in a single-line input it SHALL be cut at the end edge. THE placeholder SHALL be kept from the accessible name and SHALL go with the first character typed, without a motion.
ETI-23 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 embedded text input SHALL draw its value in the platform's text color, its placeholder and a disabled value in the platform's color for text that is not available, its selection in the platform's highlight colors and its caret as the platform draws one, and SHALL draw nothing else, as everywhere: the edge of the box and the focus indication in those colors are its holder's.
ETI-24 WHILE the text size is enlarged THE embedded text input SHALL grow with its text — a line is the enlarged line height, and minRows and maxRows still count lines — changing its height at once (ETI-9); at twice the text size in a holder 320 wide a multi-line input SHALL show every character, wrapped, with no sideways scrolling and nothing cut, and a single-line input SHALL keep its one line and scroll inside itself.
ETI-25 WHEN a prop changes while the input is shown THE embedded text input SHALL draw the new state at once, keeping its value, its selection and caret, its focus and a composition in progress; WHEN multiline changes it SHALL keep the value as it is — a value with line breaks is shown on one line as the platform's single-line element shows it, and the line breaks are there again when multiline returns — and change its height at once (ETI-9).
ETI-26 THE embedded text input SHALL have no words of its own and read none of the product's words: label, description and placeholder are the instance's props and nothing stands in for an empty one.
ETI-27 WHERE script does not run, or before the element is defined (the web), THE embedded text input SHALL leave the platform's own text element that the product wrote as its child — a textarea, or an input — as the control it is: named, typed in and submitted with its form. WHEN the element is defined it SHALL take that child's text as its first value where value was not given, and SHALL hide and disable the child so that the form submits the input alone.
ETI-28 WHILE the embedded text input is drawn with nothing around it that marks a box THE embedded text input SHALL still render and work exactly as it does in one: it cannot see whether a box is drawn, reports nothing about it, and falls back to no look of its own. That a box is drawn, that it shows focus (ETI-11), marks an error and a disabled input, pads the text and passes a press on (ETI-12) are held where the holder is: in the spec and the tests of a Tanrei component that is built from it, and by the product for a box of its own.
ETI-29 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 embedded text 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 embedded text input is invalid — except those the embedded text input owns (aria-label, aria-labelledby, aria-description, aria-describedby, aria-errormessage, aria-invalid, aria-required, aria-readonly, aria-disabled), which it SHALL move and not expose: they reach it as props (ETI-13). 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 embedded text input SHALL take it off that element. WHEN the product sets an aria- attribute the embedded text input owns THE embedded text input SHALL report it in debug builds.

プラットフォーム別の名前対応

パリティは API が同一であることを意味しません。同じ契約を、各プラットフォームの流儀で表現します。

仕様 Web SwiftUI Jetpack Compose Flutter React Native
コンポーネント tanrei-embedded-text-input TanreiEmbeddedTextInput TanreiEmbeddedTextInput TanreiEmbeddedTextInput EmbeddedTextInput
type · プロパティ type type type type type
value · プロパティ value text value controller value
label · プロパティ label label label semanticsLabel accessibilityLabel
description · プロパティ description hint stateDescription semanticsHint accessibilityHint
placeholder · プロパティ placeholder prompt placeholder hintText placeholder
multiline · プロパティ multiline axis singleLine multiline multiline
minRows · プロパティ minRows lineLimit minLines minLines minRows
maxRows · プロパティ maxRows lineLimit maxLines maxLines maxRows
enterKey · プロパティ enterKey enterKey enterKey enterKey enterKey
error · プロパティ error error isError isError error
required · プロパティ required required required required required
disabled · プロパティ disabled disabled enabled enabled disabled
readOnly · プロパティ readOnly readOnly readOnly readOnly readOnly
autocomplete · プロパティ autocomplete textContentType contentType autofillHints autoComplete
input · イベント input text onValueChange onChanged onChangeText
change · イベント change onCommit onCommit onCommit onEndEditing
action · イベント action onSubmit onAction onAction onAction
rowsChange · イベント rowschange onRowsChange onRowsChange onRowsChanged onRowsChange
focusChange · イベント focusin / focusout focused interactionSource focusNode onFocus / onBlur
備考 Class TanreiEmbeddedTextInput, React EmbeddedTextInput. A form-associated custom element with a shadow root and delegatesFocus (every point of it is its text) that renders one <textarea> while multiline and one <input> otherwise, with appearance: none, no border, no outline, no background, no padding and no resize handle; the host is display: block. Attributes are kebab-case (min-rows, max-rows, enter-key) except readonly and autocomplete, which keep HTML's names; name is the form's. The name, the description (aria-description), aria-invalid, aria-required and aria-readonly are written on the inner element; the host has no role and a debug build reports an aria-label set on it. It dispatches no tanrei-field-connect (ETI-13). Focus: the platform's focusin and focusout are composed and are the spec's focusChange; a holder styles itself with :focus-within — also from inside its own shadow root, :host(:focus-within) — and needs no script for H2. Height: the host's block size is the animated one (a transition of block-size between two measured heights, from scrollHeight of the inner element at block-size: auto), overflow: hidden during a motion whose end fits; the routine is shared with the Text input (src/primitives/auto-height.ts) and holds no token. rowschange is a composed CustomEvent with detail: { rows, scrolling }; the read-only property rows gives the number at any time, for a listener added after the first draw. action is composed and cancelable, detail: { value }. Keys (ETI-21): keydown leaves the shadow root before the input acts; a keydown whose default was prevented is not read by ETI-6. A composition is recognised by event.isComposing, by key code 229, and by a keydown that follows compositionend in the same task (ETI-4). An on-screen keyboard cannot be told from a hardware one by any web API: a keydown of Enter is read as the return key of an on-screen keyboard while the primary pointer is coarse and cannot hover ((pointer: coarse) and (hover: none)), so a tablet with a keyboard attached begins a new line with Enter and acts with Ctrl+Enter or Command+Enter — safe, and a guess. enterkeyhint is enter while multiline and the browser's own otherwise (search for type search). Selection (ETI-21): selectionStart, selectionEnd, setSelectionRange(), setRangeText() and select() are passed through to the inner element. Types set inputmode on a <textarea>, which has no type. Parts: input. Custom properties: --tanrei-comp-embedded-text-input-*. Without script (ETI-27): the product writes <textarea name aria-label> or <input> as the element's child; it is not slotted, and after definition it is hidden and disabled. Fixtures whose id begins held- are staged by the harness inside its sample holder; every other fixture is drawn bare on the stage. Not built on this platform yet. A TextField with .plain style and axis: .vertical (lineLimit(minRows...maxRows)), which draws no box; text and focused are bindings, and the holder reads focused (a FocusState it owns) for H2. The height is the view's own, so a holder laid out around it follows; the change is wrapped in an animation of the token's duration and curve, dropped when accessibilityReduceMotion is set. A hardware keyboard's Enter is told from the return key by onKeyPress(.return), which only a hardware keyboard sends; a composition by the marked text of the underlying text view. label is accessibilityLabel, description accessibilityHint. Not built on this platform yet. A BasicTextField with no decoration box. singleLine and enabled are the inverses of multiline and disabled. The holder gives a MutableInteractionSource and reads collectIsFocusedAsState() for H2. The height animates with animateContentSize given the token's duration and curve, skipped on the first composition and where the system's animator scale is 0. A hardware Enter is read in onPreviewKeyEvent; the on-screen keyboard's action is ImeAction.None while multiline; a composition is the composition range of the TextFieldValue. Not built on this platform yet. An EditableText with the placeholder painted by the widget, in no InputDecorator. enabled is the inverse of disabled. The holder gives a FocusNode and listens to it for H2. The height animates in an AnimatedSize with the token's duration and curve, with a zero duration on the first build and where MediaQuery.disableAnimations is true. A hardware Enter is read by the focus node's onKeyEvent; the on-screen action is TextInputAction.newline while multiline; a composition is controller.value.composing. Not built on this platform yet. The platform's TextInput with no border and no padding, its height set from onContentSizeChange and animated with the token's duration and curve (Animated, not the native driver: the height is layout), at once where AccessibilityInfo.isReduceMotionEnabled. The holder keeps a focused state from onFocus and onBlur for H2. This platform has no signal that a composition is in progress: until one is found, enterKey action is not offered here — Enter always begins a new line and action comes from Ctrl+Enter, Command+Enter and the holder's own button — because ETI-4 is not negotiable.

トークン

トークン 参照先 解決値(ライト)
--tanrei-comp-embedded-text-input-motion-resize-duration sys.motion.role.resize.default 300ms
--tanrei-comp-embedded-text-input-motion-resize-easing sys.motion.easing.spatial cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-embedded-text-input-selection-color sys.color.primary #1b1b1b
--tanrei-comp-embedded-text-input-selection-content-color sys.color.on-primary #ffffff
--tanrei-comp-embedded-text-input-standard-caret-color sys.color.primary #1b1b1b
--tanrei-comp-embedded-text-input-standard-disabled-caret-color — #1b1b1b · 38%
--tanrei-comp-embedded-text-input-standard-disabled-input-color — #1b1b1b · 38%
--tanrei-comp-embedded-text-input-standard-disabled-placeholder-color — #1b1b1b · 38%
--tanrei-comp-embedded-text-input-standard-input-color sys.color.on-surface #1b1b1b
--tanrei-comp-embedded-text-input-standard-placeholder-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-embedded-text-input-input-typescale sys.typescale.body-large 400 16px/1.625

フィクスチャ

基準となる描画パターンです。全プラットフォームが同じ ID でスナップショットを撮り、結果を比較できるようにしています。

生成元 specs/embedded-text-input.spec.json, tokens/comp/embedded-text-input.tokens.json, design/tokens/

Web での描き方の違い

  • 画面上のキーボードとハードウェアのキーボードは区別できません。主なポインターが粗く、ホバーできない環境では、Enter を改行キーとして扱い、改行します。そのため、キーボードを接続したタブレットでは、Ctrl+Enter か Command+Enter でアクションを実行します。
  • focusAt(x, y) は、ある位置にどの文字があるかをブラウザーが答えられる場合、最も近い文字にキャレットを置きます。答えられない場合、位置がテキストより上か手前ならテキストの先頭に、それ以外なら末尾に置きます。
  • フォームの中に直接置くと、フォームのコントロールの 1 つになり、値を name で送信します。Tanrei のコンポーネントであるホルダーの中ではフォームを持たず、値はホルダーが運びます。
  • スクリプトが動かない間は、子として書いた <textarea> か <input> がコントロールになります。要素が定義されると、その子のテキストを受け取り、子を隠します。

ガイドライン

埋め込みテキスト入力欄とテキスト入力欄

観点 埋め込みテキスト入力欄 テキスト入力欄
ボックス ホルダー自身のもので、ほかでは描かれません テキストフィールドのボックス。デザインシステムのすべての見た目とサイズがあります
フォーカス ホルダーが示します 入力欄自身が示します
ラベルとメッセージ ホルダーがテキストで渡します 自身のもの、またはフィールドのもの
種類 テキスト、検索、メール、URL、電話番号 左記に加えて、パスワード、数値、日付、時刻、コード

推奨

  • label を指定します。ほかに名前を付けるものはありません。
  • ヘルプ、エラーテキスト、文字数が伝える内容は、テキストとして description に入れます。
  • エラーはボックスに示します。無効な値として伝わるように、入力欄にも error を渡します。
  • 小さな画面の下端に置くホルダーでは、maxRows を指定します。
  • 文字は 16 px 以上にします。タッチ操作のブラウザーは、それより小さい文字にズームします。

非推奨

  • テキストフィールドのボックスが必要な場所では使いません。テキスト入力欄を使います。
  • 入力欄に 2 つ目のフォーカスリングを描きません。フォーカスはボックスが 1 回だけ示します。
  • ホルダーの高さを自分でアニメーションさせません。入力欄の高さに追従します。
  • プレースホルダーを名前の代わりにしません。
  • インプットメソッドが使う Enter で送信しません。それを除いた action を受け取ります。

アクセシビリティ

ロール
textbox
ラベル付け
The accessible name is label, which is required because nothing is drawn that could name the input and it asks no Field. The description is description: the holder puts there, as text, what its own help, error text or count says. The placeholder is never the name. Invalid, required, read-only and disabled are exposed from the props of those names; error and required draw nothing.
最小ターゲット
見た目のサイズが小さくても、ヒット領域は最小ターゲットサイズを満たします。 minimum 32px · pointer 44px · spacing 8px · touch 48px

キーボード操作

キー 動作
Tab, Shift+Tab Move focus to the input and out of it; never insert a character. The input is one stop.
Typing, Backspace, Delete, arrows, Home, End, selection and clipboard shortcuts Edit the value as the platform's own text element does. The holder sees each key first and may take one (ETI-21).
Enter (the action key) in a single-line input Commits the value and emits action; in a form of its own on the web, then submits the form (ETI-6).
Enter in a multi-line input Begins a new line (enterKey line-break), or emits action and inserts nothing (enterKey action). Never while an input method is composing (ETI-4).
Shift+Enter in a multi-line input Begins a new line.
Ctrl+Enter, Command+Enter in a multi-line input Emits action and inserts nothing, under both values of enterKey.
The return key of an on-screen keyboard in a multi-line input Begins a new line, always.
Escape Nothing: it is left to whatever holds the input.

備考

  • The holder's contract (H1 to H7). The input draws no box and no focus indication, so whatever holds it owes: H1 a visible box, or a place that reads as one; H2 a focus indication on that box for as long as the input has focus — however it was focused — that meets the contrast every Tanrei focus indicator is held to (WCAG 2.4.7, 2.4.13; on the web a rule on :focus-within, elsewhere the focusChange event); H3 the mark of an error and the look of a disabled box; H4 padding around the text, and a press on that padding passed on to the input (ETI-12); H5 the name, as label, and what its messages say, as description; H6 what action means; H7 in forced colors, an edge for the box and the focus indication in the platform's colors. A Tanrei component built from the input states each of these as sentences of its own and is tested there; a product's own box is the product's.
  • The caret is not accepted as the focus indication. It is one line a text style wide, it is not drawn while a selection is shown, and it does not say which box on the screen is the one being typed in. A holder that forgets H2 fails WCAG 2.4.7.
  • Text entry happens in the platform's native element, so input methods, autofill, dictation, selection and the platform's own text services work as they do everywhere. A composition's Enter never becomes an action (ETI-4): this is a functional requirement for every language written through candidates.
  • A height that follows the text changes with a short motion; where motion is reduced it changes at once (WCAG 2.3.3). The motion is the height's alone: text never slides, fades or scales.
  • At 200 % text in a width of 320 a multi-line input shows every character and scrolls in no direction but its own vertical one beyond maxRows (WCAG 1.4.4, 1.4.10). maxRows counts lines, so a holder at the bottom of a small screen should set one that leaves the conversation in view at that size.
  • Web: a touch browser zooms the page into a text element whose text is smaller than 16 px. The default text style is 16 px; a layer that sets a smaller one brings the zoom back.

生成元 specs/embedded-text-input.spec.json, tokens/sys/measure.tokens.json

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

  • テキストボックスです。multiline が true の間は複数行のテキストボックスです。名前は label、説明は description です。
  • 無効な値、必須、読み取り専用、無効の各状態は、それぞれの名前のプロパティから支援技術に伝わります。フィールドと同じ言い方の required も、required と同じく必須にします。
  • プレースホルダーが名前になることはありません。
  • Tab キーで止まる位置は 1 つです。Tab で文字が入ることはなく、Escape はホルダーに任せます。
  • インプットメソッドの Enter で action が発行されることはありません。
  • 反応範囲は、行の上下で最小ターゲットを満たします。大きさは変わりません。
  • 文字を 200% にしても、複数行の入力欄は折り返してすべての文字を表示します。横にはスクロールしません。
  • 強制カラーでは、テキスト、プレースホルダー、選択範囲、キャレットがシステムの色になります。
  • どの言語でも、自身の言葉を持ちません。

Web での既知の課題:

  • インプットメソッドは、Chromium では実際に動かして確かめ、Firefox と Safari ではイベントだけで確かめています。
  • 画面上のキーボードかどうかは、主なポインターが粗く、ホバーできないことから推測します。この推測は、キーボードを接続したタブレットでは外れます。

利用者側で行うこと

  • ボックスを描き、入力欄にフォーカスがある間、ボックスにフォーカスを示します。表示のコントラストは、背景に対して 3:1 以上を保ちます。
  • 入力欄の label には、ユーザーがそのボックスを呼ぶときの言葉を指定します。
  • エラーはボックスのそばに言葉で伝え、その言葉を description にも渡します。
  • 強制カラーでは、ボックスの縁をシステムのテキストの色で、フォーカスの表示をハイライトの色で描きます。