はじめに
フォームUIの状態表現は、気づくと JavaScript が増えがちです。フォーカス、エラー、入力済みなどの軽い条件までJSで管理すると、保守時に「見た目だけ直したいのにロジックまで触る」状態になりやすくなります。
:has() を使うと、子要素の状態を親側で判定できるため、UIの装飾ロジックをCSSへ戻せます。この記事では、最小例から実務向けのバリデーション表示まで段階的に組み立てます。
使い方
:has() は関係セレクタで、特定条件を満たす子要素を持つ親要素にスタイルを当てられます。フォームでは「入力中」「妥当性エラー」「チェック済み」などを親ラッパーに反映する用途が定番です。
メリットは、状態判定の分岐をHTML/CSSの近くに置けることです。デザイン変更時にJSファイルへ飛ばずに調整しやすくなります。
まずは入力フォーカス時にラベル全体を強調する最小例です。input:focus を持つ .field を直接装飾します。
See the Pen css :has() by watanabe (@web-sourcecode) on CodePen.
HTML
<label class="field">
<span class="field__label">メールアドレス</span>
<input class="field__control" type="email" required>
</label>
CSS
.field {
display: grid;
gap: 6px;
padding: 12px;
border: 1px solid #cbd5e1;
border-radius: 10px;
transition: border-color 180ms ease, box-shadow 180ms ease;
}
.field:has(.field__control:focus) {
border-color: #0ea5e9;
box-shadow: 0 0 0 3px rgb(14 165 233 / 15%);
}
:has() はフォームに限らず、チェック済みカード、選択中メニュー、入力補助UIなどで役立ちます。特に「親の見た目を変えたい」要件に強く、クラス付け用のJSを削減できます。
また、デザイナーと共同作業する現場では、状態ごとの見た目をCSS側で完結しやすいため、レビューの往復も減らせます。
応用コード
次は「未入力エラー」「妥当入力」「必須未チェック」を1つのフォーム内で扱う例です。構造が増えるため、必要な実装要素をまとめて示します。
See the Pen css :has() 2 by watanabe (@web-sourcecode) on CodePen.
HTML
<form class="profile-form" novalidate>
<label class="field">
<span class="field__label">メールアドレス</span>
<input class="field__control" type="email" required>
<small class="field__help">業務連絡に使うアドレスを入力してください。</small>
</label>
<label class="check">
<input type="checkbox" required>
<span>利用規約に同意する</span>
</label>
<button type="submit">保存</button>
</form>CSS
.profile-form {
display: grid;
gap: 14px;
}
.field {
display: grid;
gap: 6px;
padding: 12px;
border: 1px solid #cbd5e1;
border-radius: 10px;
}
.field:has(.field__control:user-invalid) {
border-color: #ef4444;
background: #fff5f5;
}
.field:has(.field__control:user-valid) {
border-color: #22c55e;
background: #f0fdf4;
}
.check {
padding: 10px;
border: 1px solid #cbd5e1;
border-radius: 10px;
}
.check:has(input:invalid) {
border-color: #f59e0b;
background: #fffbeb;
}
このように :has() を使うと、入力要素の状態を親ラッパーに伝播させるコードを増やさずに済みます。
注意点
:has() は便利ですが、複雑なネスト条件を増やしすぎると読みづらくなります。セレクタは短く保ち、対象クラスを明確にしておくのが安全です。
さらに、ブラウザサポート対象によってはフォールバック方針が必要です。影響が大きい画面では、基本表示が崩れないように「:has() なしでも読めるUI」を先に確保してください。
まとめ
:has() はフォーム状態の見せ方をCSS側へ戻しやすい実践的な機能です。軽い状態表現をJSから切り離せるため、UI改修の速度と保守性を両立しやすくなります。
まずはフォーカスとバリデーション表示から導入し、効果を確認しながら適用範囲を広げる進め方がおすすめです。
ポイント
- 親要素の装飾をJSなしで制御しやすくなる
user-valid / user-invalidと組み合わせると実務で使いやすい- セレクタは短く保ち、フォールバックを先に決める