アクセシビリティ
フォームをアクセシブルにするには、各フォーム要素を適切な属性で設定する必要がありますが、 Conform がその手助けをします。
#Aria 属性
アクセシビリティに関しては、通常、異なる要素を関連付けるために一意の ID が必要になる Aria 属性が最初に思い浮かびます。Conform は、必要なすべての ID を生成してくれることで、この点でのサポートを提供します。
1import { useForm } from '@conform-to/react';
2
3function Example() {
4 const [form, fields] = useForm();
5
6 return (
7 <form id={form.id}>
8 <label htmlFor={fields.message.id}>Message</label>
9 <input
10 type="text"
11 id={fields.message.id}
12 name={fields.message.name}
13 aria-invalid={!fields.message.valid ? true : undefined}
14 aria-describedby={
15 !fields.message.valid
16 ? `${fields.message.errorId} ${fields.message.descriptionId}`
17 : fields.message.descriptionId
18 }
19 />
20 <div id={fields.message.descriptionId}>The message you want to send</div>
21 <div id={fields.message.errorId}>{fields.message.errors}</div>
22 <button>Send</button>
23 </form>
24 );
25}
#バリデーション属性
バリデーション属性も、スクリーンリーダーのヒントを改善するなど、アクセシビリティにおいて重要な役割を果たします。 Conform を使用すると、 zod や yup スキーマからバリデーション属性を導出し、各フィールドのメタデータにそれらを反映させることができます。
1import { parseWithZod, getZodConstraint } from '@conform-to/zod';
2import { useForm } from '@conform-to/react';
3import { z } from 'zod';
4
5const schema = z.object({
6 message: z
7 .string()
8 .min(10)
9 .max(100)
10 .regex(/^[A-Za-z0-9 ]{10-100}$/),
11});
12
13function Example() {
14 const [form, fields] = useForm({
15 constraint: getZodConstraint(schema),
16 onValidate({ formData }) {
17 return parseWithZod(formData, { schema });
18 },
19 });
20
21 return (
22 <form id={form.id}>
23 <input
24 type="text"
25 name={fields.message.name}
26 required={fields.message.required}
27 minLength={fields.message.minLength}
28 maxLength={fields.message.maxLength}
29 pattern={fields.message.pattern}
30 />
31 <button>Send</button>
32 </form>
33 );
34}
#プログレッシブエンハンスメント
プログレッシブエンハンスメントも、一時的なネットワークの問題の影響を最小限に抑えるなど、アクセシビリティを支援します。たとえば、 Conform を使用すると、ページのリフレッシュをまたいでもフォームデータと状態が保持されるように、フィールドリストを操作できます。
1import { useForm } from '@conform-to/react';
2
3export default function Example() {
4 const [form, fields] = useForm();
5
6 return (
7 <form id={form.id}>
8 <ul>
9 {tasks.map((task) => (
10 <li key={task.key}>
11 <input name={task.name} defaultValue={task.initialValue} />
12 <button
13 {...form.remove.getButtonProps({
14 name: fields.tasks.name,
15 index,
16 })}
17 >
18 Delete
19 </button>
20 </li>
21 ))}
22 </ul>
23 <button
24 {...form.insert.getButtonProps({
25 name: fields.tasks.name,
26 })}
27 >
28 Add task
29 </button>
30 <button>Save</button>
31 </form>
32 );
33}
#ボイラープレートの削減
上記で述べたすべての属性を設定することは、面倒でエラーが発生しやすい作業です。 Conform は 、関連するすべての属性を導出する一連のヘルパーを提供することで、この作業を支援しようとしています。
注意: これらすべてのヘルパーはネイティブ HTML 要素用に設計されています。 react-aria-components や Radix UI のようなカスタム UI コンポーネントを使用している場合、それらの API を通じて既に属性が設定されている可能性があるため、これらのヘルパーが不要になるかもしれません。
以下は、手動設定と比較した場合の例です。ヘルパーについて詳しく知りたい場合は、上記リンクの対応するドキュメントを確認してください。
1import { parseWithZod, getZodConstraint } from '@conform-to/zod';
2import { useForm } from '@conform-to/react';
3import { z } from 'zod';
4
5const schema = z.object({
6 message: z
7 .string()
8 .min(10)
9 .max(100)
10 .regex(/^[A-Za-z0-9 ]{10-100}$/),
11});
12
13function Example() {
14 const [form, fields] = useForm({
15 constraint: getZodConstraint(schema),
16 onValidate({ formData }) {
17 return parseWithZod(formData, { schema });
18 },
19 });
20
21 return (
22 <form id={form.id}>
23 {/* ビフォー */}
24 <input
25 type="text"
26 id={fields.message.id}
27 name={fields.message.name}
28 required={fields.message.required}
29 minLength={fields.message.minLength}
30 maxLength={fields.message.maxLength}
31 pattern={fields.message.pattern}
32 aria-invalid={!fields.message.valid ? true : undefined}
33 aria-describedby={
34 !fields.message.valid
35 ? `${fields.message.errorId} ${fields.message.descriptionId}`
36 : fields.message.descriptionId
37 }
38 />
39 {/* アフター */}
40 <input
41 {...getInputProps(fields.message, {
42 type: 'text',
43 ariaDescribedBy: fields.message.descriptionId,
44 })}
45 />
46 <button>Send</button>
47 </form>
48 );
49}