ストーリー コンポーネントの作成

Instagram ストーリーに似たエクスペリエンスをウェブ上に構築する方法の基本的な概要。

この投稿では、レスポンシブで、キーボード ナビゲーションをサポートし、ブラウザ間で動作するウェブ用のストーリー コンポーネントの構築に関する考えを共有したいと思います。

デモ

このストーリー コンポーネントを自分で作成するハンズオン デモをご希望の場合は、ストーリー コンポーネントの Codelab をご覧ください。

動画をご希望の場合は、この投稿の YouTube 版をご覧ください。

概要

ストーリー UX の一般的な例としては、Snapchat のストーリーや Instagram のストーリー(フリートは言うまでもなく)があります。一般的な UX の観点から見ると、ストーリーは通常、複数のサブスクリプションを操作するための、モバイル専用のタップ中心のパターンです。たとえば、Instagram では、ユーザーは友人のストーリーを開き、その中の写真を確認します。通常、一度に多くの友だちを招待します。デバイスの右側をタップすると、その友だちの次のストーリーにスキップします。右にスワイプすると、別の友だちにスキップします。 ストーリー コンポーネントはカルーセルとよく似ていますが、1 次元配列ではなく多次元配列をナビゲートできます。各カルーセルの中にカルーセルがあるようなものです。🤯

カードを使用して多次元配列を可視化した図。左から右に、紫色の枠線のカードが積み重なっており、各カードの中には 1 つ以上のシアン色の枠線のカードがあります。リスト内のリスト。
友だちの最初のカルーセル
ストーリーの 2 番目の「積み重ねられた」カルーセル
👍 リスト内のリスト(多次元配列)

ジョブに適したツールの選択

全体として、このコンポーネントは、いくつかの重要なウェブ プラットフォーム機能のおかげで、非常に簡単に構築できました。詳しく見ていきましょう。

CSS グリッド

CSS Grid にはコンテンツを操作する強力な方法が備わっているため、このレイアウトは CSS Grid にとって難しいものではありませんでした。

友だちのレイアウト

メインの .stories コンポーネント ラッパーは、モバイル ファーストの水平スクロールビューです。

.stories {
  inline-size: 100vw;
  block-size: 100vh;

  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;

  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}

/* desktop constraint */
@media (hover: hover) and (min-width: 480px) {
  max-inline-size: 480px;
  max-block-size: 848px;
}
Chrome DevTools のデバイスモードを使用して、Grid で作成された列をハイライト表示する

grid レイアウトを詳しく見てみましょう。

  • モバイルでは 100vh と 100vw でビューポートを明示的に埋め、パソコンではサイズを制限します。
  • / は行テンプレートと列テンプレートを区切ります。
  • auto-flow は grid-auto-flow: column に変換されます。
  • 自動フロー テンプレートは 100% です。この場合、スクロール ウィンドウの幅は任意です。

スマートフォンでは、行サイズがビューポートの高さ、各列がビューポートの幅であると考えてください。Snapchat ストーリーと Instagram ストーリーの例を続けると、各列は友人のストーリーになります。友だちのストーリーは、スクロールできるようにビューポートの外に続くようにします。グリッドは、各友人のストーリーの HTML をレイアウトするために必要な数の列を作成し、動的でレスポンシブなスクロール コンテナを作成します。Grid を使用することで、エフェクト全体を一元化できました。

重ね置き対応

各友だちについて、ページ分けに対応した状態でストーリーを取得する必要があります。アニメーションやその他の楽しいパターンに備えて、スタックを選択しました。スタックとは、サンドイッチを横から見るのではなく、上から見下ろすようなものです。

CSS グリッドを使用すると、行と列がエイリアス([story])を共有する単一セルのグリッド(正方形)を定義し、各子をそのエイリアス化された単一セルのスペースに割り当てることができます。

.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.story {
  grid-area: story;
  background-size: cover;
  …
}

これにより、HTML で重ね順を制御し、すべての要素をフロー内に維持できます。absolute の配置や z-index で何もする必要がなく、height: 100% や width: 100% でボックスを修正する必要もありません。親グリッドはすでにストーリー画像ビューポートのサイズを定義しているため、これらのストーリー コンポーネントにビューポートを埋めるように指示する必要はありません。

CSS スクロール スナップ ポイント

CSS スクロール スナップ ポイント仕様を使用すると、スクロール時に要素をビューポートに簡単にロックできます。これらの CSS プロパティが存在する前は、JavaScript を使用する必要があり、控えめに言っても、それは複雑でした。Sarah Drasner 氏による CSS スクロール スナップ ポイントの紹介で、使用方法の詳細を確認してください。

