テレビ、スマートフォン、パソコンなどのレスポンシブな水平スクロール ビューを構築する方法の基本的な概要。
この記事では、ウェブで水平スクロール エクスペリエンスを作成する方法について説明します。この方法は、最小限でレスポンシブであり、アクセシビリティに優れ、ブラウザやプラットフォーム(テレビなど)で動作します。デモをお試しください。
動画でご覧になりたい場合は、こちらの YouTube 版をご覧ください。
概要
メディアや商品のサムネイルをホストするための水平スクロール レイアウトを作成します。このコンポーネントは、シンプルな <ul>
リストとして始まり、CSS によって満足のいくスムーズなスクロール エクスペリエンスに変換され、画像を表示してグリッドにスナップします。JavaScript が追加され、ロービング インデックスの操作が容易になり、キーボード ユーザーが 100 個以上の項目を移動する手間を省けるようになりました。また、試験運用版のメディアクエリ prefers-reduced-data
を使用して、メディア スクロールを軽量なタイトル スクロール エクスペリエンスに変換します。
アクセシビリティ対応のマークアップから始める
メディア スクロールは、アイテムを含むリストという 2 つのコア コンポーネントで構成されています。リストは、最も単純な形式で世界中を移動し、誰にでも明確に消費される可能性があります。このページにアクセスしたユーザーは、リストを閲覧し、リンクをクリックしてアイテムを表示できます。これがアクセス可能なベースです。
<ul>
要素を含むリストを配信します。
<ul class="horizontal-media-scroller">
<li></li>
<li></li>
<li></li>
...
<ul>
<a>
要素を使用して、リストアイテムをインタラクティブにします。
<li>
<a href="#">
...
</a>
</li>
<figure>
要素を使用して、画像とそのキャプションをセマンティックに表します。
<figure>
<picture>
<img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
</picture>
<figcaption>Legends</figcaption>
</figure>
<img>
の alt
属性と loading
属性に注目してください。メディア スクロールの代替テキストは、サムネイルにコンテキストを追加したり、画像が読み込まれなかった場合のフォールバック テキストとして使用したり、スクリーン リーダーなどの支援技術を使用するユーザー向けの音声 UI を提供したりするユーザー エクスペリエンスの機会となります。詳しくは、準拠した代替テキストを作成するための 5 つのルールをご覧ください。
loading
属性は、この画像ソースがビューポート内に画像がある場合にのみフェッチされることを示すキーワード lazy
を受け入れます。ユーザーがスクロールして表示したアイテムの画像のみをダウンロードするため、大きなリストの場合に非常に便利です。
ユーザーのカラーパターン設定をサポートする
color-scheme
を <meta>
タグとして使用して、ページでライトモードとダークモードの両方のユーザー エージェント スタイルが必要であることをブラウザに伝えます。これは、見方によっては無料のダークモードまたはライトモードです。
<meta name="color-scheme" content="dark light">
メタタグは可能な限り早いシグナルを提供するため、ユーザーがダークモードを好む場合は、ブラウザがデフォルトのキャンバスの色をダークモードに選択できます。つまり、サイトのページ間の移動で、読み込みの合間に白いキャンバスの背景が点滅しなくなります。読み込み時のダークモードがシームレスになり、目に優しくなりました。
詳しくは、Thomas Steiner 氏の https://web.dev/color-scheme/ をご覧ください。
コンテンツを追加
上記の ul > li > a > figure > picture > img
のコンテンツ構造を踏まえて、次のタスクでは、スクロールする画像とタイトルを追加します。このデモには静的なプレースホルダ画像とテキストが詰め込まれていますが、お好みのデータソースからこのデモを動かすこともできます。
CSS を使用してスタイルを追加する
CSS は、この汎用的なコンテンツのリストをエクスペリエンスに変換します。Netflix やアプリストアなど、多くのサイトやアプリでは、水平スクロール領域を使用して、ビューポートにカテゴリやオプションを詰め込んでいます。
スクロール レイアウトの作成
レイアウトでコンテンツが途切れたり、省略記号によるテキストの切り捨てに頼ったりしないようにすることが重要です。多くのテレビにはこのようなメディア スクロール機能がありますが、コンテンツを省略することが多すぎます。このレイアウトはそうではありません。また、メディア コンテンツで列のサイズをオーバーライドできるため、1 つのレイアウトで多くの興味深い組み合わせを柔軟に処理できます。
コンテナでは、デフォルトのサイズをカスタム プロパティとして指定することで、列のサイズをオーバーライドできます。このグリッド レイアウトは列のサイズについて意見を持っています。間隔と方向のみを管理します。
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2); /* parent owned value for children to be relative to*/
margin: 0;
}
カスタム プロパティは、<picture>
要素によって使用され、基本のアスペクト比(ボックス)が作成されます。
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
& picture {
inline-size: var(--size);
block-size: var(--size);
}
}
いくつかのマイナー スタイルを追加するだけで、メディア スクロールの基本が完成します。
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
overflow-x: auto;
overscroll-behavior-inline: contain;
& > li {
display: inline-block; /* removes the list-item bullet */
}
& picture {
inline-size: var(--size);
block-size: var(--size);
}
}
overflow
を設定すると、<ul>
が設定され、リストのスクロールとキーボード ナビゲーションが可能になります。次に、各直接の子 <li>
要素の ::marker
が削除され、新しい表示タイプ inline-block
が取得されます。
ただし、画像はまだレスポンシブではなく、内側のボックスから飛び出してしまいます。サイズ、フィット、ボーダー スタイル、遅延読み込み時の背景グラデーションを使用して、それらを制御します。
img {
/* smash into whatever box it's in */
inline-size: 100%;
block-size: 100%;
/* don't squish but do cover the space */
object-fit: cover;
/* soften the edges */
border-radius: 1ex;
overflow: hidden;
/* if empty, show a gradient placeholder */
background-image:
linear-gradient(
to bottom,
hsl(0 0% 40%),
hsl(0 0% 20%)
);
}
スクロール パディング
ページ コンテンツとの配置と、端から端までスクロールできるサーフェス領域は、調和のとれたミニマルなコンポーネントにとって重要です。
タイポグラフィとレイアウト ラインに沿ったエッジ ツー エッジのスクロール レイアウトを実現するには、scroll-padding
に一致する padding
を使用します。
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
overflow-x: auto;
overscroll-behavior-inline: contain;
padding-inline: var(--gap);
scroll-padding-inline: var(--gap);
padding-block: calc(var(--gap) / 2); /* make space for scrollbar and focus outline */
}
水平スクロールのパディングに関するバグの修正 上記は、スクロール コンテナのパディングがどれほど簡単であるかを示していますが、これには未解決の互換性の問題があります(ただし、Chromium 91 以降では修正されています)。詳しくはこちらをご覧ください。簡単に説明すると、スクロール ビューでは常にパディングが考慮されていたわけではありません。
ブラウザをだましてパディングをスクローラの一番下に配置するために、各リストの最後の図をターゲットにして、必要なパディングの量に相当する疑似要素を追加します。
.horizontal-media-scroller > li:last-of-type figure {
position: relative;
&::after {
content: "";
position: absolute;
inline-size: var(--gap);
block-size: 100%;
inset-block-start: 0;
inset-inline-end: calc(var(--gap) * -1);
}
}
論理プロパティを使用すると、メディア スクロールは任意の書き込みモードとドキュメントの方向で動作できます。
スクロール スナップ
オーバーフローのあるスクロール コンテナは、1 行の CSS でスナップ ビューポートにできます。その後、子要素がそのビューポートとの位置合わせ方法を指定します。
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
overflow-x: auto;
overscroll-behavior-inline: contain;
padding-inline: var(--gap);
scroll-padding-inline: var(--gap);
padding-block-end: calc(var(--gap) / 2);
scroll-snap-type: inline mandatory;
& figure {
scroll-snap-align: start;
}
}
フォーカス
このコンポーネントは、テレビやアプリストアなどで非常に人気があることから着想を得ています。多くのゲーム プラットフォームでは、このメディア スクロールと非常によく似たものが、メインのホーム画面レイアウトとして使用されています。フォーカスは、単なる小さな追加ではなく、大きな UX の瞬間です。このメディア スクロールをソファからリモコンで操作することを想定して、その操作に少しの改善を加えてみましょう。
.horizontal-media-scroller a {
outline-offset: 12px;
&:focus {
outline-offset: 7px;
}
@media (prefers-reduced-motion: no-preference) {
& {
transition: outline-offset .25s ease;
}
}
}
これにより、フォーカス アウトライン スタイル 7px
がボックスから離れて、適切なスペースが確保されます。ユーザーがモーションの低減に関する設定を行っていない場合、オフセットが移行され、フォーカス イベントに微妙なモーションが与えられます。
ロービング インデックス
ゲームパッドやキーボードを使用するユーザーは、スクロール可能なコンテンツやオプションが長々と並ぶリストで、特に注意を払う必要があります。この問題を解決する一般的なパターンは、ロービング インデックスと呼ばれます。アイテムのコンテナがキーボード フォーカスされているが、一度に 1 つの子のみがフォーカスを保持できる場合です。この一度に 1 つの項目にフォーカスする機能は、タブキーを 50 回以上押してリストの末尾に到達するのではなく、項目が長くなる可能性のあるリストをバイパスできるように設計されています。
デモの最初のスクローラには 300 個のアイテムがあります。次のセクションに到達するためにすべての要素をトラバースするよりも、もっと良い方法があります。
このエクスペリエンスを作成するには、JavaScript でキーボード イベントとフォーカス イベントを監視する必要があります。このユーザー エクスペリエンスを簡単に実現できるように、npm で小さなオープンソース ライブラリを作成しました。3 つのスクロール バーでの使用方法は次のとおりです。
import {rovingIndex} from 'roving-ux';
rovingIndex({
element: someElement
});
このデモでは、ドキュメントでスクローラをクエリし、それぞれのスクローラに対して rovingIndex()
関数を呼び出します。ロービング エクスペリエンス(リスト コンテナなど)と、フォーカス ターゲットが直接の子孫でない場合に備えてターゲット クエリ セレクタを取得するには、rovingIndex()
に要素を渡します。
document.querySelectorAll('.horizontal-media-scroller')
.forEach(scroller =>
rovingIndex({
element: scroller,
target: 'a',
}))
この効果について詳しくは、オープンソース ライブラリ roving-ux をご覧ください。
アスペクト比
この投稿の執筆時点では、aspect-ratio
のサポートは Firefox のフラグの背後にありますが、Chromium ブラウザまたはセットトップ ボックスで利用できます。メディア スクロール グリッド レイアウトでは方向と間隔のみが指定されるため、アスペクト比のサポートをチェックするメディアクエリ内でサイズを変更できます。より動的なメディア スクロールへのプログレッシブ エンハンスメント。
@supports (aspect-ratio: 1) {
.horizontal-media-scroller figure > picture {
inline-size: auto; /* for a block-size driven ratio */
aspect-ratio: 1; /* boxes by default */
@nest section:nth-child(2) & {
aspect-ratio: 16/9;
}
@nest section:nth-child(3) & {
/* double the size of the others */
block-size: calc(var(--size) * 2);
aspect-ratio: 4/3;
/* adjust size to fit more items into the viewport */
@media (width <= 480px) {
block-size: calc(var(--size) * 1.5);
}
}
}
}
ブラウザが aspect-ratio
構文をサポートしている場合、メディア スクロール画像のサイズは aspect-ratio
にアップグレードされます。下書きのネスト構文を使用すると、各画像は 1 行目、2 行目、3 行目のいずれであるかに応じてアスペクト比が変わります。ネスト構文を使用すると、他のサイズ設定ロジックとともに、ビューポートの微調整を設定することもできます。
この CSS を使用すると、この機能がより多くのブラウザ エンジンで利用可能になるにつれて、管理が容易で視覚的に魅力的なレイアウトがレンダリングされます。
データ削減を優先
次の手法は Canary のフラグの背後でのみ使用できますが、数行の CSS でページ読み込み時間とデータ使用量を大幅に削減できる方法をご紹介します。レベル 5 の prefers-reduced-data
メディアクエリを使用すると、デバイスがデータセーバー モードなどのデータ削減状態にあるかどうかを問い合わせることができます。その場合は、ドキュメントを変更して、画像を非表示にできます。
figure {
@media (prefers-reduced-data: reduce) {
& {
min-inline-size: var(--size);
& > picture {
display: none;
}
}
}
}
コンテンツはナビゲート可能ですが、重い画像をダウンロードするコストはかかりません。prefers-reduced-data
CSS を追加する前のサイトは次のようになります。
(7 件のリクエスト、131 ミリ秒で 100 KB のリソース)
prefers-reduced-data
CSS を追加した後のサイトのパフォーマンスは次のとおりです。
(71 件のリクエスト、1.07 秒で 1.2 MB のリソース)
リクエストが 64 件減り、このブラウザタブのビューポート内(ワイド画面ディスプレイでテストを実施)にある約 60 枚の画像、ページ読み込みの約 80% の高速化、ネットワーク経由のデータ量の 10% 削減が実現しました。かなり強力な CSS です。
まとめ
私がどのようにして達成したかをご理解いただけたかと思います。では、あなたならどうしますか?🙂
アプローチを多様化し、ウェブで構築するさまざまな方法を学びましょう。Codepen を作成するか、独自のデモをホストして、そのデモをツイートしてください。下のコミュニティ リミックス セクションに追加します。
ソース
コミュニティ リミックス
表示する項目はありません。