Field
ラベル・入力・説明・エラーメッセージを合成するフィールドコンポーネント群。id・htmlFor・aria-describedbyを自動配線する。
インポート
import {
Field,
FieldLabel,
FieldControl,
FieldDescription,
FieldError,
FieldGroup,
FieldSet,
FieldLegend,
FieldContent,
} from '@caroainc/ui-components/client'個別サブパス(バンドルサイズを抑えたい場合):
import {
Field,
FieldLabel,
FieldControl,
FieldDescription,
FieldError,
FieldGroup,
FieldSet,
FieldLegend,
FieldContent,
} from '@caroainc/ui-components/field'合成API(実際のProps構成)
Field は label / required / error / description のような単一propsは受け取りません。
FieldLabel / FieldControl / FieldDescription / FieldError を子要素として合成します。
Field は React.useId() ベースの内部contextを持ち、以下を自動配線します(明示指定があればそちらを優先):
| サブコンポーネント | 自動配線される内容 |
|---|---|
FieldLabel | htmlFor(FieldControlのidと一致) |
FieldControl | id・aria-describedby(FieldDescription・FieldErrorのidを参照) |
FieldDescription | id |
FieldError | id。children が空の場合は描画されない(FormMessageと同様のガード) |
FieldControl はRadixの Slot を使った合成コンポーネントです。Input / Textarea / CommandCombobox など、単一のフォームコントロール要素を子に1つ渡してください。
基本的な使い方
<Field>
<FieldLabel>メールアドレス</FieldLabel>
<FieldControl>
<Input type="email" placeholder="[email protected]" />
</FieldControl>
</Field>必須マーク付き
required propは無いため、FieldLabel の中に直接マークを書きます。
<Field>
<FieldLabel>
名前 <span className="text-destructive">*</span>
</FieldLabel>
<FieldControl>
<Input placeholder="山田太郎" />
</FieldControl>
</Field>エラー状態
FieldError を子要素として置くだけで、FieldControl の aria-describedby に自動的に紐付きます。
実際に赤枠を出すには Input 側に aria-invalid を渡してください(FieldControl はaria-invalidを自動判定しません)。
<Field>
<FieldLabel>パスワード</FieldLabel>
<FieldControl>
<Input type="password" aria-invalid={!!errorMessage} />
</FieldControl>
<FieldError>{errorMessage}</FieldError>
</Field>ヘルプテキスト付き
<Field>
<FieldLabel>ユーザー名</FieldLabel>
<FieldControl>
<Input placeholder="username" />
</FieldControl>
<FieldDescription>3文字以上で入力してください</FieldDescription>
</Field>horizontal レイアウト
orientation="horizontal" と FieldContent(ラベル+説明のグループ)を組み合わせます。
<Field orientation="horizontal">
<FieldContent>
<FieldLabel>通知を受け取る</FieldLabel>
<FieldDescription>メールで通知します</FieldDescription>
</FieldContent>
<FieldControl>
<Switch />
</FieldControl>
</Field>FieldGroup / FieldSet でまとめる
<FieldSet>
<FieldLegend>アカウント情報</FieldLegend>
<FieldGroup>
<Field>
<FieldLabel>メールアドレス</FieldLabel>
<FieldControl>
<Input type="email" placeholder="[email protected]" />
</FieldControl>
</Field>
<Field>
<FieldLabel>パスワード</FieldLabel>
<FieldControl>
<Input type="password" />
</FieldControl>
<FieldDescription>8文字以上で入力してください</FieldDescription>
</Field>
<Button className="w-full">登録</Button>
</FieldGroup>
</FieldSet>react-hook-formと組み合わせる場合
react-hook-formを使う場合は Field ではなく Form(FormItem / FormLabel / FormControl / FormDescription / FormMessage)を使用してください。Field はreact-hook-form非依存の軽量な合成コンポーネントです。
明示指定での上書き
htmlFor / id を明示的に渡すと自動配線より優先されます。複数のコントロールを1つの Field に置く場合など、自動採番では対応できないケースで使用します。
<Field>
<FieldLabel htmlFor="custom-email">メールアドレス</FieldLabel>
<FieldControl id="custom-email">
<Input type="email" />
</FieldControl>
</Field>FieldControlの子要素が独自idを持つ場合
FieldControl は Slot(@radix-ui/react-slot)合成のため、子要素側が独自の id を持っている場合はそちらがDOMへ反映されます(Radixの仕様上、通常propsは子側の値が優先されます)。FieldControl はこの子要素のidを自動検出し、FieldLabel の htmlFor にも同じidを配線するため、以下のように書いても食い違いは起きません。
<Field>
<FieldLabel>メールアドレス</FieldLabel>
<FieldControl>
{/* 子が独自のidを持っていても、FieldLabelのhtmlForは自動的にこのidを参照する */}
<Input id="email" />
</FieldControl>
</Field>ただし FieldControl に渡した id prop(<FieldControl id="...">)は、子要素が独自の id を持つ場合はDOMに反映されません(子のidが優先されるため)。両方に異なるidを指定すると意図が伝わりにくいので、子要素にidを渡すか、FieldControl にidを渡すかのどちらか一方に統一することを推奨します。
SSR / 初回描画時の制約
FieldLabel の htmlFor は、FieldControl の存在・idをReactの useEffect を通じて検知しています。useEffect はマウント後(クライアント側のコミット後)にしか実行されないため、FieldLabel を FieldControl より先に描画する一般的な順序(Label→Control)では、サーバーサイドレンダリング(SSR)の出力・クライアント側の初回コミット時点では htmlFor が付与されません。クライアント側で1回再レンダーされた後(通常は次のフレーム内)に付与されます。
- 影響: SSR出力やハイドレーション前の一瞬だけ、ラベルクリックで対応する入力欄へフォーカスが移動しない・スクリーンリーダーがラベルと入力欄の関連付けを認識しない、という状態が起こり得ます
htmlFor/idを明示指定した場合(上記「明示指定での上書き」参照)はこの制約を受けません。アクセシビリティを初回描画から保証したい場合はhtmlFor/idを明示的に指定してください- 自動配線(明示指定なし)は、通常のクライアントサイド操作では実用上問題になりませんが、SSR結果をそのまま検証するテストやLighthouse等の初回描画時点でのa11y監査では検出される場合があります
Props
Field
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'vertical' | 'horizontal' | 'vertical' | レイアウト方向 |
id | string | - | 内部contextのベースid。未指定時はReact.useId()で自動生成される |
FieldLabel
| Prop | Type | Default | Description |
|---|---|---|---|
htmlFor | string | - | 関連付けるコントロールのid。未指定時はFieldContextから自動配線される |
FieldControl
Slot合成コンポーネント(asChild前提)。単一の子要素(Input・Textarea・CommandCombobox等)に id / aria-describedby を配線します。
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | - | 子要素のid。未指定時はFieldContextから自動配線される |
FieldDescription / FieldError
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | - | 未指定時はFieldContextから自動配線される |
children | ReactNode | - | FieldErrorはchildrenが空(null/undefined/空文字)の場合、何も描画しない |
FieldSet / FieldLegend / FieldGroup / FieldContent
見た目とセマンティクスのみを提供するラッパーです。固有のPropsはありません(className 以外は標準のHTML属性をそのまま受け取ります)。