loading-states-and-perceived-performance について
読み込み状態と体感速度を設計するスキルです。デザイナーや開発者向けに、表示パターンの選択、コンテンツの優先読み込み、短縮できない待ち時間への対応を解説します。
主な機能
- 01待ち時間に応じた表示の選択
- 02レイアウトに合うスケルトン画面
- 03コンテンツ優先と不要データの遅延
- 04予測可能な次の操作の先読み
- 05ロールバック可能な楽観的更新
- 06遅いリクエストと長時間ジョブの区別
活用シーン
- 01ボタン操作やデータ取得の表示設計
- 02ダッシュボードやページの段階的読み込み
- 03アップロードやエクスポート中の応答感の改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dembrandt/ dembrandt-skills --skill loading-states-and-perceived-performanceインストール後、Claude Code で/loading-states-and-perceived-performanceを入力して読み込まれたか確認します。
詳細
ローディング状態と体感速度
ユーザーは、何を待っているのか、どれくらい進んでいるのかが分かれば、待ち時間をそれほど気にしません。体感速度を高めるとは、実際よりもシステムが速く感じられるように設計することです。
適切なローディング状態を選ぶ
| 待ち時間 | 最適なパターン | 用途 |
|---|---|---|
| 短い(< 1s) | インラインスピナー/ローダー | ボタン操作、小規模な更新、素早いデータ取得 |
| 中程度(1s – 3s) | スケルトンスクリーン | カード、リスト、ダッシュボード、プロフィールページ |
| 長い(> 3s) | 進捗確定型プログレスバー | ファイルのアップロード、複雑なエクスポート、負荷の高い処理 |
| フルページ | 段階的な表示/セクションのアニメーション | アプリの初回読み込み、ヒーローセクション、没入感のあるトランジション |
シンプルなケース:スピナーとローダー
レイアウトが大きく変わらない、小規模で範囲の限られた操作にはスピナーを使用します。
- ボタンのスピナー: ボタンのテキストと置き換えるか、テキストの横に表示します。二重送信を防ぐため、ボタンを
disabled状態にしてください。 - マイクロローダー: インライン更新(例:単一フィールドの保存)には、16〜24pxの小さな円を使用します。
- アニメーションのヒント: 一定速度で回転するアニメーションよりも、「スプリングのような」回転(加速と減速を伴うイージング)のほうが、洗練された印象になります。
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
animation: spin 800ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
スケルトンスクリーン(グリマー/シマー)
スケルトンスクリーンは、最終的なコンテンツのレイアウトを模した視覚的なプレースホルダーです。これにより「レイアウトシフト」(CLS)を抑え、コンテンツが表示される場所をユーザーに正確に伝えます。
シマー効果
スケルトン要素の上を移動する、控えめなグラデーションです。
.skeleton {
background: var(--color-grey-100);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255, 255, 255, 0) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
スケルトンのルール
- 形状を合わせる: 最終的なコンテンツが丸いアバターなら、丸いスケルトンを使用します。2行の見出しなら、幅の異なるバーを2本使用します。
- 控えめにする: スケルトンには最も目立たないグレー(
--color-grey-100またはgrey-50)を使用します。視線を引きつけないようにしてください。 - 自然に切り替える: データが届いたら、瞬時に切り替えるのではなく、スケルトンの上に実際のコンテンツをフェードインさせます(150〜200ms)。
セクション全体にアニメーションを適用する
大きなページ遷移や初回読み込みには、連動したアニメーション戦略を使用します。
段階的な表示(カスケード)
ページ全体を一度に表示するのではなく、セクションを順番にアニメーション表示します。これにより、最も重要なコンテンツ(ヒーロー)から二次的な領域へと、ユーザーの視線を誘導できます。
.section {
opacity: 0;
transform: translateY(10px);
animation: slide-up 400ms ease-out forwards;
}
/* Stagger by index */
.section:nth-child(1) { animation-delay: 100ms; }
.section:nth-child(2) { animation-delay: 200ms; }
.section:nth-child(3) { animation-delay: 300ms; }
@keyframes slide-up {
to { opacity: 1; transform: translateY(0); }
}
ヒーローセクションの「ブルーム」
ヒーローセクションには、より複雑なアニメーションを使用できます。
- 背景画像をゆっくりフェードインさせます。
- 見出しをわずかに行き過ぎる動き(スプリング)でスライドインさせます。
- CTAボタンを最後に、くっきりとフェードインさせるか、控えめな色の変化とともに表示します。
優先順位に沿って読み込み、次の内容を先読みする
何も表示する前にすべての読み込みを待つ必要はありません。ユーザーにとっての価値に沿った順序で読み込み、ユーザーが求めているものを最初に表示し、その周囲に残りの内容を埋めていきます。これは体感速度だけでなく、コードの効率性も高めます。最初に取得して描画するデータが少なくなるためです。
- まず、最も価値の高いコンテンツから — 重要な数値、主要なレコード、スクロールせずに見える範囲の回答を、準備でき次第すぐに描画します。
- 次に、その次の優先度の内容を順に — 二次パネル、関連リスト、スクロールして表示するセクションを後から読み込みます(スケルトンで表示領域を確保し、レイアウトシフトを防ぎます。上記のスケルトンセクションを参照してください)。
- 現在のビューに必要なデータだけを取得します。 タブ、ドロワー、画面外のセクションのデータは、ページ全体のデータを一度に読み込まず、開かれるまで後回しにします。
次に進むと予測できるステップを先読みします — 次のページ、ホバーした行の詳細、ウィザードの次のステップなどをバックグラウンドで読み込み、すぐに表示できるようにします。すべてを推測で読み込むのではなく、次の操作が明らかな場合に限って行います。
上級編:楽観的 UI
最も速い UI は、サーバーをまったく待たないものです。
- パターン: サーバー呼び出しが成功すると想定して、すぐに UI を更新します。失敗した場合は元に戻し、エラーを表示します。
- 用途: 投稿への「いいね」、スイッチの切り替え、フォルダーの名前変更、メッセージの削除。
- メリット: ユーザーはすぐに結果を得られ、アプリが「電光石火」のように感じられます。
待ち時間に楽しさを加える
読み込みは、無味乾燥な体験である必要はありません。2 秒を超える待ち時間では、ユーザーの関心を保つために、ブランドらしさや「楽しさ」を加えることを検討してください。
ブランドに合ったマイクロコピー
一般的な「読み込み中…」というテキストを、ブランドのトーンを反映した文言に置き換えます。
- 技術的: 「データをコンパイル中…」、「クラウドと同期中…」
- 遊び心のある表現: 「ピクセルを集めています…」、「ダッシュボードを準備中…」、「もうすぐです!」
- プロフェッショナル: 「レポートを準備中…」、「詳細を確認中…」
ブランドを反映したアニメーション(Lottie/SVG)
(アプリの初回起動や複雑なデータ処理など)重要な読み込み時には、標準のスピナーを、ブランドに特化した小さなアニメーションに置き換えます。
- デザインツールなら、鉛筆が線を描く様子を表示できます。
- フィットネスアプリなら、鼓動するハートや走る人のアイコンを表示できます。
- 金融ツールなら、積み上がるコインや、上昇するグラフの線を表示できます。
プログレッシブなストーリーテリング
待ち時間が常に長い(3 秒以上)場合は、読み込み領域を使って短いストーリーを伝えたり、役立つ情報を提供したりします。
- ヒントとコツ: 「Ctrl+K で検索できることをご存じですか?」
- 処理の透明性: システムが行っている処理を表示します:「データベースを確認中…」→「結果を最適化中…」→「表示を仕上げています…」
視覚的なトランジション(表示時)
読み込み状態からコンテンツに切り替える際は、150ms の明瞭なフェードインを使い、表示を報酬のように感じさせます。入ってくるコンテンツの拡大縮小は、レイアウトが不安定になる可能性があるため避けてください。
カーソルは読み込み状態を示すものではありません
カーソルは操作可能性を示します。通常のコンテンツには矢印、操作できる要素には pointer を使います。進行状況を示すものではありません。cursor: wait や cursor: progress では、ユーザーが見ていない場所に状態が表示され、何を読み込んでいるのか、どのくらい進んでいるのかも伝わらず、ポインターを動かした瞬間に消えてしまいます。実際に待機している要素や領域に進行状況を表示してください。コントロールについては [[button-states]] で、領域については上記のスケルトンおよび進行状況のパターンで説明しています。
ローディング状態ではできないこと
ローディング状態によって、ユーザーに待ってもらうことはできます。しかし、時間を短縮することはできず、失敗するリクエストを救うこともできません。以下のしきい値は、新たな発見ではなく、長年にわたって確立されてきた応答時間の限界です。ここから導かれることは、通常見落とされがちな点です。
- 約10秒まで: スピナーやスケルトンで待ち時間に対応できます。
- それを超える場合: インターフェースには、実際の進捗状況とキャンセルする手段が必要です。不確定なアニメーションは情報ではなくなり、未知の状態を覆い隠す装飾になります。
- 約30秒を過ぎると: どのようなアニメーションが表示されていても、ユーザーはアプリケーションが応答しなくなったと判断し、再読み込みしたり離脱したりし始めます。
リクエストが常にこうしたしきい値を超えるなら、修正すべきなのはリクエストです。 バックエンドのタイムアウトに向かっているときに、表示を充実させても、ユーザーがいずれ受け取るエラーを遅らせるだけです。その間、プロダクトが壊れていることをチームから隠してしまいます。スピナーによって失敗が後回しになるため、誰も原因を修正せず、ユーザーは結局タスクを放棄します。
待ち時間を本当に短縮できない場合、修正すべきなのはリクエストでもありません。 大規模なエクスポート、モデルの呼び出し、動画のトランスコードなど、かかる時間を短縮できない処理もあります。インジケーターを表示しても、処理時間は短くなりません。そうした場合は、処理を画面の外に移すのが解決策です。ジョブを受け付け、実行中であることをユーザーに伝え、ほかのことに取り組めるようにし、完了したら通知します。結果は、ユーザーが見つけられる場所に保存しておきます。進捗バーの前に2分間ユーザーを引き留めるのは一つの選択ですが、ユーザーがそうしたいと思うことはほとんどありません。避けるべきなのは、短縮できない待ち時間をパフォーマンスのバグとして扱い、最適化可能な待ち時間を仕方のないこととして扱うことです。この2つには正反対の対応が必要なので、待ち時間を設計する前に、どちらに当たるのか判断してください。
レビューチェックリスト
- 想定される待ち時間に適したローディング状態(スピナーかスケルトンか)になっていますか?
- スケルトン画面は、読み込まれるコンテンツの実際のレイアウトと一致していますか?
- スケルトンには、「読み込み中」であることを示す控えめなシマーアニメーションがありますか?
- 二重操作を防ぐため、読み込み中はボタンが無効になっていますか?
- コンテンツは突然表示されるのではなく、スケルトン上にフェードイン(150~200ms)しますか?
- ページ全体を読み込む場合、視線を誘導するために段階的な表示を使っていますか?
- すべてのローディングアニメーションで
prefers-reduced-motionを尊重していますか? - 「楽観的UI」の場面では、操作に失敗した場合に明確なロールバック手順がありますか?
- コンテンツは優先順位の高いものから読み込み(残りは順次表示)、最初にすべてを読み込むのではなく、現在のビューに必要なものだけを取得していますか?
- 次のステップが予測可能な場合、すべてを投機的に読み込むことなく、事前読み込みして瞬時に表示できるようにしていますか?
よくあるアンチパターン
| アンチパターン | 問題 | 修正方法 |
|---|---|---|
| アプリ全体をブロックするグローバルスピナー | 強い不満につながり、ユーザーがほかの領域を閲覧できません | 状況に応じたローダーやスケルトンを使います |
| 最終的なレイアウトと一致しないスケルトン | データの到着時にレイアウトが大きくずれます(CLS) | 形状とサイズを正確に一致させます |
| 1ページにスピナーが多すぎる | 視覚的なノイズになり、アプリ全体が壊れているように感じられます | ローディング状態を1つのコンテナスケルトンにまとめます |
| 光速より速いスケルトン | シマーアニメーションが速すぎる、またはコントラストが強すぎます | シマーはゆっくり(1.5秒以上)、ごく控えめにします |