このシリーズでは、CSS の重要な概念であるカスケード(Cascade)の基礎を改めて学び直し、その応用としてカスケードレイヤー(@layer)について説明していきます。

前回の基礎編では CSS カスケードの全体像を説明しましたが、今回はそれらの知識を踏まえてカスケードレイヤーの仕組みや revert-layer キーワードの使い方について詳しく見ていきます。

CSS のカスケード(Cascade)は、同一要素に対してスタイルが競合しているときに、優先順位を決めるルールです。

ある要素に指定されたスタイルを上書きしたいときに詳細度を上げることで実現できますが、この詳細度合戦がエスカレートしていくと !important フラグにたどりつきます。これは最終手段であり、確信を持って使用するとき以外は避けるべきです。

CSS の詳細度合戦
<!-- HTML -->
<p>Lorem ipsum</p>
<p class="text">Lorem ipsum</p>
<div id="special">
  <p class="text">Lorem ipsum</p>
  <p class="text alert">Lorem ipsum</p>
</div>

<!-- CSS -->
<style>
/* 詳細度: 0-0-1 😀 */
p {
  color: green;
}

/* 詳細度: 0-1-1 🙂 */
p.text {
  color: teal;
}

/* 詳細度: 1-1-1 😨 */
#special p.text {
  background-color: yellow;
  color: black;
}

/* 最終手段 😱 */
p.alert {
  background-color: red !important;
  color: white !important;
}
</style>

この争いを避けるために、原則クラスセレクタのみを使用するといったような、できるだけ詳細度を低く維持する工夫がされてきました。

しかし、詳細度が同じ場合には後に出現するスタイルが優先されるため、CSS のコードが長くなるほど優先順位の管理が難しくなる課題が依然として残ります。

加えて、サードパーティ製の CSS や、ブログ記事の Markdown のように class を付与することが難しいケースでは、思わぬところで詳細度の取り扱いに苦労することがあります。

カスケードレイヤー(@layer)を使用することで、設計レベルで優先順位を制御することができ、これらの課題を解決できます。

カスケードレイヤーの作成方法

見出し「カスケードレイヤーの作成方法」

カスケードレイヤー(@layer)の仕組み自体は、前回の記事で説明した CSS カスケードの内容が理解できていればそれほど複雑ではありません。

先ほどの CSS コードに対して、カスケードレイヤーを使用して優先順位を整理すると以下のようになります(HTML は同じです)。

カスケードレイヤーで優先順位を整理した例
<!-- HTML -->
<p>Lorem ipsum</p>
<p class="text">Lorem ipsum</p>
<div id="special">
  <p class="text">Lorem ipsum</p>
  <p class="text alert">Lorem ipsum</p>
</div>

<!-- CSS -->
<style>
/* レイヤーの優先順位を定義 */
@layer defaults, components, special, overrides;

/* レイヤーのブロックごとにスタイルを指定 */
@layer defaults {
  p {
    color: green;
  }
}

@layer components {
  .text {
    color: teal;
  }
}

@layer special {
  #special .text {
    background-color: yellow;
    color: black;
  }
}

@layer overrides {
  .alert {
    background-color: red;
    color: white;
  }
}
</style>

レイヤーを使用して優先順位を定義することによって、先ほどのコードで .alert のスタイル指定に付与していた !important は不要になりました。

カスケードレイヤーを作成する方法は、おおまかに以下の 3 パターンに分けられます。

  • @layer ブロックを使用する
  • @layer 宣言文を使用する
  • @import 規則で使用する

順番に見ていきましょう。

@layer ブロックを使用する

この見出しのリンク

まずは、ルールセットを @layer ブロックで囲う方法です。

@layer ブロックの例
@layer reset {
  *,
  ::after,
  ::before {
    box-sizing: border-box;
  }
}

@layer components {
  .btn {
    border-radius: 8px;
  }
}

この例では、resetcomponents という名前のレイヤーを作成しています。

ここで前回のおさらいをすると、カスケードレイヤーでは、後に作成したレイヤーのほうが優先されます。さらに、@layer を使用しない従来のルールセット(Un-Layered)があれば、このレイヤーを使用しない Un-Layered なスタイルのほうが優先されます。

Un-Layered を含めた例
<!-- HTML -->
<p id="foo" class="bar">Lorem ipsum</p>

