T-CREATOR

Tailwind CSS でローディングインジケータやスピナーを自作する方法

Tailwind CSS でローディングインジケータやスピナーを自作する方法

Webアプリケーション開発において、ユーザーエクスペリエンスを向上させる要素として、ローディング表示は非常に重要な役割を果たします。「今何が起こっているのか」を視覚的に伝えることで、ユーザーの不安を解消し、待ち時間をより快適に感じてもらえるんです。

今回は、人気の CSS フレームワークである Tailwind CSS を使って、美しく実用的なローディングインジケータやスピナーを自作する方法をご紹介いたします。ライブラリに頼らず自分でカスタマイズできるようになれば、ブランドイメージに合った独自のローディング表示が実現できますよ。

背景

現代のWebアプリケーションでは、API通信やデータ処理など、時間のかかる処理が日常的に発生します。特にSPA(Single Page Application)では、ページ遷移なしに非同期でデータを取得することが多く、ユーザーは「今何が起こっているのか」を把握しにくい状況に置かれがちです。

スマートフォンの普及により、ネットワーク環境も多様化しており、通信速度の遅い環境でのユーザー体験も考慮する必要があります。Googleの調査によると、ページの読み込み時間が3秒を超えると、53%のモバイルユーザーがサイトを離脱してしまうという結果も出ています。

Tailwind CSS の人気が高まる中で、既存のUIライブラリやコンポーネントライブラリに依存せず、独自のデザインシステムを構築したいという開発者も増えています。しかし、ローディングアニメーションについては、「どのように実装すればよいのか分からない」という声もよく耳にします。

課題

多くの開発者が直面する課題として、以下のような点が挙げられます。

既存ライブラリへの依存 外部のローディングライブラリを使用すると、バンドルサイズが増加し、パフォーマンスに影響を与える可能性があります。また、ライブラリのアップデートやメンテナンス状況に左右されるリスクも存在します。

デザインの制約 既存のローディングコンポーネントでは、企業やプロダクトのブランドイメージに合ったカスタマイズが難しい場合があります。色やサイズ、アニメーションの細かな調整ができないことも多いですね。

アクセシビリティの配慮不足 視覚的なローディング表示だけでなく、スクリーンリーダーなどの支援技術を使用するユーザーへの配慮も重要です。適切なARIA属性の設定やsemantic HTMLの使用が求められます。

パフォーマンスの最適化 CPUに負荷をかけるアニメーションや、必要以上に複雑なアニメーションは、特にモバイルデバイスでのパフォーマンスに悪影響を与えることがあります。

解決策

これらの課題を解決するため、Tailwind CSS の豊富なユーティリティクラスとCSS アニメーションを組み合わせて、軽量で美しいローディングインジケータを自作する方法をご提案いたします。

Tailwind CSS のanimate-spinanimate-pulseanimate-bounceといった標準のアニメーションクラスを基本として、カスタムアニメーションを追加することで、多様なローディング表現が可能になります。

また、beforeafter疑似要素を活用することで、HTMLの構造をシンプルに保ちながら、視覚的に魅力的なアニメーションを実現できます。レスポンシブデザインにも対応し、あらゆるデバイスで美しく表示されるローディング表示を作成していきましょう。

基本的なスピナーの実装

それでは、実際にローディングスピナーを実装していきます。まずは基本的な3つのパターンから始めて、段階的に高度な表現へと発展させていきますね。

円形スピナー

最もポピュラーで直感的な円形スピナーから実装してみましょう。この手法は、境界線の一部を透明にして回転させることで、スムーズな回転効果を生み出します。

html<!-- 基本の円形スピナー -->
<div class="flex items-center justify-center">
  <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
</div>

上記のコードでは、animate-spinクラスによって要素が360度回転し、border-b-2で下側のボーダーのみを表示することで、回転するラインを表現しています。

より視覚的にインパクトのある円形スピナーも作成できます。こちらは複数の境界線を使用したバリエーションです。

