【実務・中級編】 :valid 疑似クラス – CSS実践ガイド

お疲れ。今日も今日とて、デザインカンプ通りにピクセルパーフェクトを叩き出しつつ、アクセシビリティの怪しい実装と格闘していることだろう。

さて、今回はフォームのバリデーション、その中でも地味ながらも強力な武器である `:valid` 疑似クラス について深掘りしていこうと思う。

「入力内容が正しいときにスタイルを当てるやつでしょ? `input:valid` とかで緑色の枠線出すやつね」
……そう思ったそこのあなた。半分正解で、半分は大怪我するフラグが立っている。

実務でフォームを実装する際、ページの読み込み直後や、ユーザーがまだ何も入力していない「初期状態」の `` に `:valid` がヒットしてしまい、ページを開いた瞬間からフォーム中が「入力完了です!」と言わんばかりの緑色に染まった、なんてホラー体験はないだろうか?

今回は、ブラウザの裏側の挙動を紐解きながら、現場で本当に使える `:valid` の処世術を伝授しよう。

—

1. そもそも `:valid` 疑似クラスとは何か?(仕様の再確認)

`:valid` は、HTMLのバリデーション属性(`required`, `type=”email”`, `type=”url”`, `minlength`, `pattern` など)の制約を満たしているフォーム要素にマッチする疑似クラスだ。

DOMの標準仕様において、要素がバリデーションエラーを起こしていない状態であれば、この `:valid` が付与される。逆にエラーがあれば `:invalid` だ。

ここで重要なのは、「何も入力されていない空の要素(requiredなし)」は、デフォルトでバリデーションを通過(valid)とみなされる という仕様のトラップだ。

ブラウザの裏側で何が起きているか?

ブラウザのレンダリングエンジン(Blink, Gecko, WebKitなど)は、フォーム要素が生成されると同時に内部で `ValidityState` というインターフェースを保持している。
この中には `valueMissing`, `typeMismatch`, `patternMismatch` などのフラグがあり、これらがすべて `false` のとき、ブラウザは「よし、この要素は valid だ」と判断し、CSSの `:valid` セレクタをツリーに適用する。

つまり、初期状態の空っぽの `input`(`required` が無い場合)は、エラーフラグが立っていないため、容赦なく `:valid` の世界へ放り込まれるというわけだ。

—

2. 現場でありがちな「やっちまった」実装

まずは、よくある失敗例を見てみよう。

/ 悪い例:初期状態から緑のボーダーが出てしまう /
input:valid {
border-color: #10b981; / 成功時の緑色 /
}

input:invalid {
border-color: #ef4444; / エラー時の赤色 /
}

このコードを書いてブラウザでフォームを開いた瞬間、どうなるか?
ユーザーがまだキーボードに触れてすらいないのに、すべての入力欄が「入力完了」の緑色に染まるか、あるいは必須(`required`)の赤色に染まる。これではユーザービリティの観点から最悪だ。「まだ何も書いてないのに怒られた」「入力してないのに褒められた」というカオスな状態になる。

私たちフロントエンドエンジニアが目指すべきは、「ユーザーが入力アクションを起こした後(あるいはフォーム送信を試みた後)」に、適切にフィードバックを返すスマートなUIだ。

—

3. 実務で使える!`:valid` を飼いならすベストプラクティス

では、どうすればこの初期状態の「誤爆」を防げるのか?
答えはシンプルで、「ユーザーが一度でもそのフィールドに触れた(インタラクトした)」、あるいは「フォーム送信を試みた」という文脈をCSS側でフックすることだ。

ここに、現代CSSの強力な疑似クラス(`:user-invalid`, `:placeholder-shown` など)を組み合わせる。

アプローチA:`:placeholder-shown` との合わせ技(王道テクニック)

まだユーザーが入力していない(プレースホルダーが表示されている)状態のときは、`:valid` がついていてもスタイルを打ち消す、というアプローチだ。

/ プレースホルダーが表示されている(=未入力)のときは、validであっても装飾しない /
input:valid:not(:placeholder-shown) {
border-color: #10b981;
background-image: url(‘data:image/svg+xml,…’); / チェックマークのアイコンなど /
}

