【実務・中級編】 不正状態擬似クラス :invalid – CSS実践ガイド

お疲れ。フォームの実装、毎日ご苦労さん。
バックエンドから「バリデーションはフロントでもよろしく!」って言われて、JavaScriptでチマチマ正規表現書いて、エラーメッセージの出し分けに頭を悩ませて……なんて泥臭いこと、まだやってないだろうな?

おいおい、現代のCSSをなめんなよ。
ブラウザのネイティブ機能とCSSの擬似クラスを組み合わせれば、JSの力を借りずとも、圧倒的に美しく、アクセシブルなフォームバリデーションが構築できるんだ。

今回は、その主役である `:invalid` 擬似クラス について、実務で絶対に知っておくべき「表側の仕様」と「裏側の挙動」、そして現場で使える実践テクニックを叩き込んでやる。心して聞けよ。

—

1. `:invalid` 擬似クラスとは?その本質を理解する

`:invalid` は、一言で言えば「HTMLのバリデーションルールを満たしていないフォーム要素」を捉えるためのセレクタだ。

例えば、以下のようなHTMLがあったとする。

このインプットに「hoge」とだけ入力したとする。メールアドレスの形式(`[ユーザー名]@[ドメイン]`)になっていないよな? この瞬間、ブラウザの内部エンジンはこの要素を `:invalid` 状態であると判定する。

実務でハマりがちな「最大の罠」

ここで一つ、中級者でも盛大にやらかすポイントを教えておこう。
「ページを開いた瞬間、まだ何も入力していない状態でも `:invalid` は発火する」 という事実だ。

何も入力されていない空の ``。これも、バリデーションルール(この場合は「値が存在し、かつメールアドレス形式であること」)を満たしていない。そのため、ページを表示した瞬間に赤枠で囲まれたりして、ユーザーに「最初からエラーです」という絶望を与えるクソUIが完成してしまう。

……おっと、口が悪くなったな。だが、これが現実だ。ブラウザの仕様上、初期状態でもルールを満たしていなければ `:invalid` は適用される。だからこそ、現場ではこの挙動をコントロールする知恵が必要になる。

—

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

CSSのセレクタとしての側面だけでなく、ブラウザのConstraint Validation API(制約検証API)とどう連動しているかを知るのが、シニアへの第一歩だ。

1. DOMの構築と属性の検知: ブラウザは `required`、`type=”email”`、`minlength`、`pattern` などのHTML属性を読み取り、制約ルールを内部ツリーに保持する。
2. リアルタイムの評価: ユーザーがキーボードを叩くたび(`input` イベントの裏側で)、ブラウザは入力値がそのルールに適合しているかをミリ秒単位で評価する。
3. 状態の付与:

  • 条件クリア = `:valid`
  • 条件未達 = `:invalid`
  • ユーザーがまだ触っていない、あるいはフォーカスが当たっていないなどの状態は `:placeholder-shown` や `:user-invalid`(後述)といった別の文脈と組み合わせて管理される。

この一連の処理がすべてC++などのネイティブ層で爆速で処理されている。だからこそ、JavaScriptでDOMを監視してクラスを付け外しするよりも、圧倒的にパフォーマンスが高いんだ。使わない手はないよな?

—

3. 【実務で即コピペ】美しく破綻しないフォームの書き方

さて、理論はこれくらいにして、現場でそのまま使える実践的なコードを見せよう。
前述した「最初から赤くなる問題」を華麗に回避しつつ、ユーザーが入力し始めた瞬間、あるいはフォーカスを外した(blurした)タイミングで優しくエラーを教える、プロのスタイリングだ。

HTML




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

CSS

/ フォームグループ全体を綺麗に整える /
.form-group {
margin-bottom: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.form-group label {
font-weight: bold;
font-size: 0.875rem;
}

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

/ フォーカス時の基本スタイル /
.form-group input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}

/ ==========================================
ここからがプロの技:不適切な状態のスタイリング
========================================== /

/
1. :placeholder-shown を組み合わせることで、
「プレースホルダーが表示されている(=何も入力されていない)」初期状態では
:invalid が発火していてもエラー表示をさせない!
/
.form-group input:invalid:not(:placeholder-shown) {
border-color: #ef4444;
background-color: #fef2f2;
}

/ エラーメッセージはデフォルトで隠しておく /
.form-group .error-message {
display: none;
font-size: 0.75rem;
color: #ef4444;
}

/ 入力中で、かつバリデーションエラーの時だけメッセージをスッと出す /
.form-group input:invalid:not(:placeholder-shown) ~ .error-message {
display: block;
}

/ 逆に、しっかりとルールを満たした場合はサッと緑の枠にするなどの褒め演出 /
.form-group input:valid:not(:placeholder-shown) {
border-color: #10b981;
}

このコードの美しいポイント

`:not(:placeholder-shown)` を挟んでいるのがミソだ。
これによって、「未入力の初期状態」と「何かしら打ち始めたけど間違っている状態」を完璧に切り分けることができる。ユーザーが文字を打ち込むまでは、無駄な赤枠でプレッシャーを与えない。これぞ親切なUIデザインだ。

—

4. さらに先へ:次世代の救世主 `:user-invalid`

ここまで `:not(:placeholder-shown)` のテクニックを教えたが、実は現在、W3Cやブラウザベンダーはさらに洗練された擬似クラスを用意してくれている。それが `:user-invalid` だ。

/ ブラウザが「ユーザーが意図的にインタラクションを行った(入力した、フォーカスして離れた等)」と判断した時だけ発火する /
.form-group input:user-invalid {
border-color: #ef4444;
}

.form-group input:user-invalid ~ .error-message {
display: block;
}

これは `:invalid` と違って、ユーザーがまだ触っていない初期状態では絶対に発火しない。まさに私たちが求めていた挙動をブラウザがネイティブでやってくれる神機能だ。
※モダンブラウザでのサポートも進んできているが、プロダクトのターゲット層(社内システムか一般向けかなど)によっては、まだフォールバックとして `:not(:placeholder-shown)` や従来の書き方を併用する必要があるケースもある。そこは案件の要件定義と相談してくれ。

—

5. チーフアーキテクトからのメッセージ

CSSの擬似クラスを使いこなすということは、「ブラウザの思考プロセスをハックする」ということだ。
JavaScriptのイベントリスナーでゴリゴリ書く前に、「CSSだけでここまでエレガントに解決できないか?」と一歩立ち止まって考える癖をつけてほしい。コードの行数が減り、保守性が上がり、パフォーマンスも跳ね上がる。その快感を一度知ったら、もう元のスパゲッティなJSコードには戻れなくなるはずだ。

さて、理論はここまでだ。
早速ローカルの開発環境を開いて、今回のコードをコピー&ペーストして、自分の手で動きを確かめてみてくれ。分からなくなったら、いつでも俺のところに聞きに来い。応援しているぞ。

コメント

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