<!-- CSS -->
<style>
/* Un-Layered */
p {
  color: teal;
}

/* First Layer */
@layer first {
  p#foo {
    color: blue;
  }
}

/* Second Layer */
@layer second {
  p.bar {
    color: green;
  }
}
</style>

この例で、詳細度だけに着目すれば「p#foo > p.bar > p」ですが、レイヤーは詳細度よりも前の段階で評価されるため、優先順位は「Un-Layered > second > first」です。

  • Un-Layered
  • second
  • first

そして、このレイヤーの順番は !imortant フラグを使用すると反転します。

  • first !important
  • second !important
  • Un-Layered !important
  • Un-Layered
  • second
  • first

レイヤーは作成した時点で順位が確立されるため、後から同じ名前のレイヤーを指定しても優先順位が入れ替わることはありません。

後から同じ名前のレイヤーを指定した例
/* First Layer */
@layer first {
  p {
    color: blue;
  }
}

/* Second Layer */
@layer second {
  p {
    color: green;
  }
}

/* First Layer */
@layer first {
  p {
    color: darkslategray;
  }
}

この例では、firstsecond の順にレイヤーを定義して、再び first レイヤーのブロックを記述していますが、すでにレイヤーの順位は確立しているので入れ替わることはなく、second レイヤーの color: green が適用されます。

@layer ブロックのみでレイヤーを作成する方法では、このようにコードの出現順によって優先順位が確立してしまうため、ソースオーダーに依存してしまう課題が残ります。

この課題を解消するために、続いて @layer 宣言文の指定を見ていきましょう。

@layer 宣言文を使用する

この見出しのリンク

@layer 宣言文は、明示的にレイヤーの優先順位を指定する方法です。

@layer 宣言文の例
@layer reset, defaults, layouts, components;

@layer ブロックと同様に、後に指定したレイヤーが優先されるので、このコードの優先順位は以下のとおりです。

  • components
  • layouts
  • defaults
  • reset

通常はこの宣言の後に、@layer ブロックでスタイルを指定します。

@layer 宣言文と @layer ブロックの例
@layer reset, defaults, layouts, components;

@layer reset {
  *,
  ::after,
  ::before {
    box-sizing: border-box;
  }
}

@layer components {
  .btn {
    border-radius: 8px;
  }
}

@layer 宣言文でも、ブロックを使用したときと同様に、一度確立した優先順位を後から入れ替えることはできません。例を見てみましょう。

後から同じ名前のレイヤーを指定した例
@layer reset, defaults, layouts, components;
@layer utilities, components, layouts;

2 回目の宣言で componentslayouts が再び定義されていますが、すでに確立しているため順位は変わらず、新たに宣言された utilities レイヤーのみが追加されます。

このコードの最終的な優先順位は以下のとおりです。

  • utilities
  • components
  • layouts
  • defaults
  • reset

このように、複数箇所で宣言するとレイヤーの優先順位がわかりづらくなるので、@layer 宣言文はソースコードのなるべく先頭に一貫性を持たせて管理するのがよいでしょう。

なお、@layer ブロックの後に @layer 宣言文を指定したり、@media 規則の条件分岐を利用して順位を変えることもできますが、予測がしづらく複雑になるだけなので避けるべきです。

レイヤーの管理を複雑にする悪い見本
/* `@layer` ブロックを記述 */
@layer components {
  /* ... */
}

/* 🙅‍♂️ `@media` 規則の条件によってレイヤーの順位が変わる */
@media (min-width: 1280px) {
  @layer defaults {
    /* ... */
  }
}

/* 🙅‍♂️ `@layer` ブロックの後に宣言文を指定 */
@layer reset, defaults, layouts, components;

デベロッパーツールでレイヤーを確認する

見出し「デベロッパーツールでレイヤーを確認する」

Google Chrome や Microsoft Edge のデベロッパーツールでは、@layer の対象となる要素を選択すると、「Elements」タブの「Styles」に「Toggle CSS Layers view」のボタンが出現します。

このトグルボタンを選択すると、レイヤーの構造や優先順位を確認できます。

Google Chrome デベロッパーツールのスクリーンショット。CSS レイヤービュー切り替えボタンの位置を示している

@import 規則で使用する

この見出しのリンク

最後に @import 規則で読み込んだ CSS にレイヤーを割り当てる方法です。

@import 規則でレイヤーを指定する例
@layer reset, defaults, layouts, libs, components;

