What You Need to Know about Modern CSS (Spring 2024 Edition)
記事は上記記事を意訳したものです。
※当ブログでの翻訳記事は元サイト様に許可を得て掲載しています。
- コンテナクエリー (サイズ)
- コンテナクエリー (スタイル)
- コンテナユニット
- :has() 疑似セレクター
- ビュートランジション
- ネスティング
- スクロール駆動アニメーション
- アンカーポジショニング
- アンカーポジショニングとは?
- スコープ
- カスケードレイヤー
- 論理プロパティ
- Display P3 カラースペース
- カラーミックス
- マージントリム
- text-wrapとは?
- サブグリッド
- サブグリッドとは?
- いつ気にすべきか?
コンテナクエリー (サイズ)
サイズのコンテナクエリーとは? コンテナクエリーを使うと、コンテナ要素の特定のメディア条件(通常は幅の測定値)に合致した場合、そのコンテナ要素の子要素に適用されるスタイルを記述できます。
<div class="element-wrap"> <div class="element"> </div> </div>
.element-wrap { container: element / inline-size; } @container element (min-inline-size: 300px) { .element { display: flex; gap: 1rem; } }
いつ気にすべきか?
「ページ全体ではなく、この要素のサイズに基づいてスタイル決定ができないかな」と思ったことがある人は、@container クエリーを使うとよいでしょう。デザインシステムや、コンポーネントベースのウェブサイトで作業する人は、おそらくコンポーネントのサイズに基づいてスタイルを当てるために、ほとんどの場合 Container Queries を使うことになるでしょう。ページ全体のサイズはコンポーネントのサイズを代用するには適していないからです。
サポート状況
ブラウザサポート: 一部
プログレッシブエンハンスメント: 潜在的に - スタイルを当てる対象がクリティカルでない場合はできます
ポリフィル: 可能
基本的な使用例
真ん中のサイズ調整ツールを使って、カレンダーがスペースに応じてレイアウトを変更するのを確認してください。カレンダーには独自の3つの breakpoint があります。
コンテナクエリー (スタイル)
スタイルのコンテナクエリーとは? スタイルのコンテナクエリーを使うと、指定されたカスタムプロパティが指定された値を持つ場合にスタイルを適用できます。
.container { --variant: 1; &.variant2 { --variant: 2; } } @container style(--variant: 1) { button { } /* .containerはスタイル指定できないが、その内側は選択可能 */ .other-things { } } @container style(--variant: 2) { button { } .whatever { } }
いつ気にすべきか?
CSSでミックスインが使えたらいいなと思ったことはありませんか? つまり、1つのプロパティを設定すると、複数のプロパティが設定されるようなものです。Sassではミックスインがかなり一般的でした。スタイルのコンテナクエリーを使えば、同様のことができます。ただし、Sassに変数があった後にCSSの変数のほうが強力で便利だったように、スタイルのコンテナクエリーのほうがさらに強力で便利になる可能性があります。なぜなら、カスケードを尊重し、計算することもできるからです。
サポート状況
ブラウザサポート: ✅ Chrome 🔜 Safari ❌ Firefox
プログレッシブエンハンスメント: 潜在的に - スタイルを当てる内容次第ですが、ほぼ不可能 と言えます。 ポリフィル: 不可能
基本的な使用例
コンテナユニット
コンテナユニットとは?
コンテナユニット(文字通り、px、rem、vwのような単位)を使うと、コンテナ要素の現在のサイズに基づいて、物の大きさを設定できます。ビューポートユニットの 1vw がブラウザウィンドウ幅の1%を表すのと同様に、1cqw はコンテナの幅の1%を表します(ただし、cqi(「論理インライン」の意味)を使うことをおすすめします)。
使用可能なユニットは、cqw (「コンテナクエリ幅」)、cqh (「コンテナクエリ高さ」)、cqi (「コンテナクエリインライン」)、cqb (「コンテナクエリブロック」)、cqmin (cqi と cqb の小さい方)、cqmax (cqi と cqb の大きい方) です。
いつ気にすべきか?
要素内の何かのサイズを決める際に、コンテナの現在のサイズに基づくべき場合、コンテナユニットを使うのが本質的に唯一の方法です。その一例がタイポグラフィです。典型的なカード要素では、大きくレンダリングされた場合にヘッダーテキストを大きくするのが適切な場合があり、そのためにクラス名を追加する必要はありません(このデモが好例です)。比較的すると、コンテナクエリーを使うのは面倒です。
サポート状況
ブラウザサポート: 完全 プログレッシブエンハンスメント: 可能 - フォールバックユニットを使った宣言の直後に、コンテナクエリユニットを使った宣言を書けばよい ポリフィル: 可能
基本的な使用例
この要素では font-size だけでなく、padding、border、margin もコンテナクエリユニットを使用しています。
:has() 疑似セレクター
:has() セレクターとは?
:has() セレクターを使うと、元の要素のDOM木の中でより深い位置にある要素が :has() の中に指定したセレクターに一致する場合に、条件付きで要素を選択できます。
Copy code figure:has(figcaption) { border: 1px solid black; padding: 0.5rem; }
いつ気にすべきか?
CSSで「親要素」を選択できたらいいなと思ったことがある人にとって、:has() はそれができる上に、選択した親要素の中をさらに掘り下げられるので、より強力です。Jhey Tompkinsは :has() のことを「家族セレクター」と呼んでいますが、それは考え方としてわかりやすいでしょう。また、:not() と組み合わせて、要素の内側に一致する要素が存在しない場合のセレクターを作ることもできます。
3 サポート状況
ブラウザサポート: 完全 プログレッシブエンハンスメント: スタイルを当てる内容次第ですが、ほぼ不可能 と言えます。 ポリフィル: JavaScriptの部分のみ可能
基本的な使用例
ビュートランジション
ビュートランジションとは?
ビュートランジションには2種類あります。
- 同一ページ内のトランジション (JavaScriptが必要)
- 複数ページ間のトランジション (CSSのみで可能)
どちらも便利です。同一ページ内のトランジションは、ページ遷移せずにDOMが変更された場合(リストのソート等)のアニメーションに関係します。複数ページ間のトランジションは、ページ読み込み間での要素のアニメーション(動画サムネイルから動画要素へのトランジション等)に使われます。同一ページ内のトランジションの基本的な構文は次のとおりです。
if (!document.startViewTransition) { updateTheDOM(); } else { document.startViewTransition(() => updateTheDOM()); }
複数ページ間のトランジションでは、次のmetaタグが必要です。
<meta name="view-transition" content="same-origin">
そして、ページ間でトランジションさせたい要素には、出力ページと入力ページの両方で、完全に一意の view-transition-name をスタイルで適用する必要があります。
いつ気にすべきか?
要素が新しい位置に移動するアニメーションがあると、ユーザーはインターフェースをより理解しやすくなります。アニメーションに tweening という概念があり、開始状態と終了状態に基づいてアニメーションが自動的に作成されます。ビュートランジションは本質的にtween ingです。アニメーションの側面を制御できますが、ほとんどの場合、アニメーションの詳細を指定する必要はなく、DOMの開始状態と終了状態に基づいてアニメーションが自動的に作成されます。
サポート状況
ブラウザサポート: ✅ Chrome ❌ Safari ❌ Firefox
プログレッシブエンハンスメント: 可能 - トランジションを実行しないか、フォールバックのアニメーションを用意します。 ポリフィル: 不可能
基本的な使用例
これは同一ページ内のビュートランジションの例です。
ネスティング
ネスティングとは?
ネスティングは、CSSの記述方法の1つで、既存の ルールセット の中に追加のセレクターを記述できます。
.card { padding: 1rem; > h2:first-child { margin-block-start: 0; } footer { border-block-start: 1px solid black; } }
いつ気にすべきか?
ネスティングはおもにCSS記述の便利機能ですが、関連するCSSをまとめてグループ化でき、セレクターの重複記述を防げるので、ミスを避けてCSSの可読性を高められます。一方で、ネストされたCSSは必要以上にHTMLのネストに合わせてCSSを記述するようになり、特異性が高くなり再利用性が下がる"足かせ"になる可能性もあります。
.card { container: card / inline-size; display: grid; gap: 1rem; @container (min-inline-size: 250px) { gap: 2rem; } }
Sassスタイルのネスティングとの主な違いは、&を直接組み合わせられない点です。
.card { body.home & { /* 問題なし */ } & .footer { /* 問題なし、&すら不要 */ } &__big { /* だめ、これはできない */ } }
サポート状況 ブラウザサポート: 完全 プログレッシブエンハンスメント: 不可 ポリフィル: LightningCSS、Sass、Less等のプロセッサを使えばできます。
スクロール駆動アニメーション
スクロール駆動アニメーションとは?
要素(多くの場合はページ自体)のスクロールに連動するアニメーションは、JavaScriptでDOMスクロールイベントをバインドする必要なく、CSSで実現できるようになりました。大きく分けて2種類あります。
- 要素のスクロール進行状況に基づくアニメーション (animation-timeline: scroll())
- 要素の現在の可視位置に基づくアニメーション (animation-timeline: view())
いつ気にすべきか?
ページを下にスクロールするにつれて0%から100%まで塗りつぶされる読書進捗インジケーターバーを想像してください。これは、要素の background-position をページ全体のスクロール位置に連動してアニメーションさせることで実現できます。JavaScriptではなくCSSでこれを行うと、パフォーマンスが向上します。
スクロール駆動アニメーションのもう1つの主要なユースケースは、要素がビューポートに入った(または出た)際にアニメーションを実行することです。要素がどの程度可視かに基づいてアニメーションの開始・終了タイミングなどを詳細に制御できます。
サポート状況
ブラウザサポート: ✅ Chrome ❌ Safari 🔜 Firefox プログレッシブエンハンスメント: 可能 - これらの効果は視覚的なちょっとした演出で、必須の機能ではありません。 ポリフィル: 可能
基本的な使用例
これは画像のズームとページスクロールについて見た際のデモです。
アンカーポジショニング
アンカーポジショニングとは?
アンカーポジショニングを使うと、別の要素の位置を基準に項目を配置できます。そう言われるとごく当然のことに聞こえますが、それがアンカーポジショニングの役割です。要素をアンカーと宣言し名前を付けると、そのアンカーの上/右/下/左(またはセンター、論理的な同等物)に別の要素を配置できます。
いつ気にすべきか?
これが自由に使えるようになれば、要素の正確なDOM上の位置をあまり気にする必要がなくなります(アクセシビリティの懸念は別として)。現状では、ある要素を別の要素の相対位置に配置したい場合、基準となる要素は 子要素 でなければならず、ポジショニングコンテキストの中で動作する必要があります。このため、要素のDOM上の位置が意味をなさない場合でも、DOM構造を無理やり変更しなければならないことがあります。
主なユースケースはツールチップやカスタムのコンテキストメニューになるでしょう。
サポート状況
ブラウザサポート: 🔜 Chrome ❌ Safari ❌ Firefox プログレッシブエンハンスメント: 場合によっては可能 - 要素の位置が全く異なっても許容できる場合 ポリフィル: 可能
基本的な使用例
この記事を公開した時点では、Chrome Canaryで "Experimental Web Platform Features" フラグを有効にした場合のみ動作します。
スコープ
スコープ付きCSSとは?
CSSのスコープは @scope アトルールで宣言され、指定されたセレクターに対してのみCSSブロックが適用されます。さらに、別のセレクターを指定すれば、そこまでしかCSSが適用されなくなります。
@scope (.card) to (.markdown-output) { h2 { background: tan; /* Markdownに至るまでこの設定を適用 */ } }
いつ気にすべきか?
クラスを適用してそのクラスの中にネストさせることでも、CSSにスコープを設定できます。しかし、@scope にはいくつかの特徴があり、興味深い機能があります。「ドーナツスコープ」という独自の機能があります。
@scope (.card) to (.markdown-output) { h2 { background: tan; /* Markdownに至るまでこの設定を適用 */ } }
もう1つの便利な機能は、より論理的な近接性によるスタイル付けができることです。これは説明が難しいですが、一度見れば理解できるでしょう。テーマ設定を考えてみましょう。.darkセレクターと.lightセレクターがあるとします。ページ全体で常に1つしか使わない場合は問題ありませんが、これらをネストさせる場合、特異性が同じであるため、後に定義したほうが優先されます。これは常に意図したとおりの動作とは限りません。最小限の例を示します。
.light { background: white; color: black; } .dark { background: black; color: white; }
<div class="light"> <div class="dark"> <div class="light"> some text </div> </div> </div>
実際のテキストがある場所は "light" ですが、適用されるスタイルは "dark" になります。これは扱いづらいですが、@scope を使えば修正できます。スコープ付きセレクターが一致すると、Bramus の言葉を借りれば 「スコープのルートからの近接性で両方のセレクターを比較する」ため、ここでは "light" のほうが近いので優先されます。
私が最も気に入っているのは、DOMに <style> タグを挿入し、そのDOMの一部分にスコープ付きスタイルを適用できる点です。何も名前を付ける必要がありません。
<div class="my-cool-component"> <style> @scope { :scope { /* クラスなどで指定することなく、上のdivを選択 */ } .card { } } </style> <article class="card"> </article> </div>
サポート状況
ブラウザサポート: ✅ Chrome ✅ Safari ❌ Firefox
プログレッシブエンハンスメント: 不可 ポリフィル: 不可
基本的な使用例
カスケードレイヤー
レイヤーとは?
CSSのカスケードレイヤーは、スタイルの塊の強さに影響を与える極めて強力な構文です。レイヤーに名前を付けて順序付けることができます(明示的に順序付けしない場合は、ソース順に並びます)。上位レイヤーのスタイルは、セレクターの強さに関係なく、下位レイヤーのスタイルに自動的に勝ちます。レイヤー外のスタイルが最も強力です。
<body id="home">
@layer base { body#home { margin: 0; background: #eee; } } body { background: white; }
通常、body#home は はるかに 強力なセレクターと考えられるため、背景色は #eee になると思われがちです。しかし、ここにはレイヤー外のスタイルがあるため、それが勝ち、背景色は white になります。
レイヤーは好きなだけ持てます。また、前もって順序付けることができます。新規プロジェクトでは、レイヤー化がベストプラクティスになる可能性が高く、次のような形になると思われます。
@layer reset, default, themes, patterns, layouts, components, utilities;
注意点として、下位 レイヤーの !important ルールは実際には より 強力になります。
いつ気にすべきか?
CSSレイヤーから大きな価値を得られる明確な例は、サードパーティのスタイリングライブラリを使用するプロジェクトです。そのライブラリを自チームのスタイルより下位のレイヤーに置けば、セレクターの強さで ライブラリと戦う 必要がなくなります。上位レイヤーの自チームのスタイルが常に勝つため、より洗練され、メンテナンス性の高いCSSを作れるはずです。
たとえば、Bootstrapすべてを layer キーワードを使って下位レイヤーに置けば、その後に書いたスタイルは、Bootstrapが強力なセレクターを使っていても勝ちます。
@import url("https://cdn.com/bootstrap.css") layer; h5 { margin-bottom: 2rem; }
サポート状況 ブラウザサポート: 完全 プログレッシブエンハンスメント: 不可 ポリフィル: 可能
基本的な使用例
論理プロパティ
論理プロパティとは?
論理プロパティは、方向を指定するプロパティの代替です。たとえば、英語のような左から右の言語では、inline 方向は水平で block 方向は垂直なので、margin-right は margin-inline-end に、margin-top は margin-block-start に相当します。一方、アラビア語のような右から左の言語では、margin-inline-end は margin-left に相当するようになります。これは、要素のインラインフローの終わり側がそちら側になるためです。このように方向の要素を持つCSSプロパティと値は多数あるため(border、padding、offset、setなど)、inline と block のフローを理解し、正しい start または end の値を使うことがコツです。
いつ気にすべきか?
CSSで方向情報を宣言する際、しばしば意図しているのは「テキストのインライン方向」ということです。これは奇妙に聞こえるかもしれませんが、アイコンとテキストの間の余白を想像してください。アイコンに margin-right を適用してテキストから離せば良いように思えますが、ページが右から左の言語に翻訳された場合、余白がアイコンの 反対側 に入ってしまいます。意図していたのは、アイコンに margin-inline-end を適用することでした。このように論理プロパティを使ってコーディングすれば、追加の条件分岐を書く必要なく自動的に翻訳されます。
サポート状況
ブラウザサポート: 完全
プログレッシブエンハンスメント: 可能 - @supports と unset を使ってフォールバック値を削除し、論理プロパティで上書きする必要はあります。 ポリフィル: 処理オプションはあるようですが、確認できていません。
基本的な使用例
Display P3 カラースペース
Display P3 カラースペースとは?
ウェブでは主に sRGB カラースペースに慣れ親しんできました。16進数の色や rgb()、hsl()、hsb() 関数はすべてこのカラースペースを使用しています。現在の多くのディスプレイは、sRGB が表現できる範囲をはるかに超える色を表示可能です。そのため、sRGB に限定されるのは残念です。Display P3 カラースペースは sRGB より約50%広く、より鮮やかで艶やかな色が表現できます。新しい CSS 関数を使えば、これらの色を宣言でき、さらに独自の有用なプロパティを持つ別の カラーモデル を使うこともできます。
いつ気にすべきか?
かなり鮮やかな色を使いたい場合は、P3 カラースペースの色を使う必要があります。新しいカラーモデル(および関数)を使えば可能で、さまざまな用途で役立ちます。
たとえば、oklch() 関数(および OKLCH カラーモデル)は、他のどの方法でも表現できる(さらに P3 の)色を表示でき、hsl() と同様の人間にとっての可読性が高く、「知覚される明るさが一定」なので、最初の数値(lightness)の振る舞いが hsl() よりもはるかに予測可能です。ウェブの色にとってこれは大変うれしいことです。しかし、新しいカラーモデルと関数はこれだけではありません。私はグラデーションには一般に oklab カラーモデルが最適だと思っています。
サポート状況
ブラウザサポート: 完全 (例: oklab)
プログレッシブエンハンスメント: 可能 - フォールバックの色を宣言しておけば、指定した色を表示できないディスプレイでは範囲内の色に落ち着きます。 ポリフィル: 可能
基本的な使用例
これらの<style>ブロックは display: block; と contenteditable に設定されているので編集できます:
カラーミックス
color-mix()とは?
CSS の color-mix() 関数を使えば、ご期待通り、色を混ぜ合わせることができます。この種の機能は、以前からCSSプロセッサツールに組み込まれていましたが、CSSの進化に伴い、ネイティブのCSSに取り込まれた今、より洗練され強力になっています。
いつ気にすべきか?
すでに持っている色を手動で明るくしたり暗くしたりしたいと思ったことはありませんか? それが color-mix() のできることの1つです。カラーミックスは特定のカラーモデル内 で行われるため、そのモデルの特性を活かすことができます。たとえば、OKLCHの知覚される明るさが一定なので、明るさの調整に使うのが適切です。color-mix() を使えば、カラーパレット全体を作成できます。
ブラウザサポート
ブラウザサポート: 完全
プログレッシブエンハンスメント: 可能 - フォールバックの色を宣言できます ポリフィル: できるかもしれませんが、私は知りません。
基本的な使用例
マージントリム
margin-trimとは? margin-trimプロパティは、指定した方向の選択したコンテナの 末尾 にある余分なマージンを削除します。5つのブロックが1列に並んでおり、すべてにコンテナ内で右マージンが設定されている場合を想像してください。通常は :last-child を選択して右マージンを削除しますが、margin-trim を使えば、親要素自体からマージンを削除できます。
.container { /* 要素の末尾にある"余分な"マージンを防ぐ */ margin-trim: block-end; /* こういった要素が原因になるかもしれませんが、何でもありえます */ > p { margin-block-end: 1rem; } }
いつ気にすべきか?
FlexboxやGridの gap プロパティがとてもよいのは、要素 間 のみに間隔を入れられることです。しかし、要素間に間隔を入れる必要があるものの gap を使えない場合、margin-trim は大変役立ちます。すべての子要素に方向付きマージンを適用し、最初または最後の要素を選んで余分なマージンを削除する特別なセレクターを気にする必要がないからです。ベストプラクティスになる可能性があります。
サポート状況
ブラウザサポート: ✅ Safari ❌ Chrome ❌ Firefox プログレッシブエンハンスメント: 可能。わずかな余分な余白は大きな問題にはならないでしょう。 ポリフィル: 不可能
基本的な使用例
ここでは最後の段落に設定された下マージンが、他の辺りよりも下側に余分な空間を作り出している状況が見られます。margin-trim を使えば、その最後の段落を選んで手動で削除する必要なく余分なマージンを削り取れます。
テキストラッピング
text-wrapとは?
text-wrapプロパティはあまり記憶になさそうですね。text-wrap: nowrap;は可能ですが、一般にwhite-space: nowrap;を使うと思います。しかし今、text-wrapには2つの新しいトリックがあります。
- text-wrap: balance; - テキストの折り返し時に等幅の行を試みる
- text-wrap: pretty; - 孤立した単語(オーファン)を回避する
いつ気にすべきか?
見出しの1語が次の行に孤立するのは非常にぎこちなく、貧しい組版と見なされる可能性があります。これまでこの問題を解決する良い方法はなく、最後の2語の間に を挿入するなど、несколькоa手間のかかるトリックを使うしかありませんでした。balanceを使えばこの問題を防げますが、さらに複数行のテキストを概ね等幅にできます。一方のprettyは単にオーファンの回避に特化しているため、本文に適しています。
サポート状況
ブラウザサポート: 値による。balanceは全ブラウザですでにサポートされているか、すぐにサポートが追加される予定です。prettyはそうでもありません。 プログレッシブエンハンスメント: 可能。若干見栄えが悪くなりますが、ウィドウやオーファンはそれほど大きな問題ではありません。このプロパティが動作しなくても大したことありません。 ポリフィル: 可能
基本的な使用例
サブグリッド
サブグリッドとは?
サブグリッドは、グリッド化された要素をネストする際に関係するCSS グリッドの使用における任意の部分です。グリッドレベルの要素に grid-template-columns: subgrid; または grid-template-rows: subgrid; を設定すると、「関連する部分で親グリッドのカラムまたは行を継承する」ということになります。
いつ気にすべきか?
レイアウトにグリッドを使う目的は一般的に 要素を揃える ことです。サブグリッドを使わない場合、グリッドの子要素は親グリッドのグリッド線にアクセスできないため、要素を揃える機会がありません。サブグリッドはこの隙間を埋めます。機能性やアクセシビリティのためにDOMのネスト構造が重要な場合 (例えば