⌨️

TextFieldについて - React Ariaの実装読むぞ

2024/12/04に公開

こんにちは、フロントエンドエンジニアの mehm8128 です。
今日は TextField について書いていきます。

https://react-spectrum.adobe.com/react-aria/useTextField.html

使用例

ドキュメントからそのまま取ってきています。

function TextField(props: AriaTextFieldProps) {
  let { label } = props;
  let ref = React.useRef(null);
  let {
    labelProps,
    inputProps,
    descriptionProps,
    errorMessageProps,
    isInvalid,
    validationErrors,
  } = useTextField(props, ref);

  return (
    <div style={{ display: "flex", flexDirection: "column", width: 200 }}>
      <label {...labelProps}>{label}</label>
      <input {...inputProps} ref={ref} />
      {props.description && (
        <div {...descriptionProps} style={{ fontSize: 12 }}>
          {props.description}
        </div>
      )}
      {isInvalid && (
        <div {...errorMessageProps} style={{ color: "red", fontSize: 12 }}>
          {validationErrors.join(" ")}
        </div>
      )}
    </div>
  );
}

本題

WAI-ARIA はこちらです。

https://www.w3.org/TR/wai-aria-1.2/#textbox

フィールドの a11y とバリデーション

React Aria ではフィールド系のコンポーネントにはラベルに加えて説明文とエラーメッセージを紐づけることができます。
説明文を入れている要素にはdescriptionPropsを、エラーメッセージを入れている要素にはerrorMessagePropsを渡すことで、useFieldによって生成されている id を用いてaria-describedbyでテキストフィールドに紐づけることができます。
また、 React Hook Form などのライブラリと一緒に使うこともでき、そのバリデーション結果のエラーメッセージを、errorMessagePropsを渡している要素に入れることで紐づけます。

https://react-spectrum.adobe.com/react-aria/forms.html

エラーメッセージにはaria-errormessageが使われることもありますが、aria-errormessageは現状スクリーンリーダーによっては上手く読み上げられないことがあるため、用いられていないようです。

https://github.com/adobe/react-spectrum/issues/7425

https://a11ysupport.io/tech/aria/aria-errormessage_attribute

aria-multiline

WAI-ARIA を見てみます。

https://www.w3.org/TR/wai-aria-1.2/#textbox

NOTE の欄には、1 行のテキストフィールドであるinput要素は Enter キーを押すとデフォルトではフォームが送信されるけど、複数行であるtextarea要素は改行されるだけなので、それを区別するためにaria-multilineがあるという話が書かれています。
aria-multilinetrueにするとスクリーンリーダーでは「複数行」という読み上げがされるので、それによって Enter を押したときにすぐに送信されてしまうか、改行されるだけかという判断がつく、という話だと理解しました。
ただ、複数行のときにはちゃんとtextarea要素を使っていれば自動でaria-multiline="true"の挙動になってくれるので、RTE を触るときなどに気にすることになりそうです。React Aria のuseTextfieldでは下に載せたコードの部分でinputtextarea要素のみを受け入れるようにしているので、実装に明示的に含まれてはいませんでした。

https://github.com/adobe/react-spectrum/blob/93c26d8bd2dfe48a815f08c58925a977b94d6fdd/packages/%40react-aria/textfield/src/useTextField.ts#L50

inputMode

input要素にはtype属性があり、単純な TextField ならtype="text"、チェックボックスならtype="checkbox"、数値ならtype="number"など、用途に応じて指定することで入力フィールドの見た目や機能が変わったり、モバイル時に仮想キーボードが変化したりします。例えばtype="number"だと、数字(と一部の記号)だけの仮想キーボードになります。スマホでロック画面解除のパスワードを入力するような画面を想像してもらうと分かりやすいと思います。

しかし、例えば郵便番号は数字だから数字を入力しやすいようにtype="number"にしよう、というのは間違いです。デフォルトでtype="number"には+/-ボタンのスピナーがつくのですが、郵便番号は数字を増減させて操作するタイプのものではないからです。

こういう場合に、type属性ではなくてinputModeを利用します。
inputModeは、入力フィールドの見た目や機能を変えずに、仮想キーボードのみを変化させるためのものです。例えばtype="number"のときに表示される仮想キーボードと同じものを表示させるには、inputMode="numeric"を指定します。つまり、郵便番号の場合にはtype="text"inputMode="numeric"を指定するのが適切だと考えられます。
それ以外にもtype="email"のときに出てくる仮想キーボードはinputMode="email"で表示できるなど、いくつかの種類があります。詳しくは下のリンクから HTML Standard をご覧ください。
ちなみにinputModeは明日の記事でも登場します。

https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute

まとめ

明日の担当は @mehm8128 さんで、 NumberField についての記事です。お楽しみにー

Discussion