CSS 選択のスタイル設定の継承の変更

公開日: 2024 年 10 月 8 日

Chrome 131 より、::selection 疑似クラスと ::target-text 疑似クラスの CSS ハイライトの継承が変更されます。これは、継承のためのより直感的なモデルを作成し、最近追加された ::highlight::spelling-error::grammar-error 疑似クラスに合わせるためです。この投稿では、ほとんどのサイトに目に見える影響を与えない変更について説明します。

選択範囲のスタイル設定

選択したテキストの外観をスタイル設定することで、選択したコンテンツの目的や、テキストをまったく選択できないことなどの意味をユーザーに伝えることができます。たとえば、GitHub では、選択したコードの色が選択したディレクトリ構造とは異なります。

CSS は、ハイライト疑似要素と呼ばれる一連の疑似要素の 1 つである ::selection 疑似要素による選択スタイルをサポートしています。これらの疑似要素は、さまざまなユーザー、ブラウザ、スクリプトによるアクションでテキストが表示される方法を制御します。選択以外にも、スペルミス(::spelling-error)、文法ミス(::grammar-error)、URL 埋め込みテキスト ターゲット(::target-text)、スクリプト生成ハイライト(::highlight)のスタイルを設定できます。

他の CSS プロパティのコレクションと同様に、継承動作はサイトの設計時に考慮すべき重要な要素です。一般に、CSS プロパティは DOM 要素ツリーを介して継承されるか(font など)、まったく継承されないか(background など)のいずれかであると想定されます。

Chrome 131 での選択動作の変更

次のドキュメント フラグメントについて考えてみましょう。

p {
  color: red;
}

.blue::selection {
  color: blue;
}
<p class="blue">Some <em>emphasized</em> text that one would expect to be blue</p>

フラグメントのスタイル宣言は、選択したテキストの色を変更します。1 つのルールはすべての要素に一致し、もう 1 つのルールはクラス "blue" の要素に一致します。Chrome 130 以前で選択した場合、結果は次のようになります。

青色と想定されるテキストが赤色になっている。

Chrome 131 で選択すると、結果は次のようになります。

テキストが青色でハイライト表示されます。

変更点として、選択プロパティの継承動作は、これまでは元の要素の継承によって実装されていました。この場合、選択は、選択された要素に一致する ::selection のプロパティを使用します。Chrome バージョン 130 以前では、このモデルが使用されます。このモデルでは、.blue::selection"blue" クラスの要素のみと一致するため、強調表示されたテキストには一致する ::selection がありません。<em> 要素には "blue" クラスがありません。

Chrome 131 では、要素が親から選択動作を継承する新しい動作が有効になります。上の例では、<em> 要素には自身と一致する ::selection がないため、<p> 要素の選択色を継承します。これは CSS ハイライトの継承と呼ばれ、chrome://flags試験運用版のウェブ プラットフォームの機能を有効にすることで、以前の Chrome バージョンで試すことができます。

選択プロパティの継承に依存しているサイトでは、選択したテキストの外観が変更される可能性がありますが、バグレポートから得られた証拠によると、このような動作のユースケースはほとんどありません。

選択用の CSS カスタム プロパティは引き続き機能する

多くのサイトでは、CSS カスタム プロパティを使用して CSS ハイライトの継承をシミュレートしています。カスタム プロパティは要素ツリーを介して継承され、次のコード スニペットのように「親から継承」の結果が得られます。

:root {
   --selection-color: lightgreen;
}

::selection {
  color: var(--selection-color);
}

.blue {
  --selection-color: blue;
}
<p>Some <em>emphasized</em> text</p>
<p class="blue">Some <em>emphasized</em> text that is blue</p>

Chrome 130 と 131 の両方で選択した場合の結果は次のとおりです。

最初の行は緑色、2 番目の行は青色です。

ここでは、すべての要素が要素ツリーを介して --selection-color プロパティの値を継承し、この色がテキストの選択時に使用されます。.blue クラスの要素とその子孫は、選択すると青色になり、他の要素は薄緑色になります。多くのサイトでこの手法が使用されており、Stack Overflow でも推奨されています。

互換性を維持するため、CSS ハイライト継承モデルでは、::selection(および他の CSS ハイライト擬似要素)が、元の要素(適用される要素)からカスタム プロパティ値を継承するように指定されています。この方法を使用しているサイトは、Chrome 131 の変更の影響を受けません。

::selection 疑似要素自体で定義されたカスタム プロパティは無視され、競合する継承動作を回避します。要素自体でプロパティを定義し、疑似要素で参照する必要があります。

::selection のユニバーサル セレクタでハイライトの継承を無効化

CSS カスタム プロパティを使用していないサイトでは、ユニバーサル セレクタを使用して選択したテキストの色を設定していた可能性があります。たとえば、次の CSS のようにします。

::selection /* = *::selection (universal) */ {
  color: lightgreen;
}

.blue::selection {
  color: blue;
}
<p>Some <em>emphasized</em> text</p>
<p class="blue">Some <em>emphasized</em> text</p>

Chrome 130(以前)と Chrome 131(以降)の両方で選択した場合の結果は次のとおりです。

最初の行のテキストは緑色です。2 つ目は青色ですが、強調された単語は緑色です。

CSS ハイライトの継承では、2 番目の強調テキストが親から青色を継承することはありません。これは、ユニバーサル セレクタが <em> 要素と一致し、ユニバーサル ハイライト色(ライトグリーン)が適用されるためです。

CSS ハイライトの継承のメリットを享受するには、ユニバーサル セレクタを変更してルートのみと一致するようにします。このルートは、その子孫に継承されます。

:root::selection {
  color: lightgreen;
}

.blue::selection {
  color: blue;
}
<p>Some <em>emphasized</em> text</p>
<p class="blue">Some <em>emphasized</em> text</p>

Chrome 131 での結果は次のようになります。

最初の行のテキストは緑色です。2 行目は青色です。

サイトが選択色を変更しているが、カスタム プロパティを使用していない場合は、::selection 疑似クラスのユニバーサル セレクタが存在している可能性があります。幸い、Chrome のこの変更によってサイトが破損することはありません。ただし、ハイライトの継承による人間工学的なメリットは失われます。

::target-text のスタイルも変更されます

ここで説明する動作と変更はすべて、::selection と同様に ::target-text 疑似要素にも適用されます。1 つのサイトで複数のターゲット テキストのスタイル設定を使用するユースケースは限られており、この機能は非常に新しいため、サイトの ::target-text の動作が変更される可能性はほとんどありません。

変更の理由

他のハイライト疑似要素の開発中、CSS ワーキング グループは、ハイライト継承モデルを使用して継承を実装することを決定しました。これは、::selection 疑似要素の仕様ですでに定義されていた方法ですが、ブラウザでは実装されていませんでした。選択以外の疑似要素はハイライトの継承を使用します。この場合、疑似要素はプロパティのように継承されます。つまり、要素はドキュメントの親からハイライト疑似要素を継承します。

すべてのハイライト疑似クラスの一貫性を保つため、CSS ワーキング グループは ::selection のハイライト継承のサポートを再確認しました。ブラウザは、既存のサイトを壊さないようにしながら、新しい動作をリリースするために取り組んでいます。

試してみる

次の CodePen は、変更内容を示しています。Chrome 131 で試してみてください。