@import url('foo.css') layer(libs);

このコードでは、@import 規則の URL 指定の直後の layer() 関数によってインポートした foo.csslibs レイヤーに割り当てています。

レイヤーの優先順位は以下のとおりです。

  • components
  • libs
  • layouts
  • defaults
  • reset

これによって、外部ライブラリやフレームワークなどの、サードパーティ製の CSS に書かれているセレクタの詳細度に関わらず、スタイルの優先順位を明示することができます。

ただ、「Optimize resource loading | web.dev」にあるように、@import 規則を使用した場合、宣言されている CSS のダウンロードが完了するまで読み込みが開始せず、Preload Scanner も有効にならずにパフォーマンスに悪影響を及ぼすので、やむを得ない場合を除いては使用を控えたほうがよいでしょう。

なお、<link> 要素に対してレイヤーを指定する機能の追加については「Allow authors to apply new css features (like cascade layers) while linking stylesheets | Issue #7540 | whatwg/html」で議論されています。

@layer はスタッキングコンテキストには影響しない

この見出しのリンク

「レイヤー」という名称から誤解しやすいのですが、カスケードレイヤーの指定はスタッキングコンテキスト(要素の重なり順)には影響しません。

以下の例では、top レイヤーの優先順位を高くしていますが、重なり順には影響しません。結果的には HTML コードが後方にある .bottom が上に重なります。

<!-- HTML -->
<div class="top">Top</div>
<div class="bottom">Bottom</div>

<!-- CSS -->
<style>
@layer base, bottom, top;

@layer base {
  .bottom, .top {
    inline-size: 10em;
    aspect-ratio: 1;
    color: white;
  }
}

@layer bottom {
  .bottom {
    position: absolute;
    inset-block-start: 40px;
    inset-inline-start: 40px;
    background-color: blue;
  }
}

@layer top {
  .top {
    position: absolute;
    background-color: green;
  }
}
</style>

ここまで 3 パターンのレイヤー定義方法を見てきましたが、レイヤーは名前を指定しなくても作成できます。これらのレイヤーは「Anonymous Layer(匿名レイヤー)」や「Un-named Layer(無名レイヤー)」と呼ばれます。

用語が確立していないので迷いますが、W3C や MDN のドキュメントでは「Anonymous Layer」のほうが多いようなので、本記事では「匿名レイヤー」で統一します。

匿名レイヤーは、以下のように @import 規則、もしくは @layer ブロックで作成できます。

匿名レイヤーの例
@imoprt url('lib.css') layer;

@layer {
  .foo {
    color: blue;
  }
}

@layer {
  .bar {
    color: green;
  }
}

@import 規則の場合には url() の後に layer キーワードを付与し、@layer ブロックの場合には名前を付けずに指定するだけです。

無名レイヤーは、ソースコードで出現した場所で順位が確立されるため、@layer 宣言文でレイヤーの優先順位を定義することができません。

通常のレイヤーの管理では使用するケースは少ないと思われますが、意図的にレイヤーの外側からアクセスできないようにしたり、一時的に優先度を調整するためのテクニックとして使用することができます。

後述する revert-layer を使用したカプセル化の例で、このテクニックを使用しています。

カスケードレイヤーはネストしてサブレイヤーを作成することができます。

ネストしたレイヤーの例
@layer components {
  @layer button {
    .btn {
      /* ... */
    }
  }
}

@layer themes {
  @layer light {
    body {
      /* ... */
    }
  }
  @layer dark {
    body {
      /* ... */
    }
  }
}

レイヤーのネストは、ピリオド(.)で連結して表現できます。上記のコードを書き換えると次のようになります。

ネストしたレイヤーをピリオドで連結した例
@layer components.button {
  .btn {
    /* ... */
  }
}

@layer themes.light {
  body {
    /* ... */
  }
}

@layer themes.dark {
  body {
    /* ... */
  }
}

上記のコードの優先順位は以下のようになります。

  • themes
  • themes.dark
  • themes.light
  • components
  • components.button

また、レイヤー名が同じでも階層が異なれば別のレイヤーとして扱われます。

すべて異なるレイヤーとして扱われる
@layer dark { /* ... */ }
@layer themes { /* ... */ }
@layer themes.dark { /* ... */ }
@layer dark.themes { /* ... */ }

