最高のユーザー体験を! React でアクセシブルで使いやすいフォームを作成する方法
Webアプリケーションを設計する際、アクセシビリティとユーザーエクスペリエンスは非常に重要な要素です。この記事では、React Hook Formライブラリ、HTML属性、および開発上の考慮事項について学び、すべてのユーザーが利用できるサイトを作成する方法に焦点を当てます。
最高のユーザー体験を! React でアクセシブルで使いやすいフォームを作成する方法
Webアプリケーションを設計する際、アクセシビリティとユーザーエクスペリエンスは非常に重要な要素です。この記事では、React Hook Formライブラリ、HTML属性、および開発上の考慮事項について学び、すべてのユーザーが利用できるサイトを作成する方法に焦点を当てます。
どんな人に役立つ?
この記事は、以下のユーザーに特に役立ちます。
- 視覚障碍者で、スクリーンリーダーを使用するユーザー
- より良いユーザーフィードバックを求めるユーザー
- すべての人にわかりやすい視覚的な手がかりを必要とするユーザー
- すべてのユーザーのためのデザイン上の考慮事項に関心があるユーザー
前提条件
この記事を読む前に、以下の知識があることを推奨します。
- Reactの基礎知識
- TypeScriptおよびHTML / JSXの記述経験
- Tailwind CSSの理解(必須ではない)
目次
- 初期の基本フォームの問題点
- React-Hook-Formによるエラー処理
- useFormメソッドをフォームに組み込む
- エラーメッセージを表示する
- aria-required属性を追加する
- fieldsetとlegend要素を追加する
- ラベルとhtmlFor属性を使用する
- プレースホルダーだけに頼らない!
- aria-describedByで追加情報を提供する
- 重要な情報にツールチップを使用しない
- 重要なことを知らせる
- フォーカス状態と色分け
- ボタンをわかりやすくする
- 最後に:フォームのアクセシビリティ向上 まとめ
1. 初期の基本フォームの問題点
単純に見えるフォームでも、アクセシビリティやユーザーエクスペリエンスに問題がある場合があります。
import { TvIcon } from "@heroicons/react/24/outline";
type FormData = {
fullName: string;
email: string;
password: string;
confirmPassword: string;
agreeToTerms: boolean;
};
export const RegistrationForm = () => {
const onSubmit = () => {
alert(`Form submitted`);
};
return (
<div className="flex justify-center items-center w-screen h-screen bg-gray-900">
<div className="w-full max-w-md p-8 bg-black bg-opacity-75 rounded-lg">
<div className="flex flex-row justify-center items-center gap-x-4">
<TvIcon className="h-12 w-12 text-white" />
<h1 className="text-7xl font-bold text-center text-red-600 mb-4">Getflix</h1>
</div>
<h2 className="text-3xl font-bold text-white mb-6 text-center">
Sign Up
</h2>
<form onSubmit={onSubmit} className="space-y-6">
{/* Full Name */}
<div>
<input
type="text"
placeholder="Full Name"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "
/>
</div>
{/* Email */}
<div>
<input
type="email"
placeholder="Email Address"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "
/>
</div>
{/* Password */}
<div>
<input
type="password"
placeholder="Password"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400"
/>
</div>
{/* Confirm Password */}
<div>
<input
type="password"
placeholder="Confirm Password"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "
/>
</div>
{/* Agree to Terms */}
<div className="flex items-center text-gray-400 text-sm">
<input
type="checkbox"
id="agreeToTerms"
className="mr-2"
/>
<label htmlFor="agreeToTerms" className="select-none">
I agree to the Terms and Conditions
</label>
</div>
{/* Submit */}
<button
type="submit"
className="w-full py-3 bg-red-600 hover:bg-red-700 text-white rounded font-semibold transition"
>
Sign Up
</button>
</form>
</div>
</div>
);
};
問題点:アクセシビリティを考慮したReactフォームの改善点
- アクションフィードバックの欠如: フォーム送信後にユーザーにフィードバックがないため、操作が正常に完了したかどうか混乱する可能性があります。エラーメッセージやフィードバックがないと、ユーザーはフォームを修正するために何をすべきか分かりません。
- フォーム入力に対するラベルの欠如: フォーム入力にラベルがないと、スクリーンリーダーはその目的を理解できません。一部のスクリーンリーダーではプレースホルダーが見逃されたり、ユーザーが入力するとコンテキストが失われたり、エラーのある入力に戻るのが難しくなります。
- アクセシビリティマークアップの欠如: スクリーンリーダーやアクセシビリティツール向けにフォームが最適化されていません。
2. React-Hook-Formによるエラー処理
フォームのエラー処理は、ユーザーエクスペリエンスを向上させるために不可欠です。Reactでフォームを扱うための人気のあるライブラリは、react-hook-formです。
npm install react-hook-form
// フォーム内で使用する型構造を定義します
type FormData = {
fullName: string;
email: string;
password: string;
confirmPassword: string;
agreeToTerms: boolean;
};
// `useForm()`の基本的な使い方
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm<FormData>()
React Hook Form: useForm()フック 徹底解説:handleSubmit、Register、Watch
- register: React Hook Formの中核概念は、コンポーネント/ HTML要素を「登録」することです。これにより、フォームの検証とフォームの送信時に要素の値にアクセスできます。
- handleSubmit: フォームを送信し、検証やその他の設定されたチェックを実行するために必要な重要な関数です。
handleSubmit(onSuccess)
– フォームの送信が有効で、正常に送信できる場合に呼び出されます。handleSubmit(onSuccess, onFail)
– handleSubmit()メソッドに2つの関数を渡すことができます。1つ目はReact Hook Formがフォームを有効と判断したときにrunされ、続行できます。2つ目はフォームがエラーを検出したときに呼び出されます。これは検証、または別の規定によって発生する可能性があります。
- watch: 変更を監視し、その値を返す関数。
- formState: フォームに関する情報を保持するオブジェクト。その状態を追跡します。
isDirty
– ユーザーが何らかの入力を変更した場合にtrue。isValid
– フォームがすべての検証に合格した場合にtrue。errors
– フィールドごとの検証エラーを保持するオブジェクト。isSubmitting
– フォームが送信中の場合にtrue(ローディングスピナーを表示するのに役立ちます)isSubmitted
– フォームが送信された後にtrue。touchedFields
– ユーザーが操作したフィールド。dirtyFields
– ユーザーが変更したフィールド。
3. useFormメソッドをフォームに組み込む
useForm()メソッドを既存の<form/>
要素に統合することで、React Hook Formのすべての機能を使用できます。
import { TvIcon } from "@heroicons/react/24/outline";
import { useState } from "react";
import { useForm } from "react-hook-form";
type FormData = {
fullName: string;
email: string;
password: string;
confirmPassword: string;
agreeToTerms: boolean;
};
export const RegistrationForm = () => {
const {
register,
handleSubmit,
formState: { errors },
watch,
} = useForm < FormData > ();
const onSubmit = () => {
alert(`Form submitted`);
};
return (
<div className="flex justify-center items-center w-screen h-screen bg-gray-900">
<div className="w-full max-w-md p-8 bg-black bg-opacity-75 rounded-lg">
<div className="flex flex-row justify-center items-center gap-x-4">
<TvIcon className="h-12 w-12 text-red-500" />
<h1 className="text-7xl font-bold text-center text-white mb-4"> Getflix </h1>
</div>
<h2 className="text-3xl font-bold text-white mb-6 text-center">
Sign Up
</h2>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
{/* Full Name */}
<div>
<input
{...register("fullName", {
required: "Full Name is required"
})}
aria-required
type="text"
placeholder="Full name"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"
/>
{errors.fullName && (
<p className="text-red-500 text-sm mt-1"> {errors.fullName.message} </p>
)}
</div>
{/* Email */}
<div>
<input
{...register("email", {
required: "Email is required",
pattern: {
value: /^\S+@\S+$/i,
message: "Invalid email address",
},
})}
type="email"
placeholder="Email Address"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"
/>
{errors.email && (
<p className="text-red-500 text-sm mt-1"> {errors.email.message} </p>
)}
</div>
{/* Password */}
<div>
<input
{...register("password", {
required: "Please enter your password"
})}
type="password"
placeholder="Password"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"
/>
{errors.password && (
<p className="text-red-500 text-sm mt-1"> {errors.password.message} </p>
)}
</div>
{/* Confirm Password */}
<div>
<input
{...register("confirmPassword", {
required: "Please enter your password",
validate: (value) =>
value === watch("password") || "Passwords do not match",
})}
type="password"
placeholder="Confirm Password"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"
/>
{errors.confirmPassword && (
<p className="text-red-500 text-sm mt-1"> {errors.confirmPassword.message} </p>
)}
</div>
{/* Agree to Terms */}
<div className="flex items-center text-gray-400 text-sm">
<input
{...register("agreeToTerms", {
required: "You must agree to the terms and conditions"
})}
type="checkbox"
id="agreeToTerms"
className="mr-2"
/>
<label className="select-none">
I agree to the Terms and Conditions
</label>
</div>
{errors.agreeToTerms && (
<p className="text-red-500 text-sm mt-1"> {errors.agreeToTerms.message} </p>
)}
{/* Submit */}
<button
type="submit"
className="w-full py-3 bg-red-600 hover:bg-red-700 text-white rounded font-semibold transition"
>
Sign Up
</button>
{/* Already have account */}
<p className="text-center text-gray-400 text-sm mt-4">
Already have an account?{" "}
<a href="#" className="text-red-500 hover:underline">
Sign In
</a>
</p>
</form>
</div>
</div>
);
};
ここでは、React Hook Formのregister
関数を利用して、各入力フィールドを登録し、検証ルールを設定しています。フォームの状態管理に関する詳細については前述の説明をご参照ください。
React Hook Form : パスワード検証とカスタムバリデーションで安全性向上
React Hook Form を使用すると、pattern
プロパティによる正規表現を使用した検証や、validate
プロパティを使うことでカスタム検証を追加できることを学びました。
pattern: {
value: /^\S+@\S+$/i,
message: "Invalid email address",
},
validate: ( value) => value === watch( "password") || "Passwords do not match"
React Hook Form : バリデーションモードの使い分けでUXを最適化する
React Hook Form でより良いUXを提供するには、 validationMode
を使い分けましょう。以下に例を挙げます。
const { register, handleSubmit, formState, trigger } = useForm({
mode: "onSubmit",
});
さらに、trigger()
メソッドを使うことで、個々のフィールドに対して、指定されたタイミングで検証を実行できます。
<input
{...register (" email ", { required: " Email is required " })}
onBlur = {() => trigger("email")} // validate this field onBlur manually
/>
これらの設定により、ユーザーエクスペリエンスは大幅に向上します。
4. エラーメッセージを表示する
フォームエラーは、formState
オブジェクトに格納されています。エラーメッセージは、以下のように表示できます。
{errors.password && (
<p className="text-red-500 text-sm mt-1"> {errors.password.message} </p>
)}
5. aria-required属性を追加する
視覚障碍者のために、aria-required
属性を追加することで、必須フィールドであることを知らせることができます。
<input
{...register("fullName", {
required: "Full Name is required"
})}
aria-required
type="text"
placeholder="Full name"
className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"
/>
6. fieldsetとlegend要素を追加する
fieldset
要素はフォームコントロールをグループ化し、legend
要素はグループ化されたコントロールの説明を提供します。
7. ラベルとhtmlFor属性を使用する
ラベルとhtmlFor
属性を適切に使用することで、フォームのアクセシビリティが向上します。
8. プレースホルダーだけに頼らない!
プレースホルダーだけを使用すると、ユーザーが入力した内容がわからなくなる可能性があります。
9. aria-describedByで追加情報を提供する
aria-describedBy
属性を使用すると、入力フィールドに関する追加情報を提供できます。
10. 重要な情報にツールチップを使用しない
ツールチップは、すべてのユーザーがアクセスできるとは限りません。
11. 重要なことを知らせる
重要な情報は、明確に表示する必要があります。
12. フォーカス状態と色分け
フォーカス状態と色分けを使用することで、ユーザーがどのフィールドにフォーカスしているかを明確にすることができます。
13. ボタンをわかりやすくする
ボタンのラベルは、そのボタンが何をするのかを明確に説明する必要があります。
14. 最後に:フォームのアクセシビリティ向上 まとめ
これらのテクニックを使用することで、Reactフォームのアクセシビリティとユーザーエクスペリエンスを大幅に向上させることができます。
Webアクセシビリティの原則に従い、すべてのユーザーが利用できるWebアプリケーションを作成しましょう。