9月16日に、Safariの最新バージョン16がリリースされました。さまざまな新機能が追加されましたが、今回はその中でも3つのCSSに絞って紹介します。いずれも今後のCSS開発を便利にしてくれるものです。
01 text-align-lastプロパティで1行テキストの両端揃え
text-align-lastプロパティとは、複数行のうち、最終行の配置位置を指定するためのCSSです。本プロパティを使うと、1行テキストの両端揃えがラクに実現できます。
目的のレイアウト
次のレイアウトを考えてみましょう。テーブルレイアウトで、「日時」「会場」「参加費」というタイトルが、両端揃えになっています。
 
 
HTMLは次のようになっています。
▼ HTML
<table>
  <tr>
    <th>日時</th>
    <td>2022年07月26日</td>
  </tr>
  <tr>
    <th>会場</th>
    <td>福岡PayPayドーム</td>
  </tr>
  <tr>
    <th>参加費</th>
    <td>9,800円</td>
  </tr>
</table>
従来のやり方
目的のレイアウトを実現したいとき、 th に対して text-align: justify; を指定すればよいと考えるかもしれません。たしかに、text-align: justify; ではテキストは両端揃えになります。しかし、最終行は両端揃えの対象にはなりません。1行のテキストも複数行テキストの最終行とみなされるので、目的のレイアウトは実現できないのです。
▼ CSS
th {
  text-align: justify;
}
▼ text-align: justify; では目的のレイアウトにならない
 
 
ではどうやっていたかというと、筆者はHTMLで全角スペースを使って無理やり両端揃えを実現していました。開発現場でも、こうやってレイアウトしている例をよく見かけます。
<th>日 時</th>        //全角スペースで調整
<!-- 中略 -->
<th>会 場</th>        //全角スペースで調整
<!-- 中略-->
<th>参加費</th>
本来はCSSで見た目の制御に責任を持つはずが、文書構造のためのHTMLで見た目のコントロールをしてしまうという問題がありました。
text-align-lastプロパティを使えばCSSだけで両端揃えができる
text-align-lastプロパティを使えば、CSSのみで1行テキストの両端揃えが可能です。justifyを指定します。
▼ CSS
th {
  text-align-last: justify;
}
▼ タイトルの両端揃えが実現できた
 
 
実際のデモとコードは、このリンクより確認できます。
text-align-lastプロパティは、Safari 16の対応により、Chrome / Edge / Firefox / Safariの全ブラウザで使えるようになりました。
▼ 関連資料
02 スクロールの連鎖を防げるoverscroll-behaviorプロパティ
overscroll-behaviorプロパティとは、スクロール領域の端に達した際、どのような挙動をするか制御するためのプロパティです。contain値を指定すると、スクロールを連鎖させず、モーダルウインドウを開いたときなどのスクロールの連鎖を防げます。
スクロールの連鎖とは
次のデモを見てください。左上のボタンをクリックすると縦に長いナビゲーションが出てきます。ナビゲーション最下部までスクロールすると、そのスクロールが画面メインのコンテンツまで連鎖してしまっています。モーダルウインドウ内のコンテンツスクロールなどでもよく見かける現象です。
▼ HTML(抜粋)
<main>
  <h1>My Cute Animals</h1>
  <p>🐶</p>
  <p>🐹</p>
  <p>🐵</p>
  <p>🦊</p>
  <!-- 中略 -->
</main>
<nav class="navigation">
  <ul>
    <li><a href="#">TOP</a></li>
    <li><a href="#">セール</a></li>
    <li><a href="#">テレビ</a></li>
    <li><a href="#">洗濯機</a></li>
    <li><a href="#">掃除機</a></li>
<!--  中略  -->
  </ul>