@layer 宣言文を使用して、ネストしたサブレイヤーの順位を定義することも可能です。

@layer 宣言文の例
@layer components, themes;
@layer themes.dark, themes.light;

@layer components.button {
  .btn {
    /* ... */
  }
}

@layer themes.light {
  body {
    /* ... */
  }
}

@layer themes.dark {
  body {
    /* ... */
  }
}

2 行目の宣言文によって、themes.dark より themes.light のほうが優先度が高くなり、先ほどの順位は以下のように変わります。

  • themes
  • themes.light
  • themes.dark
  • components
  • components.button

ここで改めて注目しておきたい点は、ネストの内側のレイヤーよりも、外側のレイヤーのほうが優先度が高いということです(themes.light < themes)。

上記のコードでは、宣言した順番だけを見れば themes.darkthemes.light のほうが後ですが、サブレイヤーが親レイヤーの優先度を超えません。

このことは、@layer を使用しない Un-Layered の指定は @layer を使用した指定よりも優先度が高いというルールと同じです。つまり、原則はレイヤーの外側のほうが優先度が高くなります。

そして、これまでと同様に !important フラグを使用すると順位は入れ替わるので、例外的にサブレイヤーの優先度を親レイヤーよりも高くすることができます。

  • components.button !important
  • components !important
  • themes.dark !important
  • themes.light !important
  • themes !important
  • themes
  • themes.light
  • themes.dark
  • components
  • components.button

前回の基礎編では、オリジンの段階をロールバックする revert キーワードについて説明しましたが、revert-layer キーワードはそのカスケードレイヤー版です。

revert-layer を指定することで、レイヤーをロールバックすることができます。

revert-layer の例
<!-- HTML -->
<article>
  <p>Lorem ipsum</p>

  <aside>
    <p>Lorem ipsum</p>
  </aside>
</article>

<!-- CSS -->
<style>
@layer reset, defaults, layouts, components;

@layer defaults {
  p {
    color: green;
  }
}

@layer layouts {
  article p {
    color: blue;
  }
  aside p {
    color: revert-layer;
  }
}
</style>

上記のコードでは、layouts レイヤーで aside p に対して color: revert-layer を指定しています。これによって defaults レイヤーまで巻き戻り、color: green が適用されます。

  • components
  • 削除 layouts
    revert-layer
  • defaults
  • reset

この仕組みはレイヤーをネストしている場合も同様です。以下は themes.light レイヤーに対して revert-layer キーワードを指定したイメージです。

  • themes
  • themes.dark
  • 削除 themes.light
    revert-layer
  • components
  • components.button

同じ要素に対する競合するスタイルが見つかるまでロールバックしていき、レイヤーに見つからない場合には最終的にはオリジンまで戻ります。

つまり、revert を指定したときのように、User オリジン、User-Agent オリジンとたどっていき、それでも見つからなければ unset を指定したのと同様に扱われます。

revert-layer!important フラグを指定する

この見出しのリンク

さて、以下のように revert-layer キーワードを指定した宣言に、!important フラグを指定するとどうなるでしょうか。

@layer components, themes;
@layer components.button;
@layer themes.light, themes.dark;

@layer themes.light {
  p {
    color: revert-layer !important;
  }
}

以下は themes.light レイヤーに対して、revert-layer!important フラグを指定したときのイメージです。

  • components.button !important
  • components !important
  • 削除 themes.light !important
  • 削除 themes.dark !important
  • 削除 themes !important
  • 削除 themes
  • 削除 themes.dark
  • 削除 themes.light
    revert-layer
  • components
  • components.button

先ほどと異なり直感的ではないですが、!important フラグが指定された themes.light から通常の themes.light までの範囲がすべて取り除かれます。

そのため、この範囲内で !important フラグを使用した強いスタイルがあったとしても、指定が存在しないかのように components レイヤーまでロールバックします。

themes.dark で競合するスタイルに !important を指定しても無効化される
@layer themes.dark {
  p {
    color: darkslategray !important;
  }
}

revert-layer を使用したカプセル化

この見出しのリンク

以下のコードは、Mayank 氏の「Some use cases for revert-layer」というブログ記事で紹介されているテクニックをアレンジしたものですが1revert-layer キーワードを使用して外部のスタイルをカプセル化することができるようです。

