CSSの力でフォーム体験を極める:`:valid` と `:invalid` を使いこなす現場の流儀
フロントエンドの現場で「フォームバリデーション」という言葉が出た瞬間、多くのエンジニアは真っ先に JavaScript のバリデーションライブラリを思い浮かべるはずだ。`yup` や `zod` でスキーマを定義し、状態を管理し、エラーメッセージを出し分ける……。もちろん、それは正しい。
しかし、腕の立つフロントエンド・アーキテクトならこう考えるはずだ。「CSSだけで完結できるUXの向上を、わざわざJSに持ち込んでコストを払う必要はあるか?」と。
今回は、現代のブラウザが標準で備えている `:valid` と `:invalid` を使い倒し、JavaScriptに頼りすぎない「堅牢で美しいフォーム」を作るための知見を共有しよう。
—
ブラウザの裏側:なぜこれが最強の選択肢なのか
そもそも `:valid` と `:invalid` は、ブラウザが HTML5 のバリデーション機能(`required`, `type=”email”`, `pattern` 属性など)を解釈した結果、その要素が満たされているかどうかを自動判定する疑似クラスだ。
ここが重要なのだが、ブラウザはページが読み込まれた瞬間から、ユーザーがキーを叩くたびに、裏側で常にこの判定を回している。
多くのジュニア層は、これを「エラーメッセージを出すためのツール」としか見ていない。だが、真のプロはこれを使って「入力のフィードバックループ」を CSS だけで完結させる。JSの再レンダリングを待たずに、ユーザーが文字を打った瞬間に枠線が赤から緑に変わる。このレスポンスの速さが、UIの信頼性に直結するんだ。
実践:コピペで使える「脱・JSバリデーション」スタイル
まずは、現場ですぐに使える洗練されたパターンを見てほしい。単に赤くするだけではUXとして不十分だ。フォーカス時にエラーを出すタイミングを制御し、ユーザーの「入力中に怒られるストレス」を緩和するのがポイントだ。
/ フォームの基本スタイル /
.input-field {
display: block;
width: 100%;
padding: 12px;
border: 2px solid #ccc; / デフォルトはニュートラルに /
border-radius: 4px;
outline: none;
transition: border-color 0.2s ease;
}
/
- ポイント1: :invalid は「入力中」には適用させないのがコツ
- :not(:placeholder-shown) を組み合わせることで、
- 何も入力していない初期状態のエラー表示を防ぐ
/
.input-field:invalid:not(:placeholder-shown) {
border-color: #ff4d4f;
background-color: #fff2f0;
}
/
- ポイント2: :valid で正解を視覚的にフィードバック
- ユーザーは「正しく入力できている」という確信が持てると安心する
/
.input-field:valid:not(:placeholder-shown) {
border-color: #52c41a;
}
/
- ポイント3: エラーメッセージの出し分け
- 隣接セレクタを使って、エラー時のみメッセージを表示させる
/
.error-message {
display: none;
color: #ff4d4f;
font-size: 0.85rem;
margin-top: 4px;
}
.input-field:invalid:not(:placeholder-shown) + .error-message {
display: block;
}
—
現場で直面する「落とし穴」への対策
この手法を使いこなす上で、シニアとして君たちに伝えておきたい「泥臭い現実」が2つある。
1. 空白時の挙動を制御せよ
`:invalid` は `required` が付いている要素が空の時も適用される。しかし、ユーザーがフォームをクリックした直後に赤色が出るのはUXとして最悪だ。上述のサンプルで `:not(:placeholder-shown)` を入れているのはそのためだ。あるいは、`:focus` と組み合わせて「フォーカスが外れたタイミング(`:focus:invalid`)」でエラーを表示させるのも戦略の一つだ。
2. ブラウザのデフォルトUIを消す
`type=”email”` などを使うと、ブラウザ標準のツールチップ(「メールアドレス形式で入力してください」といった吹き出し)が邪魔になることがある。これを消すには、フォーム要素ではなく `
最後に:CSSは「状態」を記述する言語だ
フロントエンドのアーキテクチャにおいて、「DOMの属性(状態)」と「スタイル」を同期させるのは、もっともバグが少なく、パフォーマンスが高いアプローチだ。
JavaScript で `isError` のような state を管理し、それを `className` に反映させ、再レンダリングを走らせる……。それも一つの手法だが、ブラウザが提供しているネイティブな `:valid / :invalid` を活用することは、コード量を減らし、ブラウザの最適化を最大限に引き出す「プロの選択」だ。
まずは次のプルリクエストで、バリデーションロジックの一部をCSSに逃がしてみるといい。チームのメンバーが「なぜこんなにコードがスッキリしているんだ?」と驚くはずだ。それが、君が技術の核心を捉えた証拠になる。

コメント