メディア スクローラー コンポーネントの作成

テレビ、スマートフォン、パソコンなどでレスポンシブな横向きスクロール ビューを作成する方法の基本的な概要です。

この投稿では、最小限のレスポンシブで、アクセスしやすく、さまざまなブラウザやプラットフォーム(テレビなど)で動作する、ウェブ向けの水平スクロール エクスペリエンスを作成する方法について考えたいと思います。デモを試す。

デモ

動画をご覧になる場合は、この投稿の 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 属性に注目してください。メディア スクローラーの代替テキストは、サムネイルに追加のコンテキストを表示する UX の機会です。画像が読み込まれなかった場合のフォールバック テキストとして利用したり、スクリーン リーダーなどの支援技術を利用しているユーザーに音声 UI を提供したりできます。詳しくは、準拠している代替テキストに関する 5 つのゴールデン ルールをご覧ください。

loading 属性は、画像がビューポート内にある場合にのみこの画像ソースを取得する必要があることを示す方法として、キーワード lazy を受け入れます。これは、ユーザーが表示にスクロールしたアイテムの画像のみをダウンロードするため、大規模なリストでは非常に便利です。

ユーザーのカラーパターンの設定をサポートする

color-scheme<meta> タグとして使用して、ライトとダークの両方のユーザー エージェント スタイルがページで必要であることをブラウザに通知します。見方に応じて、無料のダークモードかライトモードのいずれかになります。

<meta name="color-scheme" content="dark light">

メタタグは可能な限り早期のシグナルを提供するため、ユーザーがダークモードを設定している場合、ブラウザはデフォルトのダーク キャンバス色を選択できます。つまり、サイトのページ間を移動しても、読み込みの合間に白いキャンバス背景が点滅することはありません。読み込みの合間にシームレスにダークモードが表示され、目に留まりやすくなりました。

詳しくは、Thomas Steinerhttps://web.dev/color-scheme/)をご覧ください。

コンテンツを追加する

上記の ul > li > a > figure > picture > img のコンテンツ構造に対して、次のタスクはスクロールする画像とタイトルの追加です。このデモにはプレースホルダの静的な画像とテキストが含まれていますが、お好みのデータソースから自由に調整していただけます。

CSS でスタイルを追加する

CSS では、この汎用的なコンテンツ リストをエクスペリエンスに変換してみましょう。Netflix やアプリストアをはじめ、多くのサイトやアプリでは、水平方向のスクロール エリアを使用して、ビューポートをカテゴリとオプションにまとめています。

スクローラー レイアウトの作成

レイアウトでコンテンツが途切れたり、省略記号によるテキストの切り捨てが発生したりしないようにすることが重要です。多くのテレビにはこのようなメディア スクローラーが搭載されていますが、多くの場合、コンテンツが省略されていることがあります。このレイアウトでは機能しません。 また、メディア コンテンツで列サイズをオーバーライドできるため、1 つのレイアウトでさまざまな組み合わせを処理できる柔軟性が生まれます。

2 つのスクロール行が表示されています。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;
  }
}

主要なポイント

このコンポーネントは、テレビや App Store などで広く使用されていることから着想を得ています。多くのビデオゲーム プラットフォームでは、これによく似たメディア スクローラーが、メインのホーム画面レイアウトとして使用されています。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 をご覧ください。

アスペクト比

この投稿の執筆時点で、Firefox では aspect-ratio のサポートがフラグの背後にされていますが、Chromium ブラウザまたはセットトップ ボックスでは使用できます。メディア スクローラー グリッド レイアウトは方向と間隔のみを指定するため、機能がアスペクト比のサポートを確認するメディアクエリ内でサイズが変更されることがあります。より動的なメディア スクローラーへの段階的なエンハンスメント。

アスペクト比 4:4 のボックスが、使用されている他のデザイン比率 16:9 と 4:3 の横に表示されています。

@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 行目のいずれであるかに応じてアスペクト比を変更します。nest 構文では、他のサイズ設定ロジックを使用して、ビューポートの小規模な調整を設定することもできます。

この CSS を使用すると、この機能がより多くのブラウザ エンジンで利用できるため、管理しやすく、視覚的にも魅力的なレイアウトがレンダリングされます。

データ量が少ない方が望ましい

次の手法は Canaryフラグの背後でしか利用できませんが、数行の CSS でページの読み込み時間とデータ使用量を大幅に削減する方法をご紹介したいと思います。レベル 5prefers-reduced-data メディアクエリでは、デバイスがデータ節約モード(データセーバー モードなど)にあるかどうかを照会できます。正しく表示されている場合は、ドキュメントを修正して、このケースでは画像を非表示にできます。

ALT_TEXT_HERE

figure {
  @media (prefers-reduced-data: reduce) {
    & {
      min-inline-size: var(--size);

      & > picture {
        display: none;
      }
    }
  }
}

コンテンツは引き続き操作可能ですが、重い画像のダウンロードにコストはかかりません。prefers-reduced-data CSS を追加する前のサイトは次のとおりです。

(7 件のリクエスト、131 ミリ秒で 100 KB のリソース)

ALT_TEXT_HERE

prefers-reduced-data CSS を追加した後のサイトのパフォーマンスは次のようになります。

ALT_TEXT_HERE

(71 件のリクエスト、1.07 秒で 1.2 MB のリソース)

リクエストが 64 件減ります。このブラウザタブのビューポート内の約 60 枚の画像(ワイド スクリーン ディスプレイで実施したテスト)でページ読み込みが最大 80% 増加し、データが 10% 転送されます。これは非常に強力な CSS です。

おわりに

さて、やったらどうやってやったの?🙂

多様なアプローチと、ウェブでの構築方法を学んでいきましょう。 Codepen を作成するか、独自のデモをホストして、それをツイートしてください。以下のコミュニティ リミックス セクションに追加します。

ソース

コミュニティのリミックス

まだ何も表示されません。