さとまたwiki

モダンレイアウト

ヘッダー、サイドバー、フッターなど現代的なサイト構成

モダンなWebサイトの構成

基本的なレイアウトパターン

ヘッダー + コンテンツ

ブログ、ニュースサイト
シンプルなWebサイト

サイドバー付き

管理画面、ドキュメント
このサイトのような構成

フルスクリーン

ランディングページ
アプリケーション

基本レイアウト(ヘッダー・フッター)

Sticky Header + Footer

上固定ヘッダーとフッター

html
<div class="min-h-screen flex flex-col">
  <!-- ヘッダー(上に固定) -->
  <header class="sticky top-0 z-50 bg-white border-b shadow-sm">
    <div class="max-w-7xl mx-auto px-4 py-3 flex items-center justify-between">
      <a href="/" class="text-xl font-bold">Logo</a>
      <nav class="flex gap-6">
        <a href="/" class="hover:text-blue-600">ホーム</a>
        <a href="/about" class="hover:text-blue-600">About</a>
        <a href="/contact" class="hover:text-blue-600">お問い合わせ</a>
      </nav>
    </div>
  </header>

  <!-- メインコンテンツ(残りのスペースを埋める) -->
  <main class="flex-1 max-w-7xl mx-auto px-4 py-8 w-full">
    <h1>コンテンツ</h1>
  </main>

  <!-- フッター(常に下に) -->
  <footer class="bg-gray-100 border-t">
    <div class="max-w-7xl mx-auto px-4 py-6">
      <p class="text-center text-gray-600">© 2024 My Website</p>
    </div>
  </footer>
</div>
プレビュー
Logo
ホームAboutお問い合わせ
コンテンツエリア
© 2024 My Website

コード解説:ヘッダー・フッターレイアウト

① 全体の構造

<div class="min-h-screen flex flex-col">
  • min-h-screen:画面全体の高さを確保(100vh)
  • flex flex-col:中身を縦に並べる(ヘッダー→本文→フッター)

② 固定ヘッダー

<header class="sticky top-0 z-50 bg-white border-b shadow-sm">
  • sticky top-0:スクロールしても上部に固定される
  • z-50:他の要素より前面に表示(重なり順)

③ メインコンテンツ

<main class="flex-1 max-w-7xl mx-auto px-4 py-8 w-full">
  • flex-1:残りの高さをすべて使う(フッターを下に押す)
  • max-w-7xl mx-auto:最大幅を制限して中央に配置

なぜこのパターン?

コンテンツが少なくてもflex-1のおかげでフッターは常に画面下部に配置されます。

固定サイドバー

管理画面・ドキュメントサイト向け

html
<div class="flex min-h-screen">
  <!-- サイドバー(固定幅) -->
  <aside class="w-64 bg-gray-900 text-white flex-shrink-0">
    <div class="p-4">
      <h1 class="text-xl font-bold mb-6">Admin</h1>
      <nav class="space-y-2">
        <a href="/dashboard" class="block px-4 py-2 rounded-lg bg-gray-800 hover:bg-gray-700">
          ダッシュボード
        </a>
        <a href="/users" class="block px-4 py-2 rounded-lg hover:bg-gray-700">
          ユーザー
        </a>
        <a href="/settings" class="block px-4 py-2 rounded-lg hover:bg-gray-700">
          設定
        </a>
      </nav>
    </div>
  </aside>

  <!-- メインエリア -->
  <div class="flex-1 flex flex-col">
    <!-- ヘッダー -->
    <header class="bg-white border-b px-6 py-4 flex items-center justify-between">
      <h2 class="text-lg font-semibold">ダッシュボード</h2>
      <div class="flex items-center gap-4">
        <span>田中さん</span>
        <button>ログアウト</button>
      </div>
    </header>

    <!-- コンテンツ -->
    <main class="flex-1 p-6 bg-gray-50">
      <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
        <!-- カード -->
      </div>
    </main>
  </div>