revert-layer を使用したカプセル化の例
@layer components {
  my-component {
    /* コンポーネント内でのみ使用するプライベートなレイヤー */
    @layer {
      color: green;
    }

    /* 外部のすべてのスタイルを流入させない指定 */
    all: revert-layer !important;
  }
}

このコードを見ただけでは、なぜコンポーネントの外側のスタイルを防ぐことができるのかを理解するのは難しいのですが2、順番にその仕組みを確認していきます。

上記のコードに検証用のレイヤーとスタイルを追加します。

<!-- HTML -->
<my-component>
  Lorem ipsum
</my-component>

<!-- CSS -->
<style>
@layer components, overrides;

/* 対象となるコンポーネントのレイヤー */
@layer components {
  my-component {
    /* コンポーネント内でのみ使用するプライベートなレイヤー */
    @layer {
      color: green;
    }

    /* 外部のすべてのスタイルを流入させない指定 */
    all: revert-layer !important;
  }
}

/* 検証用のスタイル */
/* これらのスタイルで上書きされないことを確認する */
my-component {
  color: white !important;
  background-color: red !important;
  text-transform: uppercase;
}

@layer overrides {
  my-component {
    color: lightcyan;
    background-color: teal !important;
    text-decoration: line-through;
  }
}
</style>

上記のコードの優先順位は以下のとおりです。なお、components.(anonymous) は匿名レイヤーを指しています。

  • components.(anonymous) !important
  • components !important
  • overrides !important
  • overrides
  • components
  • components.(anonymous)

この状態で componentsrevert-layer キーワードと !important フラグを指定すると、そこから通常の components までの範囲が無効化されます。

  • components.(anonymous) !important
  • 削除 components !important
  • 削除 overrides !important
  • 削除 overrides
  • 削除 components
    revert-layer
  • components.(anonymous)

この動作によって、その下の components.(anonymous) までロールバックします。

そして、この匿名レイヤーで指定していないプロパティ(color プロパティ以外)は、all プロパティの指定によってオリジンまで戻ります。

実際に確認すると、先ほど追加した検証用のスタイルは適用されずに、匿名レイヤーに指定した color: green のみが有効になることがわかります。

なお、先ほどのコードは <my-component> 要素のみを対象としているので、その子孫要素も対象に含めるには、以下のように全称セレクタ(*)を使用します。

子孫要素も含めてカプセル化する例
@layer components {
  my-component,
  my-component * {
    all: revert-layer !important;
  }

  @layer {
    my-component {
      /* ... */
    }
    my-component p {
      /* ... */
    }
  }
}

注意点として、ここで紹介したコードは外部のスタイルの流入は防げますが、内部のスタイルは外部のスタイルへ影響を及ぼす可能性があります。CSS だけでこの課題に対応するには @scope 規則が必要になりますが、現時点では Firefox がサポートしていません。

リセット CSS をピンポイントに適用する

見出し「リセット CSS をピンポイントに適用する」

上記の revert-layer のテクニックを発展させてみましょう。

もし、components.(anonymous) より低いレベルのレイヤーが存在すれば、オリジンに戻るよりも先にそのレイヤーが評価されます。

例えば、先ほどのレイヤー構成の最下層に reset レイヤーを追加することで、リセット CSS のみをピンポイントに適用することができます。

  • reset !important
  • components.(anonymous) !important
  • 削除 components !important
  • 削除 overrides !important
  • 削除 overrides
  • 削除 components
    revert-layer
  • components.(anonymous)
  • reset

このテクニックによって、コンポーネントの外側のスタイルが流入するのを防ぎつつ、リセット CSS をベースに匿名レイヤーにスタイルを書いていくことが可能になります。

CSS 変数(カスタムプロパティ)は無効化されない

見出し「CSS 変数(カスタムプロパティ)は無効化されない」

all プロパティはすべてのプロパティのショートハンドとして使用されますが、例外的に directionunicode-bidi、そして CSS 変数(カスタムプロパティ)は含まれません。

そのため、この revert-layer のテクニックを用いても、CSS 変数は無効化されずに適用されます。

以下のコードでは、Un-Layered に指定している border は無効化されますが、CSS 変数(--color)は有効なままなので、color: blue が適用されます。

CSS 変数は無効化されない
@layer components {
  my-component {
    all: revert-layer !important;
  }

  @layer {
    my-component {
      --color: green;
      color: var(--color);
    }
  }
}

