【ITニュース解説】Styling sibling hover and none hover
2025年09月14日に「Dev.to」が公開したITニュース「Styling sibling hover and none hover」について初心者にもわかりやすく解説しています。
ITニュース概要
CSSでリストリンクのスタイル制御方法を解説。マウスオーバーやフォーカス時は該当リンクを装飾し、それ以外は最初のリンクをスタイルする。`has`セレクタを使い、兄弟要素のホバー時に最初のリンクの装飾を解除するテクニックを紹介。素早い操作で一瞬スタイルが乱れる課題も指摘する。
ITニュース解説
ウェブサイトのデザインにおいて、リンクやメニュー項目に特別な視覚効果を加えることは、ユーザーにとって使いやすさや見た目の魅力を高める上で非常に重要だ。この記事では、リスト形式のリンク要素に対し、マウスカーソルが重なったとき(ホバー)やキーボードで選択されたとき(フォーカス)に特定のスタイルを適用しつつ、さらにどの項目も選択されていない場合には最初の項目にデフォルトのスタイルを適用するという、一見すると複雑なスタイリングの実現方法が解説されている。これはCSSの強力な機能と少しの工夫で実現できるテクニックであり、システムエンジニアを目指す上で知っておくべき実践的な知識の一つだ。
まず、基本的なHTML構造から見てみよう。この例では、一般的な順序なしリスト<ul>の中に、リスト項目<li>がいくつか並び、それぞれの項目にはリンク<a>要素が含まれている。
1<ul> 2 <li><a href="#">One</a></li> 3 <li><a href="#">Two</a></li> 4 <!-- 他のリスト項目も続く --> 5</ul>
このような構造は、ウェブサイトのナビゲーションメニューやコンテンツの一覧などで頻繁に使われる。
次に、このリスト項目がホバーまたはフォーカスされたときのスタイリングについて考える。これは最もシンプルな部分であり、CSSの疑似クラス:hoverと:focusを使用する。
1li a:hover, 2li a:focus { 3 background-color: pink; 4}
このCSSコードは、「リスト項目<li>の中にあるリンク<a>要素がホバーされるか、フォーカスされたら、その背景色をピンクにする」という意味だ。これで、ユーザーがマウスをリンクに重ねたり、キーボードのTabキーで選択したりすると、そのリンクの背景がピンク色に変わるようになる。
しかし、今回の要件にはもう一つ、「どの項目もホバーもフォーカスもされていないときには、リストの最初の項目がピンク色になる」という条件がある。これを実現するために、CSSの疑似クラス:first-of-typeを使用する。
1li:first-of-type a { 2 background-color: pink; 3}
このコードは、「同じ種類の兄弟要素の中で最初のリスト項目<li>の中にあるリンク<a>要素の背景色をピンクにする」という意味になる。これで、ページが読み込まれた時点や、どの項目にもマウスが乗っていない、あるいはキーボードで選択されていない状態では、常に最初のリンクがピンク色で表示されるようになった。
ここまでは順調だが、これら二つのCSSルールを組み合わせると一つの問題が発生する。それは、ユーザーが最初の項目ではない他の項目にマウスを合わせたときだ。例えば、2番目の項目にマウスを合わせると、2番目の項目はピンク色になる。しかし、同時に最初の項目も:first-of-typeのルールによってピンク色のままだ。つまり、「どの項目も選択されていないときには最初の項目がピンク」という意図が、「他の項目が選択されている間は最初の項目はデフォルトに戻る」という部分で破綻してしまうのだ。私たちは、「どれかが選択されている間は、最初の項目のデフォルトスタイルは解除される」という動作を求めている。
この問題を解決するために、CSSの:has()疑似セレクタという比較的新しい、非常に強力な機能を使う。:has()セレクタは、「指定された条件を満たす子孫要素を持つ要素」や「指定された条件を満たす兄弟要素を持つ要素」に対してスタイルを適用できるという画期的なものだ。今回のケースでは、次のように記述する。
1li:has(~ li a:hover) a, 2li:has(~ li a:focus) a { 3 background-color: unset; 4}
このコードを分解して見ていこう。
まず、~(チルダ)記号は「後続の兄弟セレクタ」と呼ばれるもので、「指定した要素の後に続く、同じ親要素を持つ兄弟要素」を選択する。例えば、li ~ liは、リストの中の2番目以降のすべてのli要素を選択する。
次に、:has(~ li a:hover)という部分だ。これは、「あるli要素が、そのli要素よりも後に続く兄弟li要素の中に、ホバーされているa要素を持っている場合」という条件を示している。
そして、最終的なli:has(~ li a:hover) a全体は、「もし、あるli要素が、自分自身よりも後に続く兄弟li要素の中にホバーされているa要素を持っている場合、その『あるli要素』の中のa要素の背景色をunsetにする」という意味になる。unsetという値は、そのプロパティの値を「初期値」に戻す、あるいは「親から継承される値」に戻す、という働きをする。ここでは実質的に、先に設定したピンク色の背景色を解除し、デフォルトの状態に戻す役割を果たす。
具体的な例で考えてみよう。 例えば、リストに「One」「Two」「Three」という項目があり、「Two」にマウスがホバーされたとする。
- 「One」の
li要素を考える。このli要素より後に続く兄弟li要素(「Two」のliと「Three」のli)の中に、ホバーされているa要素(「Two」のa)が存在する。 - したがって、「One」の
li要素は:has(~ li a:hover)の条件を満たす。 - 結果として、
li:has(~ li a:hover) aのルールが適用され、「One」のliの中のa要素(つまり「One」のリンク)の背景色がunsetになり、ピンク色が解除される。
同様に、「Three」にマウスがホバーされた場合、
- 「One」の
liは、後に続く兄弟li(「Two」「Three」)の中にホバーされたa(「Three」のa)を持つため、その中のa(「One」のリンク)の背景色がunsetになる。 - 「Two」の
liは、後に続く兄弟li(「Three)の中にホバーされたa(「Threeのa)を持つため、その中のa(「Two」のリンク)の背景色がunsetになる。
この仕組みにより、ホバーされているリンクより「前」にあるすべてのリンク(最初のリンクを含む)の背景色がunsetとなり、デフォルトの状態に戻る。ホバーされているリンク自体にはli a:hoverのルールが適用され続けるため、結果的にホバーされているリンクだけがピンク色になり、他のリンクはデフォルトの状態に戻るという、期待通りの動作が実現される。
この一連のルールを組み合わせることで、
- デフォルト状態: どのリンクもホバー/フォーカスされていないときは、
li:first-of-type aのルールにより「One」のリンクだけがピンク色になる。 - ホバー/フォーカス時: 「Two」や「Three」など、いずれかのリンクにマウスが重なったりキーボードで選択されたりすると、
- まず
li a:hoverまたはli a:focusのルールにより、そのリンク自身がピンク色になる。 - 同時に、
li:has(~ li a:hover) aまたはli:has(~ li a:focus) aのルールにより、ホバー/フォーカスされたリンクより前にあるすべてのリンク(「One」のリンクを含む)の背景色がunsetとなり、ピンク色が解除される。 - 結果として、ホバー/フォーカスされているリンクだけがピンク色に表示され、それ以外のリンクはデフォルトの状態に戻る。
- まず
このように、CSSの三つのルールが協力し合うことで、複雑な条件分岐のような視覚効果が実現できるのだ。
ただし、このアプローチには一つの欠点も指摘されている。それは、リンクからマウスカーソルを外し、その後すぐに別のリンクにマウスを重ねた場合、一瞬だけ最初のリンクがピンク色に「ちらつく」という現象だ。これは、マウスがどのリンクにも重なっていない瞬間に、一時的にli:first-of-type aのルールが再適用されるために起こる。このような細かなユーザー体験の課題は、高度なウェブデザインにおいてしばしば考慮すべき点となる。
この例は、CSSセレクタの持つ表現力と、それらを組み合わせることでどのようなインタラクティブなデザインが可能になるかを示している。特に、:has()セレクタのような新しい機能は、これまでJavaScriptでしか実現できなかったような複雑な条件に基づくスタイリングをCSS単体で可能にするため、システムエンジニアを目指す上ではぜひ押さえておきたい技術だ。ユーザーの操作に合わせてWebページがどのように変化するかを制御する知識は、あらゆるWebアプリケーション開発において基盤となる重要なスキルと言えるだろう。