</div>
プレビュー
Admin
ダッシュボード
ユーザー
設定
ダッシュボード田中さん
カード
カード
カード

コード解説:各クラスの役割

① 全体のコンテナ

<div class="flex min-h-screen">
  • flex:子要素を横並びにする(Flexbox)
  • min-h-screen:最低でも画面の高さを確保(100vh)

② サイドバー

<aside class="w-64 bg-gray-900 text-white flex-shrink-0">
  • w-64:幅256px固定(16rem × 16px)
  • bg-gray-900 text-white:暗い背景色と白文字
  • flex-shrink-0:画面が狭くなっても縮まない

③ ナビゲーションリンク

<a class="block px-4 py-2 rounded-lg hover:bg-gray-700">
  • block:ブロック要素に(クリック範囲が広がる)
  • px-4 py-2:左右16px、上下8pxの余白
  • rounded-lg:角を丸く(8px)
  • hover:bg-gray-700:マウスを乗せた時に背景色変更

④ メインエリア

<div class="flex-1 flex flex-col">
  • flex-1:残りの幅すべてを占める
  • flex flex-col:中を縦並びに(ヘッダー→コンテンツ)

⑤ ヘッダー

<header class="bg-white border-b px-6 py-4 flex items-center justify-between">
  • border-b:下に線を引く
  • flex items-center justify-between:左右に要素を配置、縦中央揃え

⑥ コンテンツエリア

<main class="flex-1 p-6 bg-gray-50">
  • flex-1:残りの高さをすべて使う
  • bg-gray-50:薄いグレーの背景

組み立て結果

サイドバー(256px固定)が左に、残りのスペースにヘッダー+コンテンツが縦に並び、画面全体を埋めるレイアウトが完成します。

折りたたみ可能なサイドバー

モバイル対応

svelte
<script>
  let sidebarOpen = $state(false);
</script>

