CSSの部分一致セレクタとカスタムプロパティを組み合わせてコードを簡潔にする。

具体例として、別々の要素のborder-colorbackground-colorを変数で指定するケースを考える。

HTMLとアウトプットイメージ

HTMLは以下の通り。

<body>
  <h1 class="container-of-badge">
    <span class="badge-color-red badge">RED</span>
  </h1>

  <h1 class="container-of-badge">
    <span class="badge-color-blue badge">BLUE</span>
  </h1>
</body>

最終成果物の見た目をこのようにしたい。

CSS

部分一致セレクタとカスタムプロパティを使用しない場合

この見た目を実現するCSSを考える。色関係について部分一致セレクタとカスタムプロパティを使用せずに書くと以下のようになる。

<style>
  :root {
    --badge-radius: 10px;
  }

  .badge-color-red {
    background-color: red;
    color: white;
  }

  .badge-color-blue {
    background-color: blue;
    color: white;
  }

  .container-of-badge {
    border-width: 1px;
    border-bottom-style: solid;
    border-bottom-left-radius: var(--badge-radius);

    &:has(> .badge-color-red) {
      border-color: red;
    }

    &:has(> .badge-color-blue) {
      border-color: blue;
    }
  }

  .badge {
    border-radius: var(--badge-radius);
    display: inline-block;
    padding-inline: 0.25em;
  }
</style>

部分一致セレクタとカスタムプロパティを使用しない場合のデメリットとして、以下のようにredという文字の重複があることが挙げられる。

  .badge-color-red {
    background-color: red;
/* 略 */
    &:has(> .badge-color-red) {
      border-color: red;

セレクタとしてclass名の.badge-color-redを記載するのは最低限必要なことだとしても:has(> .badge-color-red)でもclass名を記載しなければいけない。

またbackground-color, border-colorの両方にredと書かなければいけない(ただしこの点だけであれば--badge-color-red: redとカスタムプロパティを設定・使用すれば解決する)。

これを色のバリエーションの分書いていくと、将来のカラーコード修正が大変になる。

部分一致セレクタとカスタムプロパティを使用する場合

部分一致セレクタとカスタムプロパティを使用したコードをまず提示する。

<style>
  :root {
    --badge-radius: 10px;
    --badge-color: black; /* 初期値 書かなくても問題ない */
  }

  [class*='badge-color-'] {
    background-color: var(--badge-color);
    color: white;
  }

  .container-of-badge {
    border-color: var(--badge-color);
    border-width: 1px;
    border-bottom-style: solid;
    border-bottom-left-radius: var(--badge-radius);

    &:has(> .badge-color-red) {
      --badge-color: red;
    }

    &:has(> .badge-color-blue) {
      --badge-color: blue;
    }
  }

  .badge {
    border-radius: var(--badge-radius);
    display: inline-block;
    padding-inline: 0.25em;
  }
</style>

主な変更点としては以下になる。

  [class*='badge-color-'] {
    background-color: var(--badge-color);
/* 略 */
  .container-of-badge {
    border-color: var(--badge-color);
/* 略 */
    &:has(> .badge-color-red) {
      --badge-color: red;
    }

    &:has(> .badge-color-blue) {
      --badge-color: blue;
    }

[class*='badge-color-']とすることで、badge-color-という文字がclassに含まれている要素を指定できるようにしている。

このおかげで色のバリエーション分(つまり.badge-color-red, .badge-color-blue)定義する必要がなくなり、一箇所でbackground-colorを変数で設定できるようになった。またborder-colorにも同じ変数を設定するため、カラーコード修正の際に変更が一箇所で済む。

変数の設定はhasを利用している。.container-of-badge:has(> .badge-color-red)とすることで、classにbadge-color-redが指定されている親要素である.container-of-badgeでは、--badge-colorが初期値のblackからredになる。

カスタムプロパティはカスケードの対象であり、親から値を継承するため、<h1 class="container-of-badge">の子孫である<span class="badge-color-red badge">RED</span>--badge-colorが初期値のblackからredになる。