/* Un-Layered */
my-component {
  --color: blue;
  border: dotted 3px;
}

CSS 変数もカプセル化するには、個別に revert-layer を指定します。

CSS 変数に個別に revert-layer を指定
@layer components {
  my-component {
    --color: revert-layer !important;
    all: revert-layer !important;
  }

  @layer {
    my-component {
      --color: green;
      color: var(--color);
    }
  }
}

/* Un-Layered */
my-component {
  --color: blue;
  border: dotted 3px;
}

カスケードレイヤーの課題

見出し「カスケードレイヤーの課題」

ここまで、カスケードレイヤー(@layer)の仕組みや使い方について説明しましたが、運用するにあたっての課題を考えてみます。

トップレベルレイヤーの影響度

見出し「トップレベルレイヤーの影響度」

@layer 宣言文で作成した後のレイヤーの順番を変更すると広い範囲で影響が及ぶ可能性があります。そのため、特にトップレベルレイヤーは慎重に設計し、作成後はレイヤーの生態系が崩れないように注意する必要があります。

もしくは、優先順位を低くしたいリセットやベースとなるスタイルのレイヤーのみを用意して、コンポーネントのようなウワモノに相当するレイヤーはあえて作成しない(Un-Layered にする)といった方針も考えられます。

すべてのスタイルをカスタムレイヤーで管理しているときに、不用意に @layer ブロックを付け忘れてコードを記述していたとしても、そのスタイル自体は問題なく適用されます。

しかし、そのスタイルは Un-Layered のレベルを持つため、意図しない形で強い優先度を持ってしまいます。

レイヤー名のタイプミスによる影響

見出し「レイヤー名のタイプミスによる影響」

@layer ブロックでレイヤー名を誤ると、優先度の強いスタイルが出来上がってしまいます。

@layer reset, defaults, layouts, components, overrides;

/* components(複数形)を component(単数形)とタイプミス */
@layer component {
  button {
    /* ... */
  }
}

この例では、先頭の @layer 宣言文で優先順位を定義しており、そのあとの @layer ブロックで components と複数形で書くところを、誤って component と単数形で指定したため、overrides よりも強いレイヤーが作られてしまっています。

先ほどの @layer ブロックの付け忘れと同様に、CSS の構文上は何の問題もないので、ミスに気づきづらいというのも厄介なポイントです。

従来の @media 規則や @container 規則、今後サポートが広がると考えられる @scope 規則、そして CSS のネスト記法(CSS Nesting)、そしてさらに @layer ブロックが加わると、ネストが深くなりコードの見通しが悪くなる可能性があります。

@layer components {
  @layer button {
    @container (min-inline-size: 600px) {
      @scope (.foo) to (.bar) {
        button {
          svg {
            /* 😵‍💫 */
          }
        }
      }
    }
  }
}

Progressive Enhancement な実装ができない

見出し「Progressive Enhancement な実装ができない」

カスケードレイヤー(@layer)は 2022 年より主要なブラウザでサポートされており、Safari においては 15.4 からサポートしています。

そのため、多くの環境で十分にサポートされていると考えられますが、カスケードレイヤーは CSS 全体の優先順位に関わるので、非対応の環境では表示が大幅に崩れる可能性があります。

PostCSS の polyfill はありますが、Progressive Enhancement な実装はできない点には留意しておいたほうがよいでしょう3

前回の CSS カスケードの全体像の説明に続き、今回はカスケードレイヤー(@layer)の仕組みや revert-layer キーワードの使い方を説明しました。

基本的なカスケードレイヤーの仕組み自体はわりと理解しやすいのですが、実際にどのようなレイヤー構成にすればよいのか、運用時にどのような問題が発生しうるのかといったポイントは、引き続き実践をとおして考えていかなければなりません。

また、revert-layer を使用したカプセル化のテクニックのような、カスケードレイヤーの新たな可能性も探究していきたいです。

本記事の作成にあたり、以下のウェブページを参考にしました。

脚注

  1. さらに元となるアイデアは Nathan Knowler 氏の「So, You Want to Encapsulate Your Styles?」というブログ記事です。

  2. ちなみに、本シリーズを執筆するきっかけはこのコードの仕組みを理解するためでした。

  3. この表現は Chris Coyier 氏のブログ記事「What You Need to Know about Modern CSS (Spring 2024 Edition) | Frontend Masters Boost」を参考にしました。