<div class="flex min-h-screen">
  <!-- オーバーレイ(モバイル時) -->
  {#if sidebarOpen}
    <div
      class="fixed inset-0 bg-black/50 z-40 lg:hidden"
      onclick={() => sidebarOpen = false}
    ></div>
  {/if}

  <!-- サイドバー -->
  <aside class="
    fixed lg:static inset-y-0 left-0 z-50
    w-64 bg-gray-900 text-white
    transform transition-transform duration-300
    {sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
  ">
    <!-- サイドバーの中身 -->
  </aside>

  <!-- メインエリア -->
  <div class="flex-1">
    <header class="bg-white border-b px-4 py-3">
      <!-- ハンバーガーメニュー(モバイル時のみ) -->
      <button
        class="lg:hidden p-2"
        onclick={() => sidebarOpen = true}
      >
        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
        </svg>
      </button>
    </header>
    <main class="p-6">
      <!-- コンテンツ -->
    </main>
  </div>
</div>
プレビュー
// PC: サイドバー常に表示
// モバイル: ハンバーガーメニューで開閉
// lg:hidden / lg:translate-x-0 で切り替え

レスポンシブナビゲーション

PC:横並び / モバイル:ハンバーガー

svelte
<script>
  let menuOpen = $state(false);
</script>

<header class="bg-white shadow-sm">
  <div class="max-w-7xl mx-auto px-4">
    <div class="flex items-center justify-between h-16">
      <!-- ロゴ -->
      <a href="/" class="text-xl font-bold">Logo</a>

      <!-- デスクトップナビ -->
      <nav class="hidden md:flex items-center gap-6">
        <a href="/" class="hover:text-blue-600">ホーム</a>
        <a href="/products" class="hover:text-blue-600">製品</a>
        <a href="/about" class="hover:text-blue-600">会社概要</a>
        <a href="/contact" class="px-4 py-2 bg-blue-600 text-white rounded-lg">
          お問い合わせ
        </a>
      </nav>

      <!-- モバイルメニューボタン -->
      <button
        class="md:hidden p-2"
        onclick={() => menuOpen = !menuOpen}
      >
        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          {#if menuOpen}
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
          {:else}
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
          {/if}
        </svg>
      </button>
    </div>

    <!-- モバイルナビ -->
    {#if menuOpen}
      <nav class="md:hidden py-4 border-t">
        <a href="/" class="block py-2">ホーム</a>
        <a href="/products" class="block py-2">製品</a>
        <a href="/about" class="block py-2">会社概要</a>
        <a href="/contact" class="block py-2 mt-2 text-center bg-blue-600 text-white rounded-lg">
          お問い合わせ
        </a>
      </nav>
    {/if}
  </div>
</header>
プレビュー
Logo
ホーム製品会社概要お問い合わせ

タブナビゲーション

セクション切り替え

svelte
<script>
  let activeTab = $state('overview');
</script>

<div class="border-b border-gray-200">
  <nav class="flex gap-8">
    <button
      class="py-4 px-1 border-b-2 font-medium text-sm
        {activeTab === 'overview'
          ? 'border-blue-500 text-blue-600'
          : 'border-transparent text-gray-500 hover:text-gray-700'}"
      onclick={() => activeTab = 'overview'}
    >
      概要
    </button>
    <button
      class="py-4 px-1 border-b-2 font-medium text-sm
        {activeTab === 'features'
          ? 'border-blue-500 text-blue-600'
          : 'border-transparent text-gray-500 hover:text-gray-700'}"
      onclick={() => activeTab = 'features'}
    >
      機能
    </button>
    <button
      class="py-4 px-1 border-b-2 font-medium text-sm
        {activeTab === 'pricing'
          ? 'border-blue-500 text-blue-600'
          : 'border-transparent text-gray-500 hover:text-gray-700'}"
      onclick={() => activeTab = 'pricing'}
    >
      料金
    </button>
  </nav>
</div>

<div class="py-6">
  {#if activeTab === 'overview'}
    <p>概要コンテンツ</p>
  {:else if activeTab === 'features'}
    <p>機能コンテンツ</p>
  {:else}
    <p>料金コンテンツ</p>
  {/if}
</div>
プレビュー
概要機能料金

マルチカラムフッター

リンク集+コピーライト

html
<footer class="bg-gray-900 text-gray-300">
  <div class="max-w-7xl mx-auto px-4 py-12">
    <!-- リンクセクション -->
    <div class="grid grid-cols-2 md:grid-cols-4 gap-8 mb-8">
      <!-- 会社情報 -->
      <div>
        <h3 class="text-white font-semibold mb-4">Company</h3>
        <ul class="space-y-2 text-sm">
          <li><a href="/about" class="hover:text-white">会社概要</a></li>
          <li><a href="/careers" class="hover:text-white">採用情報</a></li>
          <li><a href="/news" class="hover:text-white">ニュース</a></li>
        </ul>
      </div>

      <!-- 製品 -->
      <div>
        <h3 class="text-white font-semibold mb-4">Products</h3>
        <ul class="space-y-2 text-sm">
          <li><a href="/features" class="hover:text-white">機能</a></li>
          <li><a href="/pricing" class="hover:text-white">料金</a></li>
          <li><a href="/demo" class="hover:text-white">デモ</a></li>
        </ul>
      </div>

      <!-- サポート -->
      <div>
        <h3 class="text-white font-semibold mb-4">Support</h3>
        <ul class="space-y-2 text-sm">
          <li><a href="/docs" class="hover:text-white">ドキュメント</a></li>
          <li><a href="/faq" class="hover:text-white">FAQ</a></li>
          <li><a href="/contact" class="hover:text-white">お問い合わせ</a></li>
        </ul>
      </div>

      <!-- 法的情報 -->
      <div>
        <h3 class="text-white font-semibold mb-4">Legal</h3>
        <ul class="space-y-2 text-sm">
          <li><a href="/privacy" class="hover:text-white">プライバシー</a></li>
          <li><a href="/terms" class="hover:text-white">利用規約</a></li>
        </ul>
      </div>
    </div>

    <!-- 区切り線 -->
    <div class="border-t border-gray-800 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
      <p class="text-sm">© 2024 Company. All rights reserved.</p>
      <!-- SNSリンク -->
      <div class="flex gap-4">
        <a href="#" class="hover:text-white">Twitter</a>
        <a href="#" class="hover:text-white">GitHub</a>
        <a href="#" class="hover:text-white">Discord</a>
      </div>
    </div>
  </div>
</footer>
プレビュー
Company
会社概要
採用情報
Products
機能
料金
Support
ドキュメント
FAQ
Legal
プライバシー
利用規約
© 2024 Company
Twitter GitHub

カードレイアウト

レスポンシブカードグリッド

画面サイズで列数が変わる

html
<!-- モバイル:1列 / タブレット:2列 / PC:3列 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
  <!-- カード -->
  <div class="bg-white rounded-xl shadow-sm border overflow-hidden hover:shadow-md transition-shadow">
    <img src="/image.jpg" alt="" class="w-full h-48 object-cover" />
    <div class="p-6">
      <span class="text-xs font-medium text-blue-600 bg-blue-50 px-2 py-1 rounded">
        カテゴリ
      </span>
      <h3 class="mt-3 text-lg font-semibold">タイトル</h3>
      <p class="mt-2 text-gray-600 text-sm line-clamp-2">
        説明文がここに入ります。長い場合は2行で切り詰められます。
      </p>
      <div class="mt-4 flex items-center justify-between">
        <span class="text-sm text-gray-500">2024/01/15</span>
        <a href="#" class="text-blue-600 text-sm font-medium hover:underline">
          詳しく見る →
        </a>
      </div>
    </div>
  </div>

  <!-- 他のカード... -->
</div>
プレビュー
カテゴリ
タイトル
説明文...
カテゴリ
タイトル
説明文...
カテゴリ
タイトル
説明文...

コード解説:カードグリッドの仕組み

① グリッドコンテナ(レスポンシブ)

<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
  • grid:CSS Gridレイアウトを有効化
  • grid-cols-1:デフォルトで1列(モバイル)
  • md:grid-cols-2:768px以上で2列
  • lg:grid-cols-3:1024px以上で3列
  • gap-6:カード間の隙間(24px)

② カード本体

<div class="bg-white rounded-xl shadow-sm border overflow-hidden hover:shadow-md transition-shadow">
  • rounded-xl:大きめの角丸(12px)
  • shadow-sm:軽い影
  • overflow-hidden:角丸から画像がはみ出ないように
  • hover:shadow-md transition-shadow:ホバー時に影を大きく(アニメーション付き)

③ 画像

<img class="w-full h-48 object-cover">
  • w-full:横幅いっぱい
  • h-48:高さ192px固定
  • object-cover:縦横比を保ちつつ領域を埋める(トリミング)

④ カテゴリバッジ

<span class="text-xs font-medium text-blue-600 bg-blue-50 px-2 py-1 rounded">
  • text-xs:小さい文字
  • text-blue-600 bg-blue-50:青文字に薄い青背景

⑤ フッター部分

<div class="flex items-center justify-between">

flex items-center justify-between:日付を左、リンクを右に配置

覚えておきたいパターン

中央寄せコンテナ

max-w-7xl mx-auto px-4

最大幅を制限して中央に配置、左右に余白

画面全体を埋める

min-h-screen flex flex-col

フッターを常に画面下に配置

上に固定

sticky top-0 z-50

スクロールしても上部に固定

残りを埋める

flex-1

Flexbox内で残りのスペースを占める

PC/モバイル切り替え

hidden md:flex / md:hidden

画面サイズで表示/非表示を切り替え