GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

【コピペOK】CSS スケルトン ローディング 9選

css スケルトンとは、本物のコンテンツが届く前に画面の形だけを灰色の板で先に見せておくローディング表現のことです。この記事ではシマーライン・カード・表・コンテナクエリまで9種類を選び、JavaScript を使わず SCSS だけで実装しました。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

並び順は効果の派手さではなく、実際に画面を組み立てていく順番です。最初の4つ(01〜04)は行・カード・アバター・表という基本の形で、次の2つ(05〜06)は写真とレイアウト幅という「揺れやすい要素」への対処です。最後の3つ(07〜09)は、スケルトンが役目を終えて本物に切り替わる場面・長い一覧での見え方・配色の自動化という、仕上げの挙動を扱います。「css ローディングアニメーション」で探している人が知りたい細部を、項目ごとのコードに残しました。 待ち時間が短く、形をあらかじめ描く必要がない場面ではCSS ローディング スピナー9選のほうが簡単で、07のようにスケルトンが退いた後に画面そのものが入れ替わる瞬間はCSS 画面切り替え アニメーション9選が扱っています。

01シマーライン

何が起きるか

文字行の位置を灰色の棒が先取りし、::after の光の帯だけが translateX で通り過ぎます。

シマーライン
新しいタブで開く

01 · シマーライン — 計測 累積面積 11.078% · 動いたフレーム 21/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

1.4s느리게 2s빛 없이

css ローディング デザインの使いどころ

位置ではなく transform だけを動かすのでレイアウトは揺れず、行ごとに開始をわずかにずらすと1本の光が順番に流れて見えます。記事一覧やコメント欄の読み込みに向いています。

.line {
  position: relative;
  overflow: hidden;
  height: 14px;
  border-radius: 6px;
  background: rgba($color, .14);

  &::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba($color, .45), transparent);
    transform: translateX(-120%);
    animation: shimmer $dur-loop $ease-out infinite;
  }

  &:nth-child(2)::after { animation-delay: -140ms; }
}

@keyframes shimmer {
  to { transform: translateX(120%); }
}

02カードスケルトン

動きの内容

画像・見出し・説明の3つの枠を持つカード1枚です。

カードスケルトン
新しいタブで開く

02 · カードスケルトン — 計測 累積面積 28.717% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

3장가로형둥글게

使う場面

共通の光る帯を %bar というプレースホルダーセレクターにまとめ、.thumb・.title・.desc がそれぞれ @extend するだけで同じ動きを再利用できます。商品カードや記事カードの一覧に向いています。

%bar {
  position: relative;
  overflow: hidden;
  border-radius: 6px;
  background: rgba($color, .16);

  &::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba($stage-paper, .85), transparent);
    transform: translateX(-120%);
    animation: shimmer $dur-loop $ease-out infinite;
  }
}

.thumb { @extend %bar; aspect-ratio: 2 / 1; }
.title { @extend %bar; height: 14px; width: 70%; }
.desc  { @extend %bar; height: 11px; }

03アバターリスト

どんな動きか

丸いアバターと2行のテキストが5回繰り返されます。

アバターリスト
新しいタブで開く

03 · アバターリスト — 計測 累積面積 44.317% · 動いたフレーム 17/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

5줄작게글 한 줄

使いどころ

01・02が行やカードごとに光る帯を持つのに対し、03は .list 全体を覆う .sheen をひとつだけ置き、リスト全体を1本の光が横切るようにしました。チャットやフォロワー一覧に向いています。

.list {
  position: relative;
  overflow: hidden;
  display: grid;
  gap: 10px;
  border-radius: 12px;
}

.avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba($color, .22);
}

.sheen {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba($stage-paper, .4), transparent);
  transform: translateX(-120%);
  animation: shimmer $dur-loop $ease-out infinite;
  pointer-events: none;
}

04テーブルスケルトン

何が起きるか

見出し行の文字はそのまま残し、本文のセルだけを棒に置き換えます。

テーブルスケルトン
新しいタブで開く

04 · テーブルスケルトン — 計測 累積面積 45.848% · 動いたフレーム 17/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

6행열 폭 유지줄무늬

向いている場面

.row は見出しと本文で同じ grid-template-columns を共有しているので、読み込み中も列の幅がずれません。ダッシュボードの表に向いています。

