Caroa UI

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構成)

Fieldlabel / required / error / description のような単一propsは受け取りません。 FieldLabel / FieldControl / FieldDescription / FieldError を子要素として合成します。

FieldReact.useId() ベースの内部contextを持ち、以下を自動配線します(明示指定があればそちらを優先):

サブコンポーネント自動配線される内容
FieldLabelhtmlForFieldControlのidと一致)
FieldControlidaria-describedbyFieldDescriptionFieldErrorのidを参照)
FieldDescriptionid
FieldErroridchildren が空の場合は描画されないFormMessageと同様のガード)

FieldControl はRadixの Slot を使った合成コンポーネントです。Input / Textarea / CommandCombobox など、単一のフォームコントロール要素を子に1つ渡してください。

基本的な使い方

Loading...
<Field>
  <FieldLabel>メールアドレス</FieldLabel>
  <FieldControl>
    <Input type="email" placeholder="[email protected]" />
  </FieldControl>
</Field>

必須マーク付き

required propは無いため、FieldLabel の中に直接マークを書きます。

Loading...
<Field>
  <FieldLabel>
    名前 <span className="text-destructive">*</span>
  </FieldLabel>
  <FieldControl>
    <Input placeholder="山田太郎" />
  </FieldControl>
</Field>

エラー状態

FieldError を子要素として置くだけで、FieldControlaria-describedby に自動的に紐付きます。 実際に赤枠を出すには Input 側に aria-invalid を渡してください(FieldControl はaria-invalidを自動判定しません)。

Loading...
<Field>
  <FieldLabel>パスワード</FieldLabel>
  <FieldControl>
    <Input type="password" aria-invalid={!!errorMessage} />
  </FieldControl>
  <FieldError>{errorMessage}</FieldError>
</Field>

ヘルプテキスト付き

Loading...
<Field>
  <FieldLabel>ユーザー名</FieldLabel>
  <FieldControl>
    <Input placeholder="username" />
  </FieldControl>
  <FieldDescription>3文字以上で入力してください</FieldDescription>
</Field>

horizontal レイアウト

orientation="horizontal"FieldContent(ラベル+説明のグループ)を組み合わせます。

Loading...
<Field orientation="horizontal">
  <FieldContent>
    <FieldLabel>通知を受け取る</FieldLabel>
    <FieldDescription>メールで通知します</FieldDescription>
  </FieldContent>
  <FieldControl>
    <Switch />
  </FieldControl>
</Field>

FieldGroup / FieldSet でまとめる

Loading...
<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 ではなく FormFormItem / 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を持つ場合

FieldControlSlot@radix-ui/react-slot)合成のため、子要素側が独自の id を持っている場合はそちらがDOMへ反映されます(Radixの仕様上、通常propsは子側の値が優先されます)。FieldControl はこの子要素のidを自動検出し、FieldLabelhtmlFor にも同じ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 / 初回描画時の制約

FieldLabelhtmlFor は、FieldControl の存在・idをReactの useEffect を通じて検知しています。useEffect はマウント後(クライアント側のコミット後)にしか実行されないため、FieldLabelFieldControl より先に描画する一般的な順序(Label→Control)では、サーバーサイドレンダリング(SSR)の出力・クライアント側の初回コミット時点では htmlFor が付与されません。クライアント側で1回再レンダーされた後(通常は次のフレーム内)に付与されます。

  • 影響: SSR出力やハイドレーション前の一瞬だけ、ラベルクリックで対応する入力欄へフォーカスが移動しない・スクリーンリーダーがラベルと入力欄の関連付けを認識しない、という状態が起こり得ます
  • htmlFor / id を明示指定した場合(上記「明示指定での上書き」参照)はこの制約を受けません。アクセシビリティを初回描画から保証したい場合は htmlFor / id を明示的に指定してください
  • 自動配線(明示指定なし)は、通常のクライアントサイド操作では実用上問題になりませんが、SSR結果をそのまま検証するテストやLighthouse等の初回描画時点でのa11y監査では検出される場合があります

Props

Field

PropTypeDefaultDescription
orientation'vertical' | 'horizontal''vertical'レイアウト方向
idstring-内部contextのベースid。未指定時はReact.useId()で自動生成される

FieldLabel

PropTypeDefaultDescription
htmlForstring-関連付けるコントロールのid。未指定時はFieldContextから自動配線される

FieldControl

Slot合成コンポーネント(asChild前提)。単一の子要素(InputTextareaCommandCombobox等)に id / aria-describedby を配線します。

PropTypeDefaultDescription
idstring-子要素のid。未指定時はFieldContextから自動配線される

FieldDescription / FieldError

PropTypeDefaultDescription
idstring-未指定時はFieldContextから自動配線される
childrenReactNode-FieldErrorはchildrenが空(null/undefined/空文字)の場合、何も描画しない

FieldSet / FieldLegend / FieldGroup / FieldContent

見た目とセマンティクスのみを提供するラッパーです。固有のPropsはありません(className 以外は標準のHTML属性をそのまま受け取ります)。