Skip to Content
Lecture第6回:CSSレイアウト応用

第6回:CSSレイアウト応用

🎯 学習目標

  • CSS Gridの基本概念を理解する
  • 複雑なレイアウトを実現できる
  • メディアクエリを詳細に理解する
  • モバイルファーストの設計思想を身につける

📚 導入(5分)

FlexboxとGridの使い分け

特徴FlexboxCSS Grid
次元1次元(行または列)2次元(行と列)
用途コンポーネント内レイアウトページ全体レイアウト
適用例ナビゲーション、カード内部グリッドシステム、複雑なレイアウト
サポート非常に良い良い(IE11以外)

💡 理論学習(30分)

CSS Gridの基礎(20分)

display: gridの効果

.grid-container { display: grid; }

適用すると:

  1. グリッドコンテナ(親要素)になる
  2. 子要素がグリッドアイテムになる
  3. グリッドライングリッドセルが定義される

基本的なグリッド構造

.grid-container { display: grid; /* 列の定義 */ grid-template-columns: 200px 1fr 100px; /* 行の定義 */ grid-template-rows: 100px 1fr 50px; /* グリッド間の余白 */ gap: 20px; }
┌────────┬─────────────────┬────────┐ │ 200px │ 1fr │ 100px │ 100px ├────────┼─────────────────┼────────┤ │ │ │ │ │ │ 1fr │ │ 1fr │ │ │ │ ├────────┼─────────────────┼────────┤ │ │ │ │ 50px └────────┴─────────────────┴────────┘

単位の種類

.grid { display: grid; /* 固定サイズ */ grid-template-columns: 200px 300px; /* 比率指定 */ grid-template-columns: 1fr 2fr 1fr; /* 1:2:1の比率 */ /* 最小・最大サイズ */ grid-template-columns: minmax(200px, 1fr) 300px; /* 繰り返し */ grid-template-columns: repeat(3, 1fr); /* 3つの等幅列 */ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); /* レスポンシブグリッド */ /* 混合 */ grid-template-columns: 200px repeat(2, 1fr) 100px; }

グリッドエリアの命名

.grid-container { display: grid; grid-template-areas: "header header header" "sidebar main main" "footer footer footer"; grid-template-columns: 200px 1fr 1fr; grid-template-rows: 100px 1fr 80px; } .header { grid-area: header; } .sidebar { grid-area: sidebar; } .main { grid-area: main; } .footer { grid-area: footer; }

グリッドアイテムの配置

/* ライン番号で指定 */ .item1 { grid-column: 1 / 3; /* 1列目から3列目まで */ grid-row: 1 / 2; /* 1行目 */ } /* spanを使用 */ .item2 { grid-column: span 2; /* 2列分 */ grid-row: span 3; /* 3行分 */ } /* 名前付きエリアで指定 */ .item3 { grid-area: main; }

メディアクエリの詳細(10分)

ブレークポイントの考え方

/* デバイス別アプローチ(非推奨) */ @media (max-width: 320px) { /* iPhone 5 */ } @media (max-width: 375px) { /* iPhone 6/7/8 */ } @media (max-width: 768px) { /* iPad */ } /* コンテンツ主導アプローチ(推奨) */ @media (max-width: 480px) { /* 小さなスマホ */ } @media (max-width: 768px) { /* タブレット・大きなスマホ */ } @media (max-width: 1024px) { /* 小さなデスクトップ・タブレット */ } @media (min-width: 1200px) { /* 大きなデスクトップ */ }

メディア特性の種類

/* 画面サイズ */ @media (min-width: 768px) { } @media (max-width: 1023px) { } @media (width: 768px) { } /* 画面の向き */ @media (orientation: portrait) { /* 縦向き */ } @media (orientation: landscape) { /* 横向き */ } /* 解像度 */ @media (min-resolution: 2dppx) { /* Retina対応 */ } @media (-webkit-min-device-pixel-ratio: 2) { } /* カラー対応 */ @media (prefers-color-scheme: dark) { /* ダークモード */ } @media (prefers-reduced-motion: reduce) { /* アニメーション無効 */ } /* 入力方式 */ @media (pointer: coarse) { /* タッチデバイス */ } @media (pointer: fine) { /* マウス */ } @media (hover: hover) { /* ホバー対応 */ }

モバイルファーストの考え方

/* ✅ モバイルファースト(推奨) */ .container { /* モバイル(デフォルト) */ padding: 10px; font-size: 14px; } @media (min-width: 768px) { .container { /* タブレット以上 */ padding: 20px; font-size: 16px; } } @media (min-width: 1200px) { .container { /* デスクトップ */ padding: 40px; font-size: 18px; max-width: 1200px; margin: 0 auto; } } /* ❌ デスクトップファースト(非推奨) */ .container { /* デスクトップ(デフォルト) */ padding: 40px; font-size: 18px; max-width: 1200px; } @media (max-width: 1199px) { .container { /* タブレット以下 */ padding: 20px; font-size: 16px; max-width: none; } } @media (max-width: 767px) { .container { /* モバイル */ padding: 10px; font-size: 14px; } }