scroll-snap-points スタイルを使用しない場合と使用する場合の水平スクロール。 この属性がない場合、ユーザーは通常どおり自由にスクロールできます。これにより、ブラウザは各アイテムに静かに配置されます。
親
.stories {
  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}
オーバースクロールを含む親がスナップ動作を定義します。
子ども
.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
子ウィンドウは、スナップ ターゲットになることをオプトインします。

Scroll Snap Points を選んだ理由はいくつかあります。

  • 無料のユーザー補助機能。Scroll Snap Points の仕様では、左矢印キーと右矢印キーを押すと、デフォルトでスナップポイント間を移動することになっています。
  • 仕様の拡大。Scroll Snap Points 仕様には、常に新機能や改善が追加されています。つまり、ストーリー コンポーネントは今後も改善されていくということです。
  • 実装の容易さ。スクロール スナップ ポイントは、タッチ中心の水平方向のページネーションのユースケース向けに構築されています。
  • 自由なプラットフォーム スタイルの慣性。正規化された慣性では、スクロールと停止のスタイルが不自然になることがありますが、各プラットフォームでは、独自のスタイルでスクロールと停止が行われます。

クロスブラウザの互換性

Opera、Firefox、Safari、Chrome、Android、iOS でテストしました。以下に、機能とサポートに違いが見られたウェブ機能の概要を示します。

ただし、一部の CSS が適用されなかったため、一部のプラットフォームでは現在、UX の最適化が適用されていません。これらの機能を管理する必要がないのは便利でした。最終的には他のブラウザやプラットフォームにも対応するようになると思います。

scroll-snap-stop

カルーセルは、CSS スクロール スナップ ポイント仕様の作成を促した主要な UX ユースケースの 1 つです。ストーリーとは異なり、カルーセルは、ユーザーが操作した後に各画像で停止する必要はありません。カルーセルをすばやく切り替えることが、問題ない場合や推奨される場合もあります。一方、ストーリーは 1 つずつ移動するのが最適です。scroll-snap-stop はまさにその機能を提供します。

.user {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

この投稿の執筆時点では、scroll-snap-stop は Chromium ベースのブラウザでのみサポートされています。最新情報については、ブラウザの互換性をご覧ください。ただし、これはブロッカーではありません。サポートされていないブラウザでは、ユーザーが誤って友だちをスキップしてしまう可能性があるということです。そのため、ユーザーがより注意を払うか、スキップされた友だちが閲覧済みとしてマークされないように JavaScript を記述する必要があります。

詳細については、仕様をご覧ください。

overscroll-behavior

モーダルをスクロールしているときに、突然モーダルの背後にあるコンテンツのスクロールが始まったことはありませんか?overscroll-behavior を使用すると、デベロッパーはスクロールをトラップして、スクロールが離れないようにできます。さまざまな場面で便利です。My Stories コンポーネントでは、追加のスワイプやスクロール ジェスチャーによってコンポーネントから離れないようにするために、このプロパティを使用しています。

.stories {
  overflow-x: auto;
  overscroll-behavior: contain;
}

Safari と Opera は、これをサポートしていない 2 つのブラウザでしたが、それはまったく問題ありません。そのようなユーザーは、これまでどおりのオーバースクロール エクスペリエンスを得ることができ、この機能強化に気づかない可能性があります。私は個人的にこの機能が大好きで、実装するほぼすべてのオーバースクロール機能の一部として組み込んでいます。これは、UX の向上にのみつながる無害な追加機能です。

scrollIntoView({behavior: 'smooth'})

ユーザーがタップまたはクリックして、友だちのストーリー セットの最後に到達したら、スクロール スナップ ポイント セット内の次の友だちに移動します。JavaScript を使用すると、次の友だちを参照して、ビューにスクロールするようリクエストできます。この基本的な機能は、すべてのブラウザでサポートされており、スクロールして表示されます。しかし、すべてのブラウザが 'smooth' を行ったわけではありません。これは、スナップされるのではなく、スクロールされて表示されることを意味します。

element.scrollIntoView({
  behavior: 'smooth'
})

Safari は、ここで behavior: 'smooth' をサポートしていない唯一のブラウザでした。最新情報については、ブラウザの互換性をご覧ください。

実践的

私のやり方がわかったところで、あなたならどうしますか?アプローチを多様化し、ウェブで構築するすべての方法を学びましょう。デモを作成し、ツイートしてください。以下のコミュニティ リミックス セクションに追加します。

コミュニティ リミックス