本文へスキップ

スケルトンローディング

読み込み中に輝く光が流れるプレースホルダを表示し、完了後に実コンテンツへ差し替わる。

プレビュー

クリック

操作クリック/タップで動きます。

使いどころ

読み込み後の見た目がある程度予測できる場面に向く。カード一覧・プロフィール・記事一覧のように、事前にレイアウト(アバターの円・見出しの帯・本文の帯)が決まっているコンテンツなら、実データの形に合わせたグレーの帯を先に置くことで、読み込み中でも画面がガタつかず「もうすぐこの形の情報が出る」と予測させられる。スピナーと違って最終レイアウトの寸法をあらかじめ占有するため、読み込み完了時のレイアウトシフトも防げる。逆に、最終的な形が読み込むまで分からないコンテンツ(件数や画像の縦横比が毎回変わる一覧)や、0.3〜0.5秒未満で終わる短い読み込みには向かない。形の合わない骨組みを一瞬見せるとちらつきの方が目立ち、スピナーか何も出さない方が自然になる。単発のボタン処理や小さいアイコン1個の差し替えのような形を持たない読み込みには、スケルトンではなくスピナーを使う方がよい。

仕組み

.sk要素にposition:relative; overflow:hiddenを与え、::afterでlinear-gradient(透明→白50%→透明)の帯をtransform:translateX(-100%)からtranslateX(100%)へ1.4秒かけて流すことで光沢(シマー)を作る。transformだけを動かしているためコンポジタで処理でき、帯を何個並べても軽い。骨組みの構造(アバターの円+見出し2行)は実カードのDOM構造をそのまま模しており、幅(長い行100%/中くらいの行65%)や高さを実コンテンツの寸法に近づけるほど差し替え時の見た目のジャンプが小さくなる。表示切替はhidden属性で行うが、.skeleton-cardや.real-cardはクラスでdisplay:flexを指定しているため、hidden属性だけでは隠れない。実装ではhidden時にdisplay:noneを明示的に上書きして、属性セレクタの詳細度でクラスのdisplay指定に勝たせている。この上書きを忘れると、hiddenを付けても要素がflexのまま画面に残ってしまう。

実装の注意

最大の罠は上記のhidden属性とdisplay:flexの詳細度の衝突で、これを見落とすとJSでhiddenを切り替えても骨組みが消えない。次に、このサンプルの読み込みはsetTimeout(2200)の固定待ちだが、実装では実際のfetch完了に連動させる必要があり、その際「速く返ってきた場合」への配慮が抜けやすい。データが200ms未満で返るとスケルトンが一瞬だけ点滅してかえってちらついて見えるため、表示するかどうか自体に遅延を入れる(例えば200ms待ってまだ読み込み中ならスケルトンを出す)か、出したら最低500ms程度は表示を保つ実装にするとよい。また、骨組みのマークアップと実カードのマークアップを別々に手書きしているため、実コンテンツのレイアウトを変更したときに骨組み側の更新を忘れやすく、寸法がずれて差し替え時にガクッと動く原因になる。可能ならCSSの寸法値を共有の変数にしておくと乖離を防げる。

アクセシビリティ

このサンプルの骨組み要素は文字を持たないdivのみなので、スクリーンリーダーには何も読み上げられず実害は少ないが、状態としては読み込み中であることが支援技術に伝わっていない。実装では骨組みと実カードを包む親要素にaria-busy=trueを付け、実コンテンツへの差し替えと同時にaria-busy=falseへ戻すのが基本形になる。読み込み完了を音声で知らせたい場合は、親要素とは別に用意したaria-live=polite領域に「読み込みました」のような短い文言を差し込む(骨組み自体をlive領域にすると、生成・削除のたびに空の変化が読み上げられかねないため分離した方が安全)。もう一点、このCSSのシマーアニメーションにはprefers-reduced-motionの分岐が無い(同じ辞典のローディングスピナーには存在する)。実装するときはリング・ドットと同様に、reduce設定では光の帯を止めて静的なグレー背景に倒す一文を追加すべきである。

調整して使う

変えられる値は主に3つ。帯の速度(1.4秒が既定。遅くすると上品だが動いている感が弱まり、0.9〜1.6秒が実用域)、骨組みの配色(明るい背景ではベージュ系、ダークテーマでは暗いグレーに薄い白の帯)、そして骨組みの粒度である。今回はアバター+2行だが、画像ブロックやボタン形の.skを追加すれば、より複雑なカード(サムネイル付き記事、商品カードなど)にも同じ手法で対応できる。一覧表示では複数枚の骨組みを縦に並べ、各カードのanimation-delayを少しずつずらす(0.1秒刻みなど)と、光が上から下へ流れるように見えて単調さが減る。シマーの帯を流す代わりに、骨組み全体をopacityで明滅させるパルス型に差し替える設計もあり、帯より落ち着いた印象になるぶん動いて読み込み中だという手がかりはやや弱まる。

コード(コピーして使えます)

<div class="card-wrap">
  <div class="skeleton-card" id="skel">
    <div class="sk sk-avatar"></div>
    <div class="sk-lines">
      <div class="sk sk-line sk-line--long"></div>
      <div class="sk sk-line sk-line--mid"></div>
    </div>
  </div>
  <div class="real-card" id="real" hidden>
    <div class="real-avatar">🧑‍💻</div>
    <div class="real-lines">
      <p class="real-name">田中 太郎</p>
      <p class="real-sub">フロントエンドエンジニア</p>
    </div>
  </div>
  <button class="reload-btn" id="reload">再読み込み</button>
</div>

タグ

同じカテゴリ「マイクロ・フィードバック」のほかのパーツ

関連パーツ