WebサイトのUIに役立つ、CSSの:has()疑似クラスの便利な使い方
Post on:2024年3月14日
CSSの:has()
疑似クラスは2023年暮れにFirefoxでもサポートされ、すべての主要ブラウザでサポートされました。今までのCSSでは、要素の存在のあり・なしによって特定の親や要素にスタイルを設定することは不可能でしたが、:has()
疑似クラスのおかげで、指定した要素がある場合にのみスタイルを適用できるようになりました。
WebサイトのUIでよく見かけるCSSの:has()
疑似クラスの便利な使い方を紹介します。今までは少し面倒なCSS、もしくはJavaScriptが必要でしたが、:has()
疑似クラスを用いると簡単に実装できます。
Some little ways I’m using CSS :has() in the real world
by Andy Bell
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
この記事では、私が実際にクライアントのプロジェクトで使用している:has()
の使用例を解説したいと思います。
その前に、:has()
疑似クラスについて少しだけ。わたし達がCSSで求めてきたものは、親要素を選択できることです。:has()
の便利なメンタルモデルは、子要素から親要素を選択するのではなく、親の子要素の状態や存在をクエリすることです。これを私は好ましく思い、とても理にかなっています。
ただし、:has()
が他の人が言うほど特効薬みたいなものであるとは思っていません。私個人はCUBE exceptionsで定期的に使用していますが、the studioで取り組んでいるプロジェクトではマークアップへのアクセスが制限されないという恵まれた立場にもいます。:has()
はちょっとした調整にも便利ですが、マークアップにアクセスできない場合こそ特効薬になると思います。
前置きはここまでにして、さっそく私が最近のプロジェクトでどのように:has()
疑似クラスを使用してるかを解説します。
:has()
疑似クラスはすでに、すべての主要ブラウザにサポートされています。詳しくは、下記をご覧ください。
1. ボタンがある場合とない場合のレイアウト調整
わたし達がクライアントのために取り組んでいるデザインシステムに、とても分かりやすいバナーがあります。これは最近、新しいパターンが必要になりました。
デフォルトとの違いは、ボタンがあることです。:has()
を使用して、ボタンがある場合はFlexレイアウトを適用するようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.banner { background: var(--color-primary); color: var(--color-light); font-weight: var(--font-bold); text-align: center; } .banner:has(button) { display: flex; justify-content: space-between; gap: var(--space-s); text-align: revert; } |
上がデフォルトで、下がボタンがある場合のレイアウトです。
See the Pen
Banner with :has() by coliss (@coliss)
on CodePen.
2. フォームのレイアウト調整
このコンテキストでは、label
をinline
要素として使いたいため、それに続くフォームフィールドは改行する必要があります。
1 2 3 4 |
label::after { content: "\A"; white-space: pre; } |
ただし、チェックボックスやラジオボタンなどを含むlabel
では、それらをFlexboxでレイアウトすると便利です。これまではクラスを追加する必要がありましたが、:has()
疑似クラスのおかげでグローバルスタイルに設定しています。
1 2 3 4 5 |
label:has(input) { display: flex; align-items: flex-start; gap: var(--space-s); } |
:has()
の値をinput
だけにしている理由は、label
内にinput
を入れないからです。入れる場合は、セレクタを下記のようにしてください。
1 |
label:has(:is(input[type="checkbox"], input[type="radio"])) |
実際の動作は、デモページをご覧ください。
See the Pen
Label has input by coliss (@coliss)
on CodePen.
3. 子がターゲットになったときに親をハイライト
これは非常に簡単ですが、効果的です。id
を持つ要素がある場合、URLに#
を付けてid
を追加すると、:target
をトリガーすることができます。
これまでは要素の親がターゲットになっている場合にスタイルを適用できませんでしたが、:has()
疑似クラスを使えば簡単に実装できます。
1 2 3 4 |
section:has(:target) { background: var(--color-light-shade); border: 2px solid var(--color-primary); } |
下記のデモでは、2番目のsection
にターゲットされています。
See the Pen
CSS :has() with :target by coliss (@coliss)
on CodePen.
4. 要素がホバーされたら兄弟を暗くする
これは昔からあるデザインパターンですが、このアイデアは、ある要素にカーソルを合わせると、その兄弟要素すべてがすこし暗くなり、ユーザーの視覚的なフォーカスをターゲットにより向かせられるというものです。
これまでもCSSだけで実現することは可能でしたが、その効果を得るためのセレクタはかなり複雑でした。また、カーソルが溝にある場合にちらついたり、すべての要素が暗くなってしまったりしました。
しかし、:has()
疑似クラスを使えば、そんなことはありません!
1 2 3 |
.tiles:has(:hover) .tile:not(:hover) { opacity: 70%; } |
このセレクタの利点は、何が起こっているかが非常に明確に分かることです。
See the Pen
Tiles with :has() by coliss (@coliss)
on CodePen.
終わりに
この記事で紹介したテクニックは、複雑ことや派手なことは一切ありません。実際に:has()
疑似クラスを使用する便利な方法を紹介したかっただけです。
最後に、ちょっとしたテクニックを。
コンテンツ内の最大幅を制限していて、画像など特定の要素だけその最大幅を超えて配置したい場合、:has()
疑似クラスを使うと便利です。
1 2 3 |
.post p:has(code-pen) { max-width: unset; } |
簡単で便利ですね😄
sponsors