</nav>
▼ CSS(抜粋)
.navigation {
  position: fixed;
  top: 0;
  left: 0;
  overflow: scroll;
}
従来の対処方法
従来、この問題に対処するためには、JavaScriptを使って複雑な対処をする必要がありました。たとえば、ナビゲーションが開いたときにbody要素のスクロールを禁止させ、ナビゲーションが閉じたらスクロールを可能ににするなどのアプローチなどがありました。JavaScriptの知識が必要ですし、ある程度まとまった処理を書く必要があり、テストもしっかりする必要があります。
▼ JavaScriptの対処例
// ハンバーガーボタンのクリック時、
// bodyのoverflowをhiddenにしてスクロールを禁止させる
document.querySelector(".menu-button").addEventListener("click", () => {
  document.body.style.overflow = "hidden";
});
// メニューを閉じるとき、
// bodyのoverflowを元通りにしてスクロールを復活させる
document.querySelector(".close-button").addEventListener("click", () => {
  document.body.style.overflow = "auto";
});
overscroll-behaviorプロパティを使えばCSSだけで対処できる
overscroll-behaviorプロパティを使えば、CSSだけでスクロールの連鎖を防げます。contain値を指定します。 
.navigation {
  overscroll-behavior: contain;
}
ナビゲーションを最下部以上にスクロールしても、メイン要素はそのままです。
実際のデモとコードは、こちらのURLより確認できます。
overscroll-behaviorプロパティは、Safari 16の対応により、Chrome / Edge / Firefox / Safariの全ブラウザで使えるようになりました。
▼ 関連資料
03 CSS Gridを入れ子にできる「サブグリッド」
CSS Gridでは、要素を行と列を使ってレイアウトできます。Safari 16で対応した「サブグリッド」では、行と列を入れ子にできるようになります。サブグリッドを使うことで、従来のCSS Gridでは難しかった複雑なレイアウトを手軽に実現できるようになります。
なぜサブグリッドが必要なのか?
次のようなレイアウトを考えてみましょう。ブログのカードデザインでよく見かけるようなレイアウトです。タイトル/画像/テキスト領域の高さ/リンクの高さがすべて揃っています。1行のときであれば問題なくレイアウトできるでしょう。
 
 
レスポンシブ対応のため、複数行でも各要素の高さを揃えようとすると、実装難易度は高くなります。各行ごとに各要素の高さが異なるためです。
 
 
「サブグリッド」を使うと、このレイアウトを簡単に実現できます。
親のグリッドは8行×3列になっています。
 
 
親のグリッドに、子グリッド(サブグリッド)を配置します。子グリッドの行は「親グリッドの4行分と一緒にする」というのがポイントです。
 
 このような配置をすることで、カードが複数行になっても、各要素の高さがそれぞれの行で揃うようになります。
サブグリッドを使って行列を入れ子にする
HTML / CSSでサブグリッドを表現しましょう。
▼ HTML(抜粋)
<div class="container">
  <div class="item">
    <h2>出待ち</h2>
    <img src="画像1" />
    <p>おすわりしてお行儀よくご飯を待っているうにちゃん。おすわりすればご飯がもらえると思っている。</p>
    <ul>
      <li><a href="#">#今日のうにちゃん</a></li>
      <li><a href="#">#ごはん</a></li>
      <li><a href="#">#忠犬ハチ公</a></li>
    </ul>
  </div>
  <div class="item">
    <h2>誕生日にもらった花が・・・😭</h2>
    <img src="画像2">
    <p>
      お花に興味しんしん丸のうに。SHINSHINといえば福岡の美味しい博多ラーメンもSHINSHIN。博多駅の中とKitteに存在し、どちらもよく行列になっている。おすすめは博多駅構内のSHINSHINで・・・</p>
    <ul>
      <li><a href="#">#花</a></li>
      <li><a href="#">#ラーメン</a></li>
    </ul>
  </div>
  <div class="item">
    <!--  中略  -->
  </div>
  <div class="item">
    <!--  中略  -->
  </div>
</div>
CSSです。まずは親グリッドのレイアウトです。
▼ 親グリッド関連のCSS
.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
}
子グリッド(サブグリッド)用のCSSです。ポイントはgrid-template-rows: subgrid;。子要素の行のテンプレートを、サブグリッドにすることを指定しています。あわせてgrid-row: span 4;とすることで、子要素の行テンプレートが、親要素の行テンプレートと一致します。
.item {
  /* CSS Gridを使うことを指定 */
  display: grid;
  /* 行のテンプレートをサブグリッドにすることを指定 */
  grid-template-rows: subgrid;
  /* 行の列数を4にする。サブグリッドなので、親グリッドの4行分になる */
  grid-row: span 4;
}
動作の様子です。ウインドウサイズが変わっても、それぞれの行の高さが揃っています。
実際のデモとコードは、こちらのリンクより確認できます。
サブグリッドについては、今回のSafari 16で対応したほか、Firefoxでも対応済みです。Chromeについては現在開発中で、次のページよりステータスを確認できます。
EdgeはChromiumエンジンなので、Chromeの対応が完了次第、対応となるでしょう。
▼ 関連資料
最後に
Safari 16の登場により使えるようになった、text-align-last、overscroll-behavior、サブグリッドについて紹介しました。Safari 16ではほかにもCSSのコンテナクエリやWebGL2といったさまざまなWeb技術が使えるようになります。興味のある方はWebkitの公式リリースノートを参照するとよいでしょう。