html<!-- グラデーション付き円形スピナー -->
<div class="flex items-center justify-center">
  <div class="animate-spin rounded-full h-12 w-12 border-4 border-gray-200">
    <div class="rounded-full h-full w-full border-4 border-transparent border-t-blue-500"></div>
  </div>
</div>

このスピナーでは、外側のグレーの円を背景として、内側の青いボーダーが回転することで、より立体的な印象を与えます。

さらに高度な表現として、複数のリングが異なる速度で回転するスピナーも実装できます。これには少しのカスタムCSSが必要になります。

html<!-- 複数リング型スピナー -->
<div class="relative flex items-center justify-center">
  <div class="animate-spin rounded-full h-16 w-16 border-2 border-blue-500 border-t-transparent"></div>
  <div class="absolute animate-spin rounded-full h-12 w-12 border-2 border-green-500 border-b-transparent" style="animation-duration: 1.5s;"></div>
  <div class="absolute animate-spin rounded-full h-8 w-8 border-2 border-red-500 border-l-transparent" style="animation-duration: 2s;"></div>
</div>

この実装では、3つの異なるサイズのリングが、それぞれ異なる速度で回転することで、非常にダイナミックな視覚効果を生み出します。

ドットスピナー

続いて、ドット型のスピナーを実装してみましょう。この手法は、複数の小さな要素が順番にアニメーションすることで、波のような動きを表現します。

html<!-- 3つのドットスピナー -->
<div class="flex space-x-2 items-center justify-center">
  <div class="w-3 h-3 bg-blue-500 rounded-full animate-bounce"></div>
  <div class="w-3 h-3 bg-blue-500 rounded-full animate-bounce" style="animation-delay: 0.1s;"></div>
  <div class="w-3 h-3 bg-blue-500 rounded-full animate-bounce" style="animation-delay: 0.2s;"></div>
</div>

各ドットに異なるanimation-delayを設定することで、時間差でバウンスし、リズミカルな動きを作り出しています。

さらに洗練されたドットスピナーとして、パルス効果を組み合わせた表現も可能です。

html<!-- パルス型ドットスピナー -->
<div class="flex space-x-1 items-center justify-center">
  <div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
  <div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style="animation-delay: 0.2s;"></div>
  <div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style="animation-delay: 0.4s;"></div>
  <div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style="animation-delay: 0.6s;"></div>
  <div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style="animation-delay: 0.8s;"></div>
</div>

この実装では、5つのドットが順番に透明度を変化させることで、まるで波が伝播するような美しいエフェクトを実現しています。

スケール変化を使ったドットスピナーも非常に効果的です。以下のような実装になります。

html<!-- スケール変化型ドットスピナー -->
<div class="flex space-x-2 items-center justify-center">
  <div class="w-4 h-4 bg-indigo-500 rounded-full transform scale-75 animate-ping"></div>
  <div class="w-4 h-4 bg-indigo-500 rounded-full transform scale-75 animate-ping" style="animation-delay: 0.3s;"></div>
  <div class="w-4 h-4 bg-indigo-500 rounded-full transform scale-75 animate-ping" style="animation-delay: 0.6s;"></div>
</div>

バーのローディング

プログレス表示に適したバー型のローディングインジケータも実装してみましょう。これは特にファイルアップロードやデータ処理の進捗を表示する際に有効です。

まずは、左右に移動するインジケータバーを作成します。

html<!-- 水平移動バー -->
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
  <div class="h-full bg-blue-500 rounded-full w-1/3 animate-pulse transform translate-x-0 transition-transform duration-1000" 
       style="animation: slideBar 2s ease-in-out infinite;"></div>
</div>

このバーローディングをより効果的にするため、カスタムアニメーションを追加します。

css/* カスタムアニメーションの定義 */
@keyframes slideBar {
  0% { transform: translateX(-100%); }
  50% { transform: translateX(200%); }
  100% { transform: translateX(-100%); }
}

