【デザイン基礎】scroll-margin-inline

ウェブサイトのユーザーエクスペリエンスを向上させる上で、スクロールの挙動は非常に重要な要素です。特に、固定ヘッダーやナビゲーション、特定のセクションへのスムーズな移動が求められる現代のウェブデザインにおいて、コンテンツが隠れてしまう問題は避けて通れません。このような課題を解決し、より洗練されたスクロール体験を提供するCSSプロパティの一つが、今回深く掘り下げる`scroll-margin-inline`です。

概要

`scroll-margin-inline`は、CSSスクロールスナップモジュールの一部として導入された論理プロパティであり、スクロールコンテナ内の特定の要素がビューポートの端にスナップまたはスクロールされた際に、その要素のインライン方向の「マージン」を指定します。この「マージン」は、要素がビューポートの端に到達した際に、その要素とビューポートの端との間に確保される余白を意味します。

従来の物理プロパティである`scroll-margin-top`や`scroll-margin-left`などとは異なり、`scroll-margin-inline`は書字方向(`writing-mode`や`direction`プロパティによって定義される)に応じて、その適用方向が動的に変化します。例えば、左から右へ書く言語(LTR)では`scroll-margin-left`に相当し、右から左へ書く言語(RTL)では`scroll-margin-right`に相当します。これにより、国際化されたウェブサイトにおいて、書字方向によらず一貫したスクロール挙動を簡単に実現できるようになります。

主な用途としては、固定ヘッダーやフッターが存在する場合に、アンカーリンクでジャンプしたターゲット要素や、スクロールスナップでアラインされる要素が、それらの固定要素の下に隠れてしまうのを防ぐ目的で利用されます。また、キーボードナビゲーション時にフォーカスされた要素がビューポート外に隠れないように調整する際にも有効です。

詳細解説

`scroll-margin-inline`は、CSSの論理プロパティ群の一部であり、その挙動はドキュメントの書字方向(`writing-mode`、`direction`、`text-orientation`など)に強く依存します。この特性を理解することが、`scroll-margin-inline`を効果的に活用する上で不可欠です。

論理プロパティとしての`scroll-margin-inline`

CSSにおける論理プロパティとは、物理的な上下左右ではなく、コンテンツのフロー方向(インライン方向とブロック方向)に基づいて定義されるプロパティです。
* **インライン方向:** テキストが流れる方向。多くの言語では左から右(LTR)または右から左(RTL)。
* **ブロック方向:** インライン方向に対して垂直な方向。多くの言語では上から下。

`scroll-margin-inline`は、インライン方向の開始側(`inline-start`)と終了側(`inline-end`)の両方に同じ値を適用します。具体的には、`scroll-margin-inline-start`と`scroll-margin-inline-end`のショートハンドプロパティです。

例えば、標準的な書字方向(`writing-mode: horizontal-tb; direction: ltr;`)の場合:
* `inline-start`は物理的な`left`に相当。
* `inline-end`は物理的な`right`に相当。

したがって、`scroll-margin-inline`は`scroll-margin-left`と`scroll-margin-right`の両方に適用されることになります。

もし書字方向が`writing-mode: vertical-rl; direction: ltr;`(縦書き、右から左へ)の場合:
* `inline-start`は物理的な`top`に相当。
* `inline-end`は物理的な`bottom`に相当。

この柔軟性により、異なる言語や書字モードを持つサイトでも、一貫したスクロールオフセットを維持できます。

スクロールスナップとの連携

`scroll-margin-inline`の最も一般的なユースケースの一つは、CSSスクロールスナップ機能との組み合わせです。スクロールスナップは、ユーザーのスクロール操作を特定の要素に「スナップ」させることで、コンテンツの閲覧をよりスムーズかつ意図的にする機能です。

スクロールスナップを使用する際には、スクロールコンテナに`scroll-snap-type`を、スナップ対象要素に`scroll-snap-align`を設定します。しかし、この設定だけでは、固定ヘッダーやフッターがある場合に、スナップされた要素の一部がそれらの固定要素の下に隠れてしまう問題が発生します。

ここで`scroll-margin-inline`が登場します。スナップ対象となる要素に`scroll-margin-inline`を指定することで、スナップ位置がそのマージン分だけ内側にオフセットされます。これにより、固定ヘッダーなどの要素がコンテンツを覆い隠すことなく、適切な位置にコンテンツを表示できます。

例えば、水平方向のスクロールスナップで、固定サイドバーがある場合、スナップ対象要素に`scroll-margin-inline`を設定することで、サイドバーの下にコンテンツが隠れるのを防ぎ、常にサイドバーの隣にコンテンツの始まりが見えるように調整できます。

