Claude Codeでスケルトンローディングを実装する実践ガイド
Claude Codeでスケルトンローディングを安全に実装。React例、CLS対策、アクセシビリティ、失敗例まで解説。
スケルトンローディングは、データを読み込んでいる間に「ここに画像が入る」「ここに見出しが入る」という薄い骨組みを先に見せるUIです。初心者向けに言うと、白紙の画面で待たせる代わりに、完成後の画面の席取りをしておく仕組みです。
スピナーは「処理中です」と伝えるだけですが、スケルトンは「どんな情報が、どの位置に出るか」を先に伝えます。さらに、画像や広告やAPI結果があとから入っても表示領域を確保しておけば、ページがガクッと動く失敗を減らせます。この視覚的な安定性は、web.devのCumulative Layout ShiftやCore Web Vitalsでも重視されています。
この記事では、Claude Codeにスケルトンローディングを作らせるときの依頼文、Reactでコピペして試せる実装、CSSの動き、アクセシビリティ、落とし穴をまとめます。関連して、表示速度の全体像はClaude Codeパフォーマンス最適化、画像読み込みは画像遅延読み込み、読み上げ対応はアクセシビリティ実装ワークフローも参考にしてください。
まず何を作るかを分解する
スケルトンは「灰色の長方形を置く」だけではありません。読み込み前、読み込み中、成功、空状態、失敗の5つを同じレイアウトの中で扱う必要があります。Claude Codeにいきなり「いい感じのスケルトンを作って」と頼むと、表示は整っても、失敗時のUIや読み上げ通知が抜けがちです。
flowchart LR
P["Claude Codeへの指示"] --> S["同じ寸法のスケルトン"]
S --> D["成功時の実データ"]
D --> E["空状態"]
D --> X["エラー表示"]
S --> A["aria-busy / status"]
S --> M["prefers-reduced-motion"]
S --> C["CLS確認"]
最初の依頼文は、実装範囲と検証条件をまとめて渡します。
Read the existing card/list components before editing.
Implement skeleton loading only for the article cards list.
Keep the skeleton dimensions close to the loaded content.
Handle loading, empty, error, and success states.
Respect prefers-reduced-motion and avoid layout shift.
Add a small Playwright check if the project already uses Playwright.
Do not change unrelated styles, routing, or data fetching.
ここでいうprefers-reduced-motionは、ユーザーがOSやブラウザで「動きを減らしたい」と指定しているかをCSSで見る条件です。強いシマーアニメーションは人によって負担になるため、MDNのprefers-reduced-motionを確認し、動きを止める分岐を入れておきます。
実務で使う3つ以上のユースケース
スケルトンローディングが効く場面は、単に通信が遅い画面だけではありません。次のような「待ち時間の理由が読者に見えにくい画面」で特に役立ちます。
| ユースケース | 何を骨組みにするか | 注意点 |
|---|---|---|
| 記事カード一覧 | サムネイル、タイトル2行、説明、タグ | 実画像の高さを固定し、カードの高さを大きく変えない |
| ダッシュボード | KPIカード、グラフ枠、最近の履歴 | 数字だけ先に出すと誤解されるため、まとまって切り替える |
| ECの商品一覧 | 商品画像、商品名、価格、評価 | 価格や在庫が古い状態で見えないようにする |
| 管理画面のテーブル | ヘッダー、行、操作ボタン領域 | 行数が読み込み後に激変する場合はページネーションも見直す |
| 相談・問い合わせLP | 実績カード、CTA、FAQ | CTAだけ遅れて出ると収益導線が下に押し出される |
MasaがClaudeCodeLabの相談導線で試したときは、記事カードとCTAの高さを先に固定しただけで、読み込み中の違和感がかなり減りました。一方で、本文より大きいスケルトンを置くと、読み込み完了時に逆方向のズレが起きます。スケルトンは派手な演出ではなく、完成画面の寸法を先に予約するための部品です。
コピペで試せるReactコンポーネント
次の例はVite + React + TypeScriptのsrc/App.tsxに貼って動かせる最小構成です。実APIの代わりにsetTimeoutで遅延させ、読み込み、成功、空状態、失敗を切り替えます。Claude Codeに依頼するときも、このくらい状態を明示したサンプルを先に渡すと、差分がレビューしやすくなります。
import { useEffect, useState } from "react";
import "./skeleton-demo.css";
type Article = {
id: number;
title: string;
description: string;
tag: string;
};
type LoadState = "loading" | "success" | "empty" | "error";
const demoArticles: Article[] = [
{
id: 1,
title: "Claude Codeで安全なUI差分を作る",
description: "既存コンポーネントを読ませてから、カード一覧の読み込み体験だけを改善します。",
tag: "UX",
},
{
id: 2,
title: "CLSを増やさない画像枠の決め方",
description: "画像、タイトル、説明文の高さを先に予約し、読み込み完了後のズレを減らします。",
tag: "Performance",
},
{
id: 3,
title: "アクセシブルなローディング表示",
description: "aria-busy、status、motion設定を組み合わせて、待ち時間を静かに伝えます。",
tag: "A11y",
},
];
function SkeletonLine({ width = "100%" }: { width?: string }) {
return <span className="sk-line" style={{ width }} aria-hidden="true" />;
}
function ArticleCardSkeleton() {
return (
<article className="article-card is-skeleton" aria-hidden="true">
<div className="sk-media" />
<div className="article-card__body">
<SkeletonLine width="46%" />
<SkeletonLine />
<SkeletonLine width="86%" />
<SkeletonLine width="32%" />
</div>
</article>
);
}
function ArticleCard({ article }: { article: Article }) {
return (
<article className="article-card">
<div className="article-card__media">{article.tag}</div>
<div className="article-card__body">
<p className="article-card__tag">{article.tag}</p>
<h2>{article.title}</h2>
<p>{article.description}</p>
</div>
</article>
);
}
export default function App() {
const [state, setState] = useState<LoadState>("loading");
const [articles, setArticles] = useState<Article[]>([]);
useEffect(() => {
const timer = window.setTimeout(() => {
setArticles(demoArticles);
setState("success");
}, 1200);
return () => window.clearTimeout(timer);
}, []);
const reloadAs = (nextState: LoadState) => {
setState("loading");
setArticles([]);
window.setTimeout(() => {
setArticles(nextState === "success" ? demoArticles : []);
setState(nextState);
}, 700);
};
return (
<main className="demo-shell">
<div className="demo-toolbar" aria-label="表示状態を切り替える">
<button onClick={() => reloadAs("success")}>成功</button>
<button onClick={() => reloadAs("empty")}>空状態</button>
<button onClick={() => reloadAs("error")}>エラー</button>
</div>
<section
aria-busy={state === "loading"}
aria-describedby="article-list-status"
className="article-grid"
>
<p id="article-list-status" className="sr-only" role="status">
{state === "loading" ? "記事一覧を読み込んでいます" : "記事一覧の読み込みが完了しました"}
</p>
{state === "loading" &&
Array.from({ length: 3 }).map((_, index) => (
<ArticleCardSkeleton key={index} />
))}
{state === "success" &&
articles.map((article) => (
<ArticleCard key={article.id} article={article} />
))}
{state === "empty" && (
<div className="state-panel">該当する記事はまだありません。</div>
)}
{state === "error" && (
<div className="state-panel" role="alert">
記事一覧を読み込めませんでした。時間をおいて再試行してください。
</div>
)}
</section>
</main>
);
}
アクセシビリティで大事なのは、スケルトン自体を読み上げ対象にしすぎないことです。灰色の線が3本あることを読み上げても意味がありません。上の例では、スケルトンカードにはaria-hidden="true"を付け、リストの状態だけをrole="status"で静かに知らせています。ARIAのstatusロールは、緊急ではない状態変化を伝える用途です。詳しくはMDNのARIA: status roleを確認してください。
CSSで寸法と動きを固定する
次のCSSをsrc/skeleton-demo.cssとして保存します。ポイントは、読み込み中と読み込み後でカードのmin-height、画像枠、本文の余白を大きく変えないことです。シマーは控えめにし、動きを減らす設定ではアニメーションを止めます。
:root {
color: #18212f;
background: #f6f7f9;
font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
button {
min-height: 40px;
border: 1px solid #b8c2d6;
border-radius: 8px;
background: #ffffff;
color: #18212f;
padding: 0 14px;
font-weight: 700;
}
.demo-shell {
width: min(1040px, calc(100% - 32px));
margin: 40px auto;
}
.demo-toolbar {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 18px;
}
.article-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}
.article-card {
min-height: 316px;
overflow: hidden;
border: 1px solid #d7deea;
border-radius: 8px;
background: #ffffff;
}
.article-card__media,
.sk-media {
display: grid;
min-height: 148px;
place-items: center;
background: #dfe7f3;
color: #39506f;
font-weight: 800;
}
.article-card__body {
display: grid;
gap: 10px;
padding: 18px;
}
.article-card__tag {
color: #3b6b4f;
font-size: 0.875rem;
font-weight: 800;
}
.article-card h2 {
min-height: 56px;
margin: 0;
font-size: 1.16rem;
line-height: 1.45;
}
.article-card p {
margin: 0;
line-height: 1.7;
}
.sk-line,
.sk-media {
border-radius: 8px;
background: linear-gradient(90deg, #d9e0ea 25%, #edf1f7 37%, #d9e0ea 63%);
background-size: 240% 100%;
animation: skeleton-shimmer 1.4s ease-in-out infinite;
}
.sk-line {
display: block;
height: 16px;
}
.state-panel {
min-height: 180px;
display: grid;
place-items: center;
border: 1px solid #d7deea;
border-radius: 8px;
background: #ffffff;
padding: 24px;
text-align: center;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
}
@keyframes skeleton-shimmer {
from {
background-position: 120% 0;
}
to {
background-position: -120% 0;
}
}
@media (prefers-reduced-motion: reduce) {
.sk-line,
.sk-media {
animation: none;
background: #d9e0ea;
}
}
このCSSは「綺麗な光り方」よりも「レイアウトが動かないこと」を優先しています。スケルトンの幅をランダムにしすぎると、実データに切り替わったときに文章量の違いが目立ちます。タイトルは2行相当、説明は2行相当など、完成画面に近い寸法を先に決めるほうが実務では安定します。
Playwrightで最低限の崩れを検出する
すでにPlaywrightを使っているプロジェクトなら、スケルトンの表示確認を1本だけ追加しておくと安心です。次の例は、読み込み中にカードが3枚出ること、状態切り替え後にエラー表示が見えること、カード高さが極端に小さくないことを見ます。
import { expect, test } from "@playwright/test";
test("article skeleton keeps a stable card area", async ({ page }) => {
await page.goto("/");
await expect(page.getByText("記事一覧を読み込んでいます")).toBeAttached();
await expect(page.locator(".is-skeleton")).toHaveCount(3);
const firstBox = await page.locator(".article-card").first().boundingBox();
expect(firstBox?.height).toBeGreaterThan(280);
await page.getByRole("button", { name: "エラー" }).click();
await expect(page.getByRole("alert")).toContainText("読み込めませんでした");
});
このテストだけでCLSを完全に保証できるわけではありません。実際のCLSはユーザー環境、画像サイズ、広告、フォント、第三者タグの影響を受けます。だからこそ、スケルトンの自動テストは「明らかな崩れを早く見つけるもの」と割り切り、本番ではweb.devのCLS解説にあるようにフィールドデータも確認します。
よくある失敗と落とし穴
1つ目の失敗は、スケルトンが実コンテンツと違う大きさになることです。読み込み中は整って見えても、実データに切り替わる瞬間にカードが伸び縮みします。画像にはwidth、height、aspect-ratioのどれかを決め、広告やCTAも表示前から枠を確保します。
2つ目は、読み込みが速い画面にも必ずスケルトンを出すことです。100ms程度で終わる処理に毎回スケルトンを挟むと、かえってチラつきます。実務では「300msを超えたら表示する」「初回だけ表示する」「再取得時は既存データを薄く残す」などのルールを決めます。
3つ目は、アクセシビリティ通知の出しすぎです。カードごとにrole="status"を置くと、スクリーンリーダー利用者に同じ通知が何度も届く可能性があります。通知はリスト単位にまとめ、スケルトンの形状はaria-hiddenにします。
4つ目は、失敗状態を作らないことです。APIエラー時にもスケルトンを出し続けると、ユーザーには「待てば表示される」と見えてしまいます。エラー、空状態、再試行ボタンを別の状態として設計します。
5つ目は、Claude Codeにデザイン判断を丸投げすることです。Claude Codeは既存ファイルを読めますが、事業上どのCTAを優先するか、広告枠をどこまで予約するか、どの情報を先に見せるかは人間の判断です。依頼文には「触ってよいコンポーネント」「変えてはいけないCTA」「確認する画面幅」を具体的に書きます。
Claude Codeにレビューさせる観点
実装後は、生成したコードをそのまま公開せず、次のようにレビューを依頼します。
Review only the skeleton loading changes.
Check whether loaded content and skeleton content reserve similar space.
Check loading, success, empty, and error states.
Check reduced-motion behavior and ARIA announcements.
Point out any code that may increase CLS or create repeated screen reader messages.
Return findings with file names and exact lines.
このレビュー指示は、Claude Codeを「実装者」から「批判的なレビュアー」に切り替えるためのものです。特に記事サイトでは、広告枠、関連記事、CTA、画像の遅延読み込みが絡むため、見た目だけの確認では足りません。CSSスタイリング実践ガイドやテスト戦略と合わせて、表示、読み上げ、回帰テストを分けて確認してください。
収益導線としてのスケルトン
スケルトンローディングはUX改善だけでなく、収益導線の保護にも関係します。相談CTA、教材カード、ニュースレター登録、広告枠があとから出て本文を押し下げると、読者は意図しない位置でクリックしたり、読みたい段落を見失ったりします。これは信頼にもCVにも悪影響です。
個人でClaude CodeのUI改善を進めるなら、まず無料チートシートで変更前後の確認手順を固定してください。スケルトン、画像遅延読み込み、Core Web Vitals、アクセシビリティをチームの標準に落とし込みたい場合は、Claude Code研修・導入相談でMasaが既存リポジトリを前提に改善順序、レビュー観点、テンプレート化まで一緒に整理します。
まとめ
良いスケルトンローディングは、待ち時間をごまかす演出ではありません。完成画面に近い寸法を先に予約し、読み込み中の不安を減らし、レイアウトシフトを起こしにくくするための設計です。Claude Codeを使う場合は、対象コンポーネント、状態、寸法、アクセシビリティ、検証コマンドをまとめて渡すと、生成コードの品質が大きく変わります。
この記事で紹介した内容を実際に試した結果、カード一覧では「画像枠とタイトル高さを先に固定する」「スケルトンをリスト単位で読み上げる」「動きを減らす設定でアニメーションを止める」の3点が最も効きました。逆に、シマーの見た目だけを先に作ると、空状態やエラー状態が後回しになり、公開前レビューで手戻りします。Claude Codeには実装だけでなく、失敗時の表示と検証までセットで依頼するのが現実的です。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Code権限セーフティラダー: 初心者がallowを広げる順番
Claude Codeの権限をread-onlyからbuild、限定編集、deploy確認まで段階的に広げる安全な運用手順。
Claude Code Small PR Proof Pack: 小さなPRをレビュー可能にする証拠セット
Claude Codeの小さなPRに、差分・検証・公開URL・CTA・rollbackを添える実務チェックリスト。
Claude Codeのコミット前レビューゲート: 差分、テスト、CTAをまとめて止める型
Claude Codeでcommit前に差分をレビューする実践手順。build、公開URL、CTA、Gumroadリンク、未翻訳本文を検知します。