Field
ラベル・説明・誤りを、入力ひとつにまとめる
ラベルと入力の関連付けは Field が作ります
ラベルと入力、説明と入力、誤りと入力。どれも忘れると読み上げだけが黙ります。 見た目は正常なので、書き忘れても気づけません。
Field に id を1つ渡せば、ラベル・説明・誤りの関連付けは全部こちらで作ります。
<Field id="mail" label="メールアドレス" description="仕事用のもの" error={errors.mail} required>
<Input type="email" />
</Field>利用側が渡すのは id を1つだけです。説明と誤りの id はそこから導きます。
id は必須です
自動で作る道は取っていません。安定した名前を作るには useId が要り、
このコンポーネントがクライアント側になるためです。
必須にしたことで、書き忘れが型で止まります。
バリデーションは持ちません
規則を走らせる仕組みはありません。 誤りの文言を受け取るだけです。
受け取るのは標準の props だけなので、どのライブラリとも組めます——
react-hook-form でも TanStack Form でも
Conform でも、素の FormData でも同じです。
{/* react-hook-form の例 */}
<Field id="mail" label="メール" error={formState.errors.mail?.message}>
<Input {...register('mail')} />
</Field>実際に動かせます
下は react-hook-form で検証が走る本物のフォームです。 打っている最中に検証が走ります——形が整うと線が緑に変わり、印が出ます。
Field は react-hook-form に依存していません。 このデモが使っているだけで、
受け取るのは標準の props だけです。register() が返すものをそのまま渡せます。
入力側で同じ属性を書くとエラーになります
id / aria-describedby / aria-invalid は Field が渡します。
入力側で書くとField が渡した値が上書きされ、関連付けだけが切れます——
見た目は正常なままなので気づけません。だからエラーにしています。
register() が返す name / onChange / onBlur / ref は重なりません。
入力が正しいことも示せます
<Field id="mail" label="メール" valid>
<Input />
</Field>境界が変わり、入力の右端に印が出ます。印は読み上げから隠れています—— 出しても「チェック」としか言わないので、状態は境界が伝えます。
誤りと同時には使えません。両方は成り立たないので、誤りが勝ちます。
空の段落は置きません
説明も誤りも無いときは、何も描きません。 置くと、 何も書かれていない行が読み上げに出ます。
必須であることを記号だけで伝えません
* は読み上げに届かないので、文字も一緒に置いています。
3つの状態
通常・空・エッジケースの3つは、どのコンポーネントにも必ずあります。
通常default.tsx
通常。結びつけを利用側に書かせない。
他の人に見える名前です
空empty.tsx
空。説明も誤りも無いとき。
エッジケースedge.tsx
エッジケース。誤りと説明が両方あるとき、必須のとき。
仕事用のものを入れてください
この形では受け取れません
印と境界で表します
何行でも構いません
props
Prop
Type
型と説明はコンポーネントのソースから抽出しています。既定値は出していません——cva の defaultVariants は 署名に現れないので、表に出すと「引数のデフォルトが書かれているものだけ」になり、嘘になります。