アンカーリンク(フラグメント識別子)との連携

ウェブページ内の特定のセクションへ直接リンクするアンカーリンク(`#section-id`)を使用する場合にも、`scroll-margin-inline`は非常に有効です。ユーザーがアンカーリンクをクリックすると、ブラウザはそのターゲット要素までスクロールします。この際、固定ヘッダーが存在すると、ターゲット要素の開始位置がヘッダーの下に隠れてしまうことがよくあります。

この問題を解決するために、ターゲットとなる要素(例: `id=”section-id”`を持つ要素)に`scroll-margin-inline`プロパティを適用します。指定された値の分だけ、スクロールのターゲット位置がオフセットされ、固定ヘッダーの下にコンテンツが隠れることなく、適切に表示されるようになります。

この機能は、`scroll-behavior: smooth;`と組み合わせることで、ユーザーに滑らかなスクロール体験を提供しつつ、コンテンツの視認性を確保する上で非常に強力です。

アクセシビリティ(フォーカス時の調整)

キーボードナビゲーションを使用するユーザー(例: Tabキーで要素間を移動する)にとって、フォーカスされた要素がビューポートの外に隠れてしまうことは、大きなアクセシビリティの問題となります。`scroll-margin-inline`は、この問題の解決にも貢献できます。

要素にフォーカスが当たった際に、ブラウザは通常、その要素がビューポート内に見えるようにスクロールします。この時、`scroll-margin-inline`が設定されていると、そのマージンを考慮してスクロール位置が調整されます。これにより、フォーカスされた要素がビューポートの端や固定されたUI要素に隠れることなく、常に視認可能な状態で表示されるようになります。これは、特にフォーム要素やインタラクティブなコンポーネントが多数存在するページで、ユーザーエクスペリエンスを大幅に向上させます。

`scroll-padding`との違いと使い分け

`scroll-margin`系のプロパティとよく混同されるのが`scroll-padding`系のプロパティです。両者は似たような目的(スクロールオフセットの調整)を持ちますが、適用される要素が異なります。
* **`scroll-margin`:** スクロールコンテナ内の**ターゲット要素**(またはスナップ対象要素)に適用され、その要素の外側に余白を確保します。
* **`scroll-padding`:** **スクロールコンテナ自身**に適用され、そのスクロール可能な領域の内側にパディングを追加します。

どちらを使用すべきかは、状況によります。
* **`scroll-margin-inline`:** 特定の要素(例: アンカーリンクのターゲット、スクロールスナップのターゲット)に対して個別にオフセットを設定したい場合に適しています。要素ごとに異なるオフセットが必要な場合や、要素自身のスタイルの一部としてオフセットを管理したい場合に便利です。
* **`scroll-padding-inline`:** スクロールコンテナ内のすべてのターゲット要素に対して一律のオフセットを適用したい場合に適しています。例えば、固定ヘッダーやサイドバーが常に存在するスクロールコンテナ全体に対して、一貫したオフセットを適用したい場合に効率的です。

多くの場合、固定ヘッダーがあるサイト全体で一貫したオフセットを適用したい場合は`html`要素や`body`要素に`scroll-padding-top`(または`scroll-padding-block-start`)を設定する方がシンプルですが、スクロールスナップや特定の要素にのみカスタムオフセットが必要な場合は`scroll-margin-inline`が強力な選択肢となります。

サンプルコード

ここでは、`scroll-margin-inline`の具体的な使用例をいくつか示します。

例1: 水平スクロールスナップと固定サイドバー

この例では、水平方向にスナップするコンテンツがあり、左側に固定されたサイドバーが存在する場合に`scroll-margin-inline`を使用してコンテンツがサイドバーの下に隠れないようにします。


<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>scroll-margin-inline 水平スナップ</title>
<style>
body {
margin: 0;
font-family: sans-serif;
overflow: hidden; /* bodyのスクロールを無効化 */
}

.sidebar {
position: fixed;
top: 0;
left: 0;
width: 150px; /* 固定サイドバーの幅 */
height: 100vh;
background-color: #333;
color: white;
padding: 20px;
box-sizing: border-box;
z-index: 100;
}

.scroll-container {
width: 100vw;
height: 100vh;
overflow-x: scroll; /* 水平スクロールを有効化 */
scroll-snap-type: x mandatory; /* 水平方向のスナップを強制 */
display: flex; /* flexboxでセクションを横並びにする */
padding-left: 150px; /* サイドバーの幅分パディング */
box-sizing: border-box;
}

.section {
flex: 0 0 calc(100vw - 150px); /* 画面幅からサイドバーの幅を引いた分 */
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 3em;
color: white;
scroll

コメント

タイトルとURLをコピーしました