.row {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 8px;
  padding: 8px 12px;
}

.body {
  position: relative;
  overflow: hidden;
}

.cell {
  height: 10px;
  border-radius: 6px;
  background: rgba($color, .13);
}

.sheen {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba($subject-blue, .45), transparent);
  transform: translateX(-120%);
  animation: shimmer $dur-loop $ease-out infinite;
}

05画像ブラーアップ

動きの内容

極小サイズの画像を blur(20px) で先に敷いておき、本物の画像がその上でふわっと現れて鮮明になります。

画像ブラーアップ
新しいタブで開く

05 · 画像ブラーアップ — 計測 累積面積 49.817% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

20px400ms확대 섞기

使う場面

イージングを ease 系ではなくあえて linear にしたのは、途中で速度が変わると「ぼけている時間」が不揃いに見えたためです。ヒーロー画像やギャラリーに向いています。

.placeholder {
  filter: blur(20px);
  transform: scale(1.15);
}

.real {
  opacity: 0;
  transform: scale(1.04);
  animation: reveal $dur-loop linear infinite;
}

@keyframes reveal {
  0%   { opacity: 0; transform: scale(1.04); }
  50%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.04); }
}

06レスポンシブスケルトン

どんな動きか

入れ物の実際の幅に応じて列数が変わります。

レスポンシブスケルトン
新しいタブで開く

06 · レスポンシブスケルトン — 計測 累積面積 56.241% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

3열2열 @ 480px1열 @ 320px

使いどころ

ビューポート幅ではなく .wrap 自身の幅を基準にする container-type: inline-size を使うので、同じファイルをサイドバーに置いても本文に置いても正しい列数になります。コンテナクエリは Chrome 105 以降・Safari 16 以降・Firefox 110 以降が対応済みで、対応状況は MDN の @container 対応表 で確認できます。

.wrap {
  container-type: inline-size;
  container-name: skeleton;
}

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

@container skeleton (max-width: 260px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}
@container skeleton (max-width: 150px) {
  .grid { grid-template-columns: 1fr; }
}

07スケルトン→本文

何が起きるか

棒が跳ねずに実際の内容へ切り替わります。

スケルトン→本文
新しいタブで開く

07 · スケルトン→本文 — 計測 累積面積 8.035% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

300ms위로 살짝페이드만

向いている場面

.skeleton と .content を同じ grid-area: 1 / 1 に重ね、opacity だけをクロスフェードさせるので、幅や高さが変わって周りの要素が飛ぶことがありません。すべてのスケルトンの最後の場面として使えます。

.card {
  width: 74%;
  display: grid;
}

.skeleton, .content {
  grid-area: 1 / 1;
}

.skeleton {
  animation: fade-out $dur-loop $ease-out infinite;
}

@keyframes fade-out {
  0%, 40%   { opacity: 1; }
  55%, 85%  { opacity: 0; }
  100%      { opacity: 1; }
}

08行ごとに時差

動きの内容

各行が少しずつ遅れて光り、波のように見えます。

行ごとに時差
新しいタブで開く

08 · 行ごとに時差 — 計測 累積面積 13.818% · 動いたフレーム 17/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

60ms역방향2열

使う場面

Sass の @for で行ごとの animation-delay を自動計算し、偶数行だけ animation-name を逆方向のキーフレームに差し替えているので、波が途中で分かれて見える効果を1つのルールで作れます。長い一覧に向いています。

.bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba($subject-blue, .35), transparent);
  transform: translateX(-120%);
  animation: shimmer $dur-loop $ease-out infinite;
}