さらに高度な表現として、複数のバーが順番にアニメーションするローディング表示も作成できます。

html<!-- 複数バーローディング -->
<div class="space-y-2 w-full max-w-sm">
  <div class="flex space-x-1">
    <div class="w-4 h-8 bg-blue-500 rounded animate-pulse" style="animation-delay: 0s;"></div>
    <div class="w-4 h-8 bg-blue-500 rounded animate-pulse" style="animation-delay: 0.1s;"></div>
    <div class="w-4 h-8 bg-blue-500 rounded animate-pulse" style="animation-delay: 0.2s;"></div>
    <div class="w-4 h-8 bg-blue-500 rounded animate-pulse" style="animation-delay: 0.3s;"></div>
    <div class="w-4 h-8 bg-blue-500 rounded animate-pulse" style="animation-delay: 0.4s;"></div>
  </div>
</div>

このイコライザー風のアニメーションは、音楽アプリやオーディオ処理系のアプリケーションで特に効果的です。

カスタマイズ方法

基本的なスピナーの実装ができたところで、実際のプロジェクトに合わせてカスタマイズする方法を詳しく見ていきましょう。

サイズの調整

Tailwind CSS のサイズユーティリティを活用することで、レスポンシブなローディング表示を簡単に実現できます。

html<!-- レスポンシブサイズ対応 -->
<div class="flex items-center justify-center">
  <!-- 小画面用 -->
  <div class="sm:hidden animate-spin rounded-full h-6 w-6 border-b-2 border-blue-500"></div>
  
  <!-- 中画面用 -->
  <div class="hidden sm:block md:hidden animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
  
  <!-- 大画面用 -->
  <div class="hidden md:block animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
</div>

テーブルやカード内での小さなローディング表示には、以下のようなコンパクトなサイズが適しています。

html<!-- コンパクトサイズ -->
<div class="inline-flex items-center">
  <div class="animate-spin rounded-full h-4 w-4 border border-gray-300 border-t-blue-600"></div>
  <span class="ml-2 text-sm text-gray-600">読み込み中...</span>
</div>

色の変更

ブランドカラーに合わせたローディング表示は、一貫したデザイン体験を提供します。Tailwind CSS のカラーパレットを活用して、様々な色のバリエーションを作成してみましょう。

html<!-- 複数色のグラデーションスピナー -->
<div class="flex space-x-4 items-center justify-center">
  <!-- プライマリーカラー -->
  <div class="animate-spin rounded-full h-10 w-10 border-2 border-indigo-200 border-t-indigo-600"></div>
  
  <!-- 成功カラー -->
  <div class="animate-spin rounded-full h-10 w-10 border-2 border-green-200 border-t-green-600"></div>
  
  <!-- 警告カラー -->
  <div class="animate-spin rounded-full h-10 w-10 border-2 border-yellow-200 border-t-yellow-600"></div>
  
  <!-- エラーカラー -->
  <div class="animate-spin rounded-full h-10 w-10 border-2 border-red-200 border-t-red-600"></div>
</div>

ダークモード対応も重要な考慮点です。以下のようにdark:プレフィックスを使用することで、テーマに応じて適切な色を表示できます。

html<!-- ダークモード対応スピナー -->
<div class="flex items-center justify-center">
  <div class="animate-spin rounded-full h-8 w-8 border-2 border-gray-300 border-t-blue-600 
              dark:border-gray-600 dark:border-t-blue-400"></div>
</div>

アニメーション速度の調整

ユーザビリティを向上させるため、適切なアニメーション速度の設定も重要です。処理時間が短い場合は速めに、長時間の処理では少し遅めに設定することで、ユーザーストレスを軽減できます。

