第6回:CSSレイアウト応用
🎯 学習目標
- CSS Gridの基本概念を理解する
- 複雑なレイアウトを実現できる
- メディアクエリを詳細に理解する
- モバイルファーストの設計思想を身につける
📚 導入(5分)
FlexboxとGridの使い分け
| 特徴 | Flexbox | CSS Grid |
|---|---|---|
| 次元 | 1次元(行または列) | 2次元(行と列) |
| 用途 | コンポーネント内レイアウト | ページ全体レイアウト |
| 適用例 | ナビゲーション、カード内部 | グリッドシステム、複雑なレイアウト |
| サポート | 非常に良い | 良い(IE11以外) |
💡 理論学習(30分)
CSS Gridの基礎(20分)
display: gridの効果
.grid-container {
display: grid;
}適用すると:
- グリッドコンテナ(親要素)になる
- 子要素がグリッドアイテムになる
- グリッドラインとグリッドセルが定義される
基本的なグリッド構造
.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">© 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の使い分け確認
✅ 使い分けガイド
| 用途 | 推奨技術 | 理由 |
|---|---|---|
| ページ全体のレイアウト | Grid | 2次元での配置が可能 |
| コンポーネント内レイアウト | Flexbox | 1次元での配置がシンプル |
| カードの内部構成 | Flexbox | 柔軟な要素配置 |
| 複雑なギャラリー | Grid | 不規則なサイズ対応 |
| ナビゲーション | Flexbox | 水平/垂直配置が簡単 |
次回予告:CSSアニメーション
次回学習する内容:
- transformプロパティの詳細
- transitionとanimationの違い
- keyframesアニメーション
- パフォーマンスを考慮したアニメーション
🏠 宿題
-
レスポンシブカードグリッドの作成
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 2rem; } -
CSS変数を使った色管理
- テーマカラーをCSS変数で定義
- ダークモードの実装
-
アクセシビリティの考慮
prefers-reduced-motionへの対応- 適切なalt属性の設定
📚 参考リソース
- CSS Tricks - A Complete Guide to Grid
- Grid Garden (ゲーム形式で学習)
- MDN - CSS Grid Layout
- Can I Use - CSS Grid
次回もお楽しみに! 🎨
Last updated on