@for $i from 1 through 6 {
  .bar:nth-child(#{$i})::after { animation-delay: -($i - 1) * 60ms; }
}

.bar:nth-child(even)::after { animation-name: shimmer-reverse; }

@keyframes shimmer-reverse {
  from { transform: translateX(120%); }
  to   { transform: translateX(-120%); }
}

09ダークモード自動配色

どんな動きか

light-dark() ひとつで、ダークモードのときに色が自動で切り替わります。

ダークモード自動配色
新しいタブで開く

09 · ダークモード自動配色 — 計測 累積面積 15.289% · 動いたフレーム 9/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

light-dark()대비 높게회색 대신 파랑

使いどころ

color-scheme: light dark を宣言した要素の中で使うと、ライト用とダーク用の2つの値のうちどちらを描くかをブラウザ自身が判断してくれるので、prefers-color-scheme の分岐を自分で書く必要がありません。ダークモードを持つすべてのサイトに向いています。

:root {
  color-scheme: light dark;
}

.card {
  background: light-dark(#f5f2e9, #1b1b1f);
  color: light-dark(#1b1b1f, #f5f2e9);
}

.bar {
  background: light-dark(rgba(#1b1b1f, .16), rgba(#f5f2e9, .22));
}

どこで崩れるか — 落とし穴

光の帯がはみ出す

9つとも overflow: hidden を持つ親要素の中でだけ光の帯を動かしています。01を作ったとき、この overflow: hidden を忘れると光の帯がカードの外側にまではみ出し、隣の要素の上を横切って見えるという崩れ方をしました。position: relative の親に overflow: hidden を必ずセットで書くのが、シマー系5つ(01・02・03・04・08)に共通する条件です。

コンテナクエリの例外

もうひとつは06のコンテナクエリです。デモでは仕組みを見せるためにあえて .wrap の width を keyframes で動かしていますが、これは「揺れやすい要素」を可視化するための例外で、コンテナクエリ自体は transform の変化には反応しません。実際のページでは、サイドバーの開閉やウィンドウのリサイズのような本物のボックス幅の変化に対して .grid が反応します。修正済みの9ファイルを開くには a3rjf9rd という文字列が必要です。下のカードから zip を取ってください。

07の受け渡しがカード1枚ではなく画面全体の規模になると、この落とし穴もそのまま大きくなります。その規模の切り替えはcss ページ遷移 アニメーション9選で別に扱いました。

ブラウザ対応

3つ目はブラウザの対応です。light-dark() と @container はどちらも比較的新しい構文で、09の light-dark() は Chrome・Edge 123 以降と Safari 17.5 以降が対応済みですが、MDN の light-dark() 対応表 にあるとおり古いブラウザでは値そのものが無効になり、背景色が透明のまま抜けてしまいます。zip のファイルは @supports で分岐させ、非対応ブラウザでは固定の色にフォールバックするようにしてあります。

アクセシビリティ

動きを減らす設定のとき

9つとも prefers-reduced-motion: reduce で光の移動や拡大縮小の animation を止めます。ただし止めた後に何を残すかは項目ごとに違い、01・02・03・04・08は光を止めて薄い一定の明るさだけを残し、05は最も鮮明な最終状態を、07は本文が見えている状態を、09はダークモードのカードを固定で表示します。06だけは動きを止めても列数の変化そのものが機能なので、width のアニメーションだけを止め、コンテナクエリの判定はそのまま生かしています。

@media (prefers-reduced-motion: reduce) {
  .line::after, .sheen { animation: none !important; opacity: .4; }
  .real { animation: none !important; opacity: 1; transform: scale(1); }
}

ローディング アニメーション css のみで作れるスケルトン以外のパターンは CSS アニメーションのカテゴリ に、このサイトについては 紹介ページ にまとめています。

FAQ

スケルトンスクリーン css とローディングスピナー、どちらを使うべきですか?

読み込むものの形があらかじめ分かっているなら、スケルトンスクリーンのほうが向いています。カードや表のように最終的な形が決まっている場所ではスケルトンが「もうすぐこの形の内容が来る」と伝えられますが、形が定まらない処理中の待ち時間にはスピナーのほうが誤解を招きません。

9つを1つのページに全部入れても大丈夫ですか?

入れられますが、06のコンテナクエリだけは実際のレイアウトで使うときに親のボックス幅がはっきり決まっている場所に置いてください。幅が決まっていない場所に置くと、列数の切り替わる境目が意図しない位置にずれることがあります。それ以外の8つは1つのページに何個置いても互いに影響しません。

React ではどう書きますか?

zip の react/ フォルダのコンポーネントは、状態管理をせずクラス名を1つ付けるだけで9つとも同じ見た目になります。09の light-dark() だけは CSS 側で完結しているため、ダークモード用の状態を React 側に持つ必要がありません。 同じ読み込み中の状態は、たいてい要求を出したボタンにも渡ります。押されたボタン自身がローディングへ変わる項目はCSS ボタン アニメーション 9選に入っています。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。