/ エラー時も同様に、未入力の初期状態では赤くしない工夫が必要 /
input:invalid:not(:placeholder-shown) {
border-color: #ef4444;
}

この手法のミソは `:not(:placeholder-shown)` だ。ユーザーが1文字でも入力した瞬間にプレースホルダーが消えるため、そこから初めて `:valid` や `:invalid` の真価が発揮される。

—

4. コピペで使える!実践フォームコンポーネントのサンプル

実際にプロダクトでそのまま使えるレベルの、リッチなCSS設計のサンプルコードを置いておく。
HTMLの構造とあわせて確認してほしい。

HTML



有効なメールアドレスを入力してください。
OK!

※注: ブラウザ標準のネイティブツールチップを抑止するために、`

` には `novalidate` を付与しつつ、CSSでリッチな自前UIを構築するのがモダンな現場のスタンダードだ。

CSS

/ フォーム全体のコンテナ /
.c-form {
max-width: 480px;
margin: 2rem auto;
font-family: sans-serif;
}

.c-form__field {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1.5rem;
position: relative;
}

.c-form__label {
font-size: 0.875rem;
font-weight: bold;
color: #333;
}

.c-form__badge {
font-size: 0.75rem;
color: #fff;
background-color: #666;
padding: 0.1rem 0.4rem;
border-radius: 4px;
}

/ インプットの基本スタイル /
.c-form__input {
padding: 0.75rem 1rem;
font-size: 1rem;
border: 2px solid #ccc;
border-radius: 6px;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/ フォーカス時の挙動 /
.c-form__input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}

/
【ここが重要】
初期状態(空かつプレースホルダー表示中)では、requiredであっても
エラー/サクセスのスタイルを絶対に発火させない。
/
.c-form__input:invalid:not(:placeholder-shown) {
border-color: #ef4444;
background-color: #fef2f2;
}

.c-form__input:valid:not(:placeholder-shown) {
border-color: #10b981;
background-color: #f0fdf4;
}

/ メッセージの制御:デフォルトは非表示 /
.c-form__message {
font-size: 0.75rem;
display: none;
}

.c-form__message–error {
color: #ef4444;
}

.c-form__message–success {
color: #10b981;
}

/
入力内容に応じたメッセージの出し分け
※隣接セレクタや兄弟セレクタを駆使してDOM構造で魅せる
/
.c-form__input:invalid:not(:placeholder-shown) ~ .c-form__message–error {
display: block;
}

.c-form__input:valid:not(:placeholder-shown) ~ .c-form__message–success {
display: block;
}

—

5. チーフアーキテクトからの実践アドバイス

`:valid` や `:invalid` は、JavaScriptのバリデーションライブラリを導入するまでもなく、CSS単体でリアクティブなUIを作るための強力なプリミティブだ。

しかし、実務で使う上では以下の点に注意してほしい。

1. JavaScriptとの役割分担を明確にする
複雑な非同期バリデーション(例:「このメールアドレスは既に登録されていますか?」といったサーバーサイド連携)はCSSの `:valid` では絶対に判定できない。あくまで「形式的なフォーマットチェック」や「必須チェック」の補助、およびUIのスタイリングのフックとして割り切って使うこと。
2. アクセシビリティ(a11y)を忘れない
色(赤や緑)だけに頼ったエラー表現は、色覚多様性を持つユーザーにとって不親切だ。必ず上記のサンプルコードのように、テキストメッセージやアイコンを併用し、スクリーンリーダーにも正しく状態が伝わるように `aria-invalid` などをJSで適切に同期させる設計を忘れないようにしよう。

CSSの仕様の裏側を理解し、ブラウザの機嫌を損ねないスマートなスタイリングを組むこと。それが、ワンランク上のフロントエンドエンジニアへの第一歩だ。
次のスプリントのフォーム改修から、ぜひこの知見をコードに落とし込んでみてくれ。健闘を祈る!

コメント

タイトルとURLをコピーしました