html<!-- 速度バリエーション -->
<div class="flex space-x-6 items-center justify-center">
  <!-- 高速(緊急性を表現) -->
  <div class="animate-spin rounded-full h-8 w-8 border-2 border-gray-200 border-t-red-600" 
       style="animation-duration: 0.5s;"></div>
  
  <!-- 標準速度 -->
  <div class="animate-spin rounded-full h-8 w-8 border-2 border-gray-200 border-t-blue-600"></div>
  
  <!-- 低速(リラックス感を表現) -->
  <div class="animate-spin rounded-full h-8 w-8 border-2 border-gray-200 border-t-green-600" 
       style="animation-duration: 2s;"></div>
</div>

カスタム CSS でより細かな制御も可能です。

css/* アニメーション速度のカスタム定義 */
@keyframes customSpin {
  0% { transform: rotate(0deg); }
  25% { transform: rotate(90deg); }
  50% { transform: rotate(180deg); }
  75% { transform: rotate(270deg); }
  100% { transform: rotate(360deg); }
}

.custom-loading {
  animation: customSpin 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

実用的な活用例

理論だけでなく、実際のWebアプリケーションでよく遭遇するシナリオでの実装例を見ていきましょう。これらの例は、すぐに実際のプロジェクトで活用していただけます。

ボタンと組み合わせたローディング

フォーム送信や API 呼び出し時のボタンローディング状態は、ユーザビリティの観点から非常に重要です。重複送信を防ぎ、処理中であることを明確に伝えることができます。

html<!-- ローディング状態のボタン(JavaScript連携前提) -->
<button id="submitBtn" 
        class="relative px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 
               disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200">
  <span id="btnText">送信する</span>
  <div id="btnLoader" class="hidden absolute inset-0 flex items-center justify-center">
    <div class="animate-spin rounded-full h-5 w-5 border-2 border-white border-t-transparent"></div>
  </div>
</button>

対応するJavaScriptの実装例も併せてご紹介します。

javascript// ボタンローディング状態の制御
function setButtonLoading(loading) {
  const btn = document.getElementById('submitBtn');
  const text = document.getElementById('btnText');
  const loader = document.getElementById('btnLoader');
  
  if (loading) {
    btn.disabled = true;
    text.classList.add('invisible');
    loader.classList.remove('hidden');
  } else {
    btn.disabled = false;
    text.classList.remove('invisible');
    loader.classList.add('hidden');
  }
}

// 使用例
document.getElementById('submitBtn').addEventListener('click', async () => {
  setButtonLoading(true);
  
  try {
    await fetch('/api/submit', { method: 'POST' });
    // 成功処理
  } catch (error) {
    console.error('送信エラー:', error);
  } finally {
    setButtonLoading(false);
  }
});

アイコン付きのより洗練されたボタンローディングも実装可能です。

html<!-- アイコン付きローディングボタン -->
<button class="group relative px-8 py-3 bg-gradient-to-r from-purple-600 to-blue-600 
               text-white rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 
               transition-all duration-200">
  <div class="flex items-center space-x-2">
    <svg class="w-5 h-5 group-disabled:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>
    </svg>
    <div class="hidden group-disabled:block animate-spin rounded-full h-5 w-5 border-2 border-white border-t-transparent"></div>
    <span>メッセージを送信</span>
  </div>
</button>

データ取得時のローディング表示

APIからのデータ取得時に表示するローディング状態は、リスト表示やカード表示など、様々なレイアウトに対応する必要があります。

html<!-- リスト用ローディングスケルトン -->
<div class="space-y-4 p-4">
  <div class="flex items-center space-x-4">
    <div class="animate-pulse bg-gray-300 rounded-full h-12 w-12"></div>
    <div class="flex-1 space-y-2">
      <div class="animate-pulse bg-gray-300 h-4 rounded w-3/4"></div>
      <div class="animate-pulse bg-gray-300 h-3 rounded w-1/2"></div>
    </div>
  </div>
  
  <div class="flex items-center space-x-4">
    <div class="animate-pulse bg-gray-300 rounded-full h-12 w-12"></div>
    <div class="flex-1 space-y-2">
      <div class="animate-pulse bg-gray-300 h-4 rounded w-2/3"></div>
      <div class="animate-pulse bg-gray-300 h-3 rounded w-3/4"></div>
    </div>
  </div>
</div>

カード形式のコンテンツにはスケルトンローディングが効果的です。実際のコンテンツの構造を模倣することで、より自然なローディング体験を提供できます。

html<!-- カード用スケルトンローディング -->
<div class="bg-white rounded-lg shadow-md p-6 max-w-sm">
  <div class="animate-pulse space-y-4">
    <!-- 画像部分 -->
    <div class="bg-gray-300 h-48 rounded-lg"></div>
    
    <!-- タイトル部分 -->
    <div class="space-y-2">
      <div class="bg-gray-300 h-6 rounded w-3/4"></div>
      <div class="bg-gray-300 h-4 rounded w-1/2"></div>
    </div>
    
    <!-- 本文部分 -->
    <div class="space-y-2">
      <div class="bg-gray-300 h-4 rounded"></div>
      <div class="bg-gray-300 h-4 rounded"></div>
      <div class="bg-gray-300 h-4 rounded w-2/3"></div>
    </div>
    
    <!-- アクション部分 -->
    <div class="flex space-x-2">
      <div class="bg-gray-300 h-10 rounded flex-1"></div>
      <div class="bg-gray-300 h-10 rounded w-20"></div>
    </div>
  </div>
</div>

データテーブルでのローディング表示も重要な要素です。以下のような実装で、テーブル構造を保ちながらローディング状態を表現できます。

html<!-- テーブル用ローディング -->
<div class="overflow-x-auto">
  <table class="min-w-full bg-white border border-gray-200">
    <thead class="bg-gray-50">
      <tr>
        <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">名前</th>
        <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">メール</th>
        <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">部署</th>
        <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">ステータス</th>
      </tr>
    </thead>
    <tbody class="divide-y divide-gray-200">
      <!-- ローディング行 -->
      <tr class="animate-pulse">
        <td class="px-6 py-4"><div class="bg-gray-300 h-4 rounded w-24"></div></td>
        <td class="px-6 py-4"><div class="bg-gray-300 h-4 rounded w-32"></div></td>
        <td class="px-6 py-4"><div class="bg-gray-300 h-4 rounded w-20"></div></td>
        <td class="px-6 py-4"><div class="bg-gray-300 h-6 rounded-full w-16"></div></td>
      </tr>
      <tr class="animate-pulse">
        <td class="px-6 py-4"><div class="bg-gray-300 h-4 rounded w-28"></div></td>
        <td class="px-6 py-4"><div class="bg-gray-300 h-4 rounded w-36"></div></td>
        <td class="px-6 py-4"><div class="bg-gray-300 h-4 rounded w-24"></div></td>
        <td class="px-6 py-4"><div class="bg-gray-300 h-6 rounded-full w-16"></div></td>
      </tr>
    </tbody>
  </table>
</div>

まとめ

Tailwind CSS を使ったローディングインジケータの自作について、基本的な実装から実用的な活用例まで幅広くご紹介させていただきました。

外部ライブラリに依存せず、軽量で美しいローディング表示を実現することで、ユーザーエクスペリエンスの向上とパフォーマンスの最適化を同時に達成できます。特に重要なポイントとして、以下の点を念頭に置いてください。

適切な表現の選択 処理の種類や時間に応じて、円形スピナー、ドットアニメーション、プログレスバーなどを使い分けることで、より直感的な体験を提供できます。

レスポンシブ対応 様々なデバイス環境を考慮し、画面サイズに応じてローディング表示のサイズや配置を調整することが大切です。

アクセシビリティの配慮 視覚的な表現だけでなく、aria-labelrole属性の適切な設定により、すべてのユーザーが快適に利用できる仕様にしましょう。

今回ご紹介したテクニックを組み合わせることで、あなたのプロジェクトに最適なローディング表示を作成していただけるはずです。ユーザーが「待つ」時間を、少しでも快適に感じてもらえるような工夫を心がけてくださいね。

関連リンク