カラーモード

:has() と :not() を使って親にも子にもリストを持たない単独のリストだけにスタイルを当てる

CSS

マークアップを変更できないライブ会場(現場のこと)において、CSS のタイプセレクタと擬似クラスで対応するのは難解なパズルを解いているようで楽しい。

今回は表題の通り単独のリスト、つまり自身がリスト要素の中に入っておらずかつ子要素にもリストを持たないようなリスト要素にスタイルを当てたい。

<!-- ようはこういったリスト要素である -->
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>

下記のような入れ子リストいずれの階層であってもスタイルを当てたくない。

<!-- 入れ子リスト -->
<ul>
  <li>
    Item 1
    <ul>
      <li>
        Item 1-1
        <ul>
          <li>Item 1-1-1</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Item 1 は子要素にリストを持つ。Item 1-1 は自身がリストの子リストであり、さらに子リストを持つ。Item 1-1-1 は子リストは持っていないが、自身が子リストである。

実食

パッと思いつくのは ul:not(:has(ul)) というセレクタだろう。しかしこれは「子要素に ul を持たない ul 」がマッチするので、自身が子リストであることは考慮されずうまくいかない。

ではさらに親要素の条件を加えて、 :not(li) ul:not(:has(ul)) ならどうだとなるが、これもうまくいかない。

Item 1-1-1 の ul に着目すると、親要素に liul もあるゆえか :not(li) だけでは不十分なようだ。

というわけでファイナルアンサーは :not(li) > ul:not(:has(ul)) である。

li の直接の子要素でなく、かつ子要素に ul を持たない ul をセレクタで表現すれば良いと言うわけだ。今回は ul で書いているがもちろん ol でも動く。

:has() はすごいけど気をつけたい

2023 年 9 月 4 日現在 :has() のブラウザ対応は Firefox を残すのみとなっている。

:has() CSS relational pseudo-class | Can I use... Support tables for HTML5, CSS3, etc

:has() は以前の記事「関係擬似クラス :has() でホバーしている要素の前の隣接要素を指定する」でも紹介したとおりとてもパワフルだ。構造さえわかっていれば識別のための CSS クラスや ID が不要になる。マークアップを変更できないライブ会場や、識別子を付与するためのプロパティをバケツリレーすることが煩雑なライブ現場では大いに力を発揮するだろう。

しかしあくまで構造依存なので、HTML が変わってしまうとスタイルは当たらない。また、スタイリングの全容を知るために HTML と CSS の両方を読む必要があり、慣れない人にとっては :has() のような擬似クラスの利用は障壁となるだろう。 :not() との組み合わせでより顕著になり、複雑なセレクタは理解の妨げになってしまう。

当然だがちゃんと識別子を使ったほうが設計として堅牢だ。:has() を使う他ない場合や、そうする方が既存の設計を壊さない場合など、利用する際は十分な理由を見つけたい。

何事も用法用量を守って、という点は忘れないでいよう。