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

フロントエンドの現場で日々コードを書いていると、「親要素の中にいる子要素のどれかにフォーカスが入ったとき、親のスタイルをごっそり変えたい」という要件に必ずぶち当たりますよね。

例えば、検索フォーム。`` と検索ボタンの `

/ 検索フォームの親コンテナ:普段はニュートラルなボーダー /
.search-form-group {
display: flex;
align-items: center;
border: 2px solid #ccc;
border-radius: 8px;
padding: 4px 8px;
background-color: #fff;
/ フォーカス時のトランジションを滑らかに仕込んでおくのがプロの技 /
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/
【ここがポイント】
input自体、またはbutton自体にフォーカスが入った瞬間、
親である .search-form-group のスタイルを変化させる
/
.search-form-group:focus-within {
border-color: #0066cc;
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.2);
}

/ インプット自体のデフォルトの輪郭(アラインメント)は邪魔なので消す /
.search-form-input {
flex: 1;
border: none;
outline: none;
background: transparent;
font-size: 1rem;
padding: 8px;
}

/ ボタンも同様にデフォルトのアウトラインをリセットして親に委譲する /
.search-form-button {
background: none;
border: none;
cursor: pointer;
padding: 8px;
}

この実装の美しいところは、インプットにフォーカスが当たっていようが、tabキーで隣の検索ボタンにフォーカスが移動していようが、「ユーザーがこの検索フォーム群のどこかを操作している間は、一貫して親枠が光り続ける」という点です。UXの観点から見ても非常に親切ですよね。

—

パターンB:アクセシビリティを担保したドロップダウンメニュー

アクセシビリティ(a11y)を意識したキーボード操作可能なドロップダウンメニューでも、`:focus-within` は大活躍します。マウスホバー(`:hover`)だけでなく、キーボードユーザーが `Tab` でメニュー内に入ってきたときにもリストを展開させることができます。

.dropdown-menu {
position: relative;
display: inline-block;
}

/ デフォルトではリストを非表示にしておく /
.dropdown-list {
position: absolute;
top: 100%;
left: 0;
display: none; / 本来はvisibilityやopacityでアニメーションさせるのがベター /
list-style: none;
padding: 8px 0;
margin: 4px 0 0;
background: #fff;
border: 1px solid #ddd;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

/
ホバーしている時、または
トリガーボタンやメニュー内のリンクにフォーカスが入っている時にリストを表示する!
/
.dropdown-menu:hover .dropdown-list,
.dropdown-menu:focus-within .dropdown-list {
display: block;
}

.dropdown-list a {
display: block;
padding: 8px 16px;
color: #333;
text-decoration: none;
}

.dropdown-list a:hover {
background-color: #f0f0f0;
}

JavaScriptの `mouseenter` と `focusin` を両方キャッチしてクラスを付け外しするコードを書いていた時代を思い出してください……。あのボイラープレートなコードが、CSSのこの数行で完結してしまうのだから、現代のフロントエンド開発は本当に素晴らしいです。

—

3. 実務でハマりがちな「落とし穴」と回避のコツ

さて、ここからがシニアからのリアルな現場アドバイスです。`:focus-within` は非常に強力ですが、実務で使う上でいくつか注意すべき罠があります。

罠1:IE11の呪縛はもう忘れていいが、古い環境のポリシーを確認せよ

今更言うまでもありませんが、Internet Explorer 11は完全にサポートが終了しています。`:focus-within` は主要モダンブラウザで完全サポートされているため、新規案件やリプレイス案件であれば基本は気にせず使って大丈夫です。
ただし、レガシーな社内システムなどを保守している場合は、未対応ブラウザが存在するリスクがあるので、Can I use等でターゲット層の環境を必ず確認してください。

罠2:「フォーカスが外れた瞬間」のアニメーションの罠

`:focus-within` が外れたとき(フォーカスがロストしたとき)、`transition` を設定していると、パッとスタイルが元に戻る挙動になります。これ自体は自然なのですが、「フォーカスが子孫要素の間で移動するとき(例:インプットからボタンへ)」に注意が必要です。

例えば、親コンテナの中に `input` と `button` があり、両方ともに `:focus-within` でスタイルを変えている場合、
1. `input` にフォーカス (`:focus-within` 適用)
2. `Tab` で `button` へ移動 (`:focus-within` 継続)

この間、親要素は一貫してフォーカス内にあるためスタイルは維持されますが、もし子要素個別の `:focus` スタイルと親の `:focus-within` が複雑に絡み合っていると、意図しないチラつきが発生することがあります。
「親のスタイル制御は `:focus-within` に一任し、子要素個別の派手なスタイル上書きは控えめにする」という役割分担を意識すると、コードが綺麗に破綻なくまとまります。

—

まとめ

`:focus-within` は、単なる「便利なCSSプロパティ」の一つではありません。
これまでJavaScriptに頼らざるを得なかった「コンテキストの変更検知」を、宣言的なCSSの世界に引き戻してくれたアーキテクチャ上の重要ピースです。

JSのコード量を減らし、ブラウザのネイティブ機能に処理をオフロードすることで、アプリケーションのパフォーマンス向上と保守性の高さを同時に手に入れることができます。

もしあなたのプロジェクトで、まだ「親のスタイルを変えるためにJSでクラスをゴソゴソ操作しているコード」を見つけたら、ぜひこの `:focus-within` を使ってリファクタリングを提案してみてください。「おっ、わかってるな」とチームで一目置かれる存在になれるはずです。

明日からのコーディングで、ぜひガンガン活用していきましょう!

コメント

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