🛠️ 実習(50分)

レスポンシブギャラリーサイト作成(45分)

HTML構造

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>フォトギャラリー</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="page-wrapper"> <header class="header"> <div class="header-content"> <h1 class="site-title">Photo Gallery</h1> <nav class="nav"> <ul class="nav-list"> <li><a href="#" class="nav-link active">すべて</a></li> <li><a href="#" class="nav-link">自然</a></li> <li><a href="#" class="nav-link">建物</a></li> <li><a href="#" class="nav-link">人物</a></li> </ul> </nav> </div> </header> <main class="main"> <section class="hero"> <div class="hero-content"> <h2 class="hero-title">美しい瞬間を切り取る</h2> <p class="hero-text">世界各地で撮影した写真をご覧ください</p> </div> </section> <section class="gallery"> <div class="gallery-grid"> <article class="gallery-item"> <img src="https://picsum.photos/400/300?random=1" alt="自然の風景" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">山の朝日</h3> <p class="gallery-category">自然</p> </div> </article> <article class="gallery-item gallery-item--large"> <img src="https://picsum.photos/400/600?random=2" alt="建築物" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">現代建築</h3> <p class="gallery-category">建物</p> </div> </article> <article class="gallery-item"> <img src="https://picsum.photos/400/300?random=3" alt="街の風景" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">夜の街角</h3> <p class="gallery-category">街</p> </div> </article> <article class="gallery-item"> <img src="https://picsum.photos/400/300?random=4" alt="ポートレート" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">笑顔</h3> <p class="gallery-category">人物</p> </div> </article> <article class="gallery-item gallery-item--wide"> <img src="https://picsum.photos/800/300?random=5" alt="パノラマ" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">海岸線</h3> <p class="gallery-category">自然</p> </div> </article> <article class="gallery-item"> <img src="https://picsum.photos/400/300?random=6" alt="動物" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">野生動物</h3> <p class="gallery-category">動物</p> </div> </article> <article class="gallery-item"> <img src="https://picsum.photos/400/300?random=7" alt="花" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">春の花</h3> <p class="gallery-category">自然</p> </div> </article> <article class="gallery-item"> <img src="https://picsum.photos/400/300?random=8" alt="料理" class="gallery-image"> <div class="gallery-overlay"> <h3 class="gallery-title">美味しい料理</h3> <p class="gallery-category">料理</p> </div> </article> </div> </section> </main> <footer class="footer"> <div class="footer-content"> <p class="copyright">&copy; 2024 Photo Gallery. All rights reserved.</p> <div class="social-links"> <a href="#" class="social-link">Instagram</a> <a href="#" class="social-link">Twitter</a> <a href="#" class="social-link">Facebook</a> </div> </div> </footer> </div> </body> </html>

CSS(css/style.css)

/* ========================================== CSS変数(カスタムプロパティ) ========================================== */ :root { --primary-color: #2c3e50; --secondary-color: #3498db; --accent-color: #e74c3c; --text-color: #333; --text-light: #666; --background-color: #f8f9fa; --white: #ffffff; --shadow: 0 2px 10px rgba(0, 0, 0, 0.1); --shadow-hover: 0 5px 20px rgba(0, 0, 0, 0.2); --border-radius: 8px; --transition: all 0.3s ease; } /* ========================================== リセット ========================================== */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Helvetica Neue', Arial, 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif; font-size: 16px; line-height: 1.6; color: var(--text-color); background-color: var(--background-color); } img { max-width: 100%; height: auto; } a { color: var(--secondary-color); text-decoration: none; transition: var(--transition); } ul { list-style: none; } /* ========================================== レイアウト ========================================== */ .page-wrapper { display: grid; grid-template-areas: "header" "main" "footer"; grid-template-rows: auto 1fr auto; min-height: 100vh; } /* ========================================== ヘッダー ========================================== */ .header { grid-area: header; background-color: var(--white); box-shadow: var(--shadow); position: sticky; top: 0; z-index: 100; } .header-content { max-width: 1200px; margin: 0 auto; padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center; } .site-title { font-size: 1.5rem; font-weight: bold; color: var(--primary-color); } .nav-list { display: flex; gap: 1.5rem; } .nav-link { color: var(--text-light); font-weight: 500; padding: 0.5rem 1rem; border-radius: var(--border-radius); transition: var(--transition); } .nav-link:hover, .nav-link.active { color: var(--secondary-color); background-color: rgba(52, 152, 219, 0.1); } /* ========================================== メイン ========================================== */ .main { grid-area: main; } /* ヒーローセクション */ .hero { background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); color: var(--white); padding: 4rem 2rem; text-align: center; } .hero-content { max-width: 600px; margin: 0 auto; } .hero-title { font-size: 2.5rem; margin-bottom: 1rem; font-weight: bold; } .hero-text { font-size: 1.2rem; opacity: 0.9; } /* ========================================== ギャラリー(CSS Grid) ========================================== */ .gallery { padding: 4rem 2rem; max-width: 1200px; margin: 0 auto; } .gallery-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; grid-auto-rows: 300px; } .gallery-item { position: relative; border-radius: var(--border-radius); overflow: hidden; box-shadow: var(--shadow); transition: var(--transition); cursor: pointer; } .gallery-item:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); } /* 大きなアイテム */ .gallery-item--large { grid-row: span 2; } /* 横長アイテム */ .gallery-item--wide { grid-column: span 2; } .gallery-image { width: 100%; height: 100%; object-fit: cover; transition: var(--transition); } .gallery-item:hover .gallery-image { transform: scale(1.05); } .gallery-overlay { position: absolute; bottom: 0; left: 0; right: 0; background: linear-gradient(transparent, rgba(0, 0, 0, 0.8)); color: var(--white); padding: 2rem 1.5rem 1.5rem; transform: translateY(100%); transition: var(--transition); } .gallery-item:hover .gallery-overlay { transform: translateY(0); } .gallery-title { font-size: 1.2rem; margin-bottom: 0.5rem; font-weight: bold; } .gallery-category { font-size: 0.9rem; opacity: 0.8; text-transform: uppercase; letter-spacing: 0.5px; } /* ========================================== フッター ========================================== */ .footer { grid-area: footer; background-color: var(--primary-color); color: var(--white); padding: 2rem; } .footer-content { max-width: 1200px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; } .social-links { display: flex; gap: 1rem; } .social-link { color: var(--white); opacity: 0.8; transition: var(--transition); } .social-link:hover { opacity: 1; color: var(--secondary-color); } /* ========================================== レスポンシブデザイン(モバイルファースト) ========================================== */ /* 小さなスマホ */ @media (max-width: 480px) { .header-content { flex-direction: column; gap: 1rem; padding: 1rem; } .nav-list { gap: 0.5rem; } .nav-link { padding: 0.5rem; font-size: 0.9rem; } .hero { padding: 3rem 1rem; } .hero-title { font-size: 2rem; } .hero-text { font-size: 1rem; } .gallery { padding: 2rem 1rem; } .gallery-grid { grid-template-columns: 1fr; gap: 1rem; grid-auto-rows: 250px; } .gallery-item--large, .gallery-item--wide { grid-column: span 1; grid-row: span 1; } .footer-content { flex-direction: column; gap: 1rem; text-align: center; } } /* タブレット */ @media (min-width: 481px) and (max-width: 768px) { .gallery-grid { grid-template-columns: repeat(2, 1fr); } .gallery-item--wide { grid-column: span 2; } } /* 中型タブレット */ @media (min-width: 769px) and (max-width: 1024px) { .gallery-grid { grid-template-columns: repeat(3, 1fr); } } /* 大型デスクトップ */ @media (min-width: 1400px) { .gallery-grid { grid-template-columns: repeat(4, 1fr); } .hero-title { font-size: 3rem; } .hero-text { font-size: 1.4rem; } } /* 横向きスマホ対応 */ @media (max-height: 500px) and (orientation: landscape) { .hero { padding: 2rem; } .hero-title { font-size: 2rem; } } /* ダークモード対応 */ @media (prefers-color-scheme: dark) { :root { --text-color: #e0e0e0; --text-light: #b0b0b0; --background-color: #1a1a1a; --white: #2d2d2d; --shadow: 0 2px 10px rgba(255, 255, 255, 0.1); --shadow-hover: 0 5px 20px rgba(255, 255, 255, 0.2); } } /* アニメーション無効化対応 */ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* 印刷対応 */ @media print { .nav, .footer { display: none; } .hero { background: none; color: black; } .gallery-overlay { position: static; background: none; color: black; transform: none; } }

📝 まとめ・質疑応答(5分)

GridとFlexboxの使い分け確認

✅ 使い分けガイド

用途推奨技術理由
ページ全体のレイアウトGrid2次元での配置が可能
コンポーネント内レイアウトFlexbox1次元での配置がシンプル
カードの内部構成Flexbox柔軟な要素配置
複雑なギャラリーGrid不規則なサイズ対応
ナビゲーションFlexbox水平/垂直配置が簡単

次回予告:CSSアニメーション

次回学習する内容:

  • transformプロパティの詳細
  • transitionとanimationの違い
  • keyframesアニメーション
  • パフォーマンスを考慮したアニメーション

🏠 宿題

  1. レスポンシブカードグリッドの作成

    .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 2rem; }
  2. CSS変数を使った色管理

    • テーマカラーをCSS変数で定義
    • ダークモードの実装
  3. アクセシビリティの考慮

    • prefers-reduced-motion への対応
    • 適切なalt属性の設定

📚 参考リソース


次回もお楽しみに! 🎨

Last updated on