【テクニカル・上級編】 フォーカス可視擬似クラス :focus-visible – CSS実践ガイド

`:focus-visible`:アクセシビリティの「負債」を帳消しにする、真のエンジニアの選択

Web開発の現場で、一度は「`outline: none` を書くな」という教えに直面したことがあるはずだ。デザイナーの「フォーカスの青い枠がデザインを壊す」という要望と、アクセシビリティ担保のための「キーボード操作時のインジケーター」という要件。この永遠のトレードオフを、我々エンジニアは長年、泥臭いJavaScriptのイベント監視で解決してきた。

だが、モダンなフロントエンドにおいて、もはや泥臭いハックは必要ない。`:focus-visible` は、ブラウザが「今、ユーザーに視覚的なガイダンスを表示すべきか否か」を自律的に判断する、極めてインテリジェントな擬似クラスだ。今日は、これを単なる「便利なCSS」としてではなく、レンダリングパイプラインとパフォーマンスの観点から深く掘り下げていこう。

—

1. ブラウザエンジンが裏側で行っている「推論」

`:focus` と `:focus-visible` の決定的な違いは、ブラウザが保持する「フォーカス状態のコンテキスト」にある。

従来の `:focus` は、クリックだろうがTabキーによる移動だろうが、要素がフォーカスを受け取った瞬間に強制的に適用される。一方で `:focus-visible` は、ユーザーの入力デバイスの履歴(マウスでクリックしたか、キーボードで移動してきたか)をブラウザが内部的に追跡し、「ユーザーが今、キーボード操作によるナビゲーションを必要としている可能性が高い」と判断した時のみ有効化される。

これは、メインスレッドで行われるイベントループにおいて、非常に軽量なヒューリスティクスとして実装されている。JavaScriptで `mousedown` や `keydown` を監視し、クラスを付与・削除するような処理は、DOMの変更を伴い、再描画のトリガーとなる。対して `:focus-visible` は、ブラウザのスタイルエンジンがネイティブに解決するため、オーバーヘッドはほぼゼロだ。

2. 実装の要諦:プログレッシブ・エンハンスメントの極致

上級エンジニアとして推奨したいのは、グローバルなCSS設計に組み込む際のアプローチだ。まず、デフォルトの `outline` を削除し、より洗練されたスタイルで再定義する。ここで重要なのは「アクセシビリティを殺さないこと」だ。

/ リセット:ブラウザ依存の醜いアウトラインを無効化 /
:focus {
outline: none;
}

/ 鍵:必要な時だけ、堅牢な視覚インジケーターをレンダリングする /
:focus-visible {
/ box-shadowはlayout/paintのコストが低く、GPUアクセラレーションを考慮しやすい /
outline: 2px solid transparent;
box-shadow: 0 0 0 3px var(–brand-color, #007bff);

/ 輪郭のオフセットを調整し、要素の境界を明確にする /
outline-offset: 2px;
}

ここで `box-shadow` を使うのには理由がある。`outline` プロパティは一部のブラウザでレンダリングの挙動が異なり、要素の形状に追従しないケースがある。`box-shadow` であれば、`border-radius` と組み合わせた際の親要素の形状を維持しつつ、GPUレイヤーでの合成が行われるため、パフォーマンス上の懸念が少ない。

3. 非同期読み込みと競合の回避

大規模なWebアプリケーションでは、CSS-in-JSやShadow DOMとの競合がつきものだ。特にShadow DOM内で `:focus-visible` を使用する場合、スタイルカプセル化の影響で外部のグローバルスタイルが継承されないことがある。

もしコンポーネント単位で管理する場合、以下のような設計が堅牢だ。

/ コンポーネント内のスコープで定義 /
.my-button {
transition: box-shadow 0.2s ease-out;
}

.my-button:focus-visible {
/ コンポーネントの文脈に応じたフォーカススタイル /
outline: 2px solid var(–comp-focus-color);
}

ここで注意すべきは、`transition` を付与する場合だ。`focus` 状態から `blur` 状態へ移行する際、フォーカスインジケーターが消える瞬間にアニメーションが走る。このアニメーションが `will-change` プロパティなどを伴う場合、過度な適用はブラウザのレンダリング負荷を高める。シンプルに `box-shadow` のみ、あるいは `outline-offset` の微調整に留めるのが、パフォーマンス最適化の定石である。

4. 現場で直面する「バグ」への処方箋

最も厄介なのは、`