0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[CSS] `:has()` 疑似クラスを入れ子にする

Last updated at Posted at 2024-11-01

TL;DR 仕様的に許可されていません

:has() 疑似クラスは他の :has() の中に入れ子にすることはできません。
https://developer.mozilla.org/ja/docs/Web/CSS/:has#構文

ただ、:has() 内の has を削除するだけで凡そ簡単に解消できると思います。

背景と問題例

Web アプリの利用時に、普段は見えなくしておきたい要素が有り、
特定の要素をホバーしたりフォーカスしたりすると見えるようにする、ちょっとトリッキーなユーザースタイルを書いていました。

e.g. .my-header 内の hover、focus 時以外は .target を非表示にする

css
body:has(.my-header:not(:hover)) {
    &:has(.my-header:not(:has(:focus))) { /* has の入れ子あり */
        .target {
            &:not(:hover, :focus, :has(:focus)) { display: none }
        }
    }
}

hover だけだと .target の操作が出来なくなるので、.my-header 内のリンクやボタンなどインタラクティブな要素をドラッグ(focus)した状態でも消えない様にするハックを入れてます。

ただ上記のコードでは残念ながら、仕様のとおり意図したスタイルが適用されません。

解決策

:has() を入れ子しない書き方に修正します。
例の場合では body:has(.my-header:not(:has(:focus)))body:not(:has(.my-header :focus)) とする事で解消できました。

css
body:has(.my-header:not(:hover)) {
    &:not(:has(.my-header :focus)) { /* Fixed */
        .target {
            &:not(:hover, :focus, :has(:focus)) { display: none }
        }
    }
}

結果として CSS も多少シンプルになって読みやすくなりました。
仕様としてもその辺を見越しての物なのかなと思いました。

/* Before */
body:has(.my-header:not(:has(:focus))) {}

/* After */
body:not(:has(.my-header :focus)) {}

挙動の例は以下の JSFiddle で確認できます。

https://jsfiddle.net/4cj7xmo9/
https://jsfiddle.net/5yj1sa07/ (下記に関連する不要な行・コメント削除版)

:has() を入れ子にしてしまうと後続のスタイルに悪影響が出てしまう様なので、
利用の際はその点だけご注意ください。

2024年11月26日時点でバグ?は解消している様です。
何にしても has の入れ子は無効な書き方なのでご注意ください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?