aspect-ratio メディアグリッド
aspect-ratio をCSS変数で制御し、1:1/4:3/16:9などの比率を保ったままタイルを敷き詰める画像ギャラリー。比率切替も滑らかに。
ライブデモ
使用例(お題: アイドルグループ Sakura)
この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- Sakura MVギャラリー:aspect-ratio をCSS変数で制御し比率を保ったタイル -->
<section class="sk-mv" id="skMv">
<header class="sk-mv__head">
<h2 class="sk-mv__title">🌸 楽曲MV</h2>
<div class="sk-mv__ratios">
<button data-r="16/9" class="is-active" type="button">16:9</button>
<button data-r="4/3" type="button">4:3</button>
<button data-r="1/1" type="button">1:1</button>
</div>
</header>
<div class="sk-mv__grid" id="skGrid">
<figure class="sk-mv__tile">
<div class="sk-mv__thumb" style="background-image:url('https://picsum.photos/400/300?random=61')"><span class="sk-mv__play">▶</span></div>
<figcaption>桜ロード<small>4:12</small></figcaption>
</figure>
<figure class="sk-mv__tile">
<div class="sk-mv__thumb" style="background-image:url('https://picsum.photos/400/300?random=62')"><span class="sk-mv__play">▶</span></div>
<figcaption>はじまりのチャイム<small>3:48</small></figcaption>
</figure>
<figure class="sk-mv__tile">
<div class="sk-mv__thumb" style="background-image:url('https://picsum.photos/400/300?random=63')"><span class="sk-mv__play">▶</span></div>
<figcaption>キミと春風<small>4:30</small></figcaption>
</figure>
<figure class="sk-mv__tile">
<div class="sk-mv__thumb" style="background-image:url('https://picsum.photos/400/300?random=64')"><span class="sk-mv__play">▶</span></div>
<figcaption>ひらり、夢の中<small>3:55</small></figcaption>
</figure>
<figure class="sk-mv__tile">
<div class="sk-mv__thumb" style="background-image:url('https://picsum.photos/400/300?random=65')"><span class="sk-mv__play">▶</span></div>
<figcaption>ペタル・ダンス<small>4:02</small></figcaption>
</figure>
<figure class="sk-mv__tile">
<div class="sk-mv__thumb" style="background-image:url('https://picsum.photos/400/300?random=66')"><span class="sk-mv__play">▶</span></div>
<figcaption>満開エモーション<small>4:21</small></figcaption>
</figure>
</div>
</section>
CSS
/* Sakura:aspect-ratio をCSS変数(--r)で制御し、比率を保ったMVタイル */
:root {
--pink: #ffd1e0;
--pink-deep: #ff8fb3;
--gray: #fbf7f9;
}
* { box-sizing: border-box; }
body {
margin: 0;
height: 400px;
font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", system-ui, sans-serif;
background: linear-gradient(165deg, #fff5f9, var(--gray));
color: #6a2740;
overflow: hidden;
}
.sk-mv {
height: 400px;
display: flex;
flex-direction: column;
padding: 14px 16px 0;
/* タイルの比率はここで一括管理 */
--r: 16/9;
}
.sk-mv__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.sk-mv__title { margin: 0; font-size: 16px; font-weight: 800; }
.sk-mv__ratios { display: flex; gap: 6px; }
.sk-mv__ratios button {
font: inherit;
font-size: 11px;
font-weight: 700;
padding: 5px 11px;
border-radius: 999px;
border: 1px solid #ffc4d8;
background: #fff;
color: #c06088;
cursor: pointer;
transition: all 0.2s ease;
}
.sk-mv__ratios button.is-active {
background: var(--pink-deep);
border-color: var(--pink-deep);
color: #fff;
}
.sk-mv__grid {
flex: 1;
min-height: 0;
overflow: auto;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
padding-bottom: 14px;
align-content: start;
}
.sk-mv__tile {
margin: 0;
border-radius: 12px;
overflow: hidden;
background: #fff;
border: 1px solid #ffe0ea;
box-shadow: 0 6px 14px rgba(214, 94, 140, 0.14);
}
/* aspect-ratio を変数で受け取り、比率を保ったまま敷き詰める */
.sk-mv__thumb {
aspect-ratio: var(--r);
background-size: cover;
background-position: center;
position: relative;
display: grid;
place-items: center;
transition: aspect-ratio 0.35s ease;
}
.sk-mv__play {
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.82);
color: var(--pink-deep);
display: grid;
place-items: center;
font-size: 12px;
padding-left: 2px;
box-shadow: 0 4px 10px rgba(214, 94, 140, 0.3);
}
.sk-mv__tile figcaption {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: 7px 10px;
font-size: 11px;
font-weight: 700;
color: #8a4a64;
}
.sk-mv__tile figcaption small { font-size: 9.5px; color: #b88; font-weight: 500; }
@media (prefers-reduced-motion: reduce) {
.sk-mv__thumb, .sk-mv__ratios button { transition: none; }
}
JavaScript
// 比率ボタンで --r を切替。全タイルの aspect-ratio が滑らかに変わる
const root = document.getElementById("skMv");
const buttons = document.querySelectorAll(".sk-mv__ratios button");
buttons.forEach((btn) => {
btn.addEventListener("click", () => {
// アクティブ表示を付け替え
buttons.forEach((b) => b.classList.remove("is-active"));
btn.classList.add("is-active");
// CSS変数を更新(dataset の比率をそのまま渡す)
root?.style.setProperty("--r", btn.dataset.r);
});
});
コード
HTML
<!-- aspect-ratio メディアグリッド:比率を保つタイルを敷き詰めたギャラリー -->
<div class="ar">
<div class="ar__top">
<h1 class="ar__head">Aspect <span>Gallery</span></h1>
<div class="ar__ratios" id="ratioBtns" role="group" aria-label="比率切替">
<button class="ar__chip is-active" data-ratio="1 / 1">1:1</button>
<button class="ar__chip" data-ratio="4 / 3">4:3</button>
<button class="ar__chip" data-ratio="16 / 9">16:9</button>
<button class="ar__chip" data-ratio="3 / 4">3:4</button>
</div>
</div>
<div class="ar__grid" id="arGrid">
<!-- タイルはJSで生成(picsum画像) -->
</div>
</div>
CSS
/* 全体:黒地のギャラリー */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Hiragino Sans", system-ui, sans-serif;
background: radial-gradient(120% 120% at 50% -20%, #1f2937, #0b0f17 70%);
color: #f3f4f6; min-height: 100vh; padding: 16px;
}
.ar { width: min(900px, 100%); margin: 0 auto; }
.ar__top {
display: flex; align-items: center; justify-content: space-between;
flex-wrap: wrap; gap: 10px; margin-bottom: 14px;
}
.ar__head { font-size: clamp(18px, 3.4vw, 26px); font-weight: 800; letter-spacing: .03em; }
.ar__head span {
background: linear-gradient(90deg, #fda4af, #c084fc);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
/* 比率切替チップ */
.ar__ratios { display: flex; gap: 6px; flex-wrap: wrap; }
.ar__chip {
font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
color: #cbd5e1; background: #1e293b; border: 1px solid #334155;
padding: 6px 12px; border-radius: 20px; transition: all .2s;
}
.ar__chip:hover { border-color: #64748b; }
.ar__chip.is-active {
color: #0b0f17; border-color: transparent;
background: linear-gradient(90deg, #fda4af, #c084fc);
}
/* グリッド:自動敷き詰め */
.ar__grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
gap: 10px;
}
/* タイル:aspect-ratio で比率を維持(CSS変数で動的に) */
.ar__tile {
position: relative; overflow: hidden; border-radius: 12px;
aspect-ratio: var(--ratio, 1 / 1);
background: #1e293b;
border: 1px solid #2b3648;
transition: aspect-ratio .35s ease, transform .25s ease;
}
.ar__tile:hover { transform: translateY(-4px) scale(1.02); z-index: 1; }
.ar__tile img {
width: 100%; height: 100%; object-fit: cover; display: block;
}
.ar__tile span {
position: absolute; left: 8px; bottom: 8px;
font-size: 10px; font-weight: 700; letter-spacing: .1em;
background: rgba(0,0,0,.55); color: #fff; padding: 3px 8px; border-radius: 6px;
backdrop-filter: blur(2px);
}
@media (prefers-reduced-motion: reduce) {
.ar__tile, .ar__chip { transition: none; }
}
JavaScript
// 画像タイルを生成し、チップで全タイルの aspect-ratio を一括変更
(() => {
const grid = document.getElementById('arGrid');
const btnBox = document.getElementById('ratioBtns');
if (!grid || !btnBox) return;
// picsum のランダム画像でタイルを生成(seed で安定化)
const COUNT = 8;
const frag = document.createDocumentFragment();
for (let i = 0; i < COUNT; i++) {
const tile = document.createElement('div');
tile.className = 'ar__tile';
const img = document.createElement('img');
img.loading = 'lazy';
img.alt = '';
img.src = `https://picsum.photos/seed/ar${i + 10}/400/400`;
const tag = document.createElement('span');
tag.textContent = `0${i + 1}`;
tile.append(img, tag);
frag.appendChild(tile);
}
grid.appendChild(frag);
// チップで比率を切り替え
const tiles = grid.querySelectorAll('.ar__tile');
const setRatio = (ratio) => {
tiles.forEach((t) => t.style.setProperty('--ratio', ratio));
};
setRatio('1 / 1'); // 初期比率
btnBox.addEventListener('click', (e) => {
const btn = e.target.closest('.ar__chip');
if (!btn) return;
btnBox.querySelectorAll('.ar__chip').forEach((b) => b.classList.remove('is-active'));
btn.classList.add('is-active');
setRatio(btn.dataset.ratio);
});
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「aspect-ratio メディアグリッド」の効果を追加してください。
# 追加してほしい効果
aspect-ratio メディアグリッド(レイアウト & グリッド)
aspect-ratio をCSS変数で制御し、1:1/4:3/16:9などの比率を保ったままタイルを敷き詰める画像ギャラリー。比率切替も滑らかに。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- aspect-ratio メディアグリッド:比率を保つタイルを敷き詰めたギャラリー -->
<div class="ar">
<div class="ar__top">
<h1 class="ar__head">Aspect <span>Gallery</span></h1>
<div class="ar__ratios" id="ratioBtns" role="group" aria-label="比率切替">
<button class="ar__chip is-active" data-ratio="1 / 1">1:1</button>
<button class="ar__chip" data-ratio="4 / 3">4:3</button>
<button class="ar__chip" data-ratio="16 / 9">16:9</button>
<button class="ar__chip" data-ratio="3 / 4">3:4</button>
</div>
</div>
<div class="ar__grid" id="arGrid">
<!-- タイルはJSで生成(picsum画像) -->
</div>
</div>
【CSS】
/* 全体:黒地のギャラリー */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Hiragino Sans", system-ui, sans-serif;
background: radial-gradient(120% 120% at 50% -20%, #1f2937, #0b0f17 70%);
color: #f3f4f6; min-height: 100vh; padding: 16px;
}
.ar { width: min(900px, 100%); margin: 0 auto; }
.ar__top {
display: flex; align-items: center; justify-content: space-between;
flex-wrap: wrap; gap: 10px; margin-bottom: 14px;
}
.ar__head { font-size: clamp(18px, 3.4vw, 26px); font-weight: 800; letter-spacing: .03em; }
.ar__head span {
background: linear-gradient(90deg, #fda4af, #c084fc);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
/* 比率切替チップ */
.ar__ratios { display: flex; gap: 6px; flex-wrap: wrap; }
.ar__chip {
font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
color: #cbd5e1; background: #1e293b; border: 1px solid #334155;
padding: 6px 12px; border-radius: 20px; transition: all .2s;
}
.ar__chip:hover { border-color: #64748b; }
.ar__chip.is-active {
color: #0b0f17; border-color: transparent;
background: linear-gradient(90deg, #fda4af, #c084fc);
}
/* グリッド:自動敷き詰め */
.ar__grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
gap: 10px;
}
/* タイル:aspect-ratio で比率を維持(CSS変数で動的に) */
.ar__tile {
position: relative; overflow: hidden; border-radius: 12px;
aspect-ratio: var(--ratio, 1 / 1);
background: #1e293b;
border: 1px solid #2b3648;
transition: aspect-ratio .35s ease, transform .25s ease;
}
.ar__tile:hover { transform: translateY(-4px) scale(1.02); z-index: 1; }
.ar__tile img {
width: 100%; height: 100%; object-fit: cover; display: block;
}
.ar__tile span {
position: absolute; left: 8px; bottom: 8px;
font-size: 10px; font-weight: 700; letter-spacing: .1em;
background: rgba(0,0,0,.55); color: #fff; padding: 3px 8px; border-radius: 6px;
backdrop-filter: blur(2px);
}
@media (prefers-reduced-motion: reduce) {
.ar__tile, .ar__chip { transition: none; }
}
【JavaScript】
// 画像タイルを生成し、チップで全タイルの aspect-ratio を一括変更
(() => {
const grid = document.getElementById('arGrid');
const btnBox = document.getElementById('ratioBtns');
if (!grid || !btnBox) return;
// picsum のランダム画像でタイルを生成(seed で安定化)
const COUNT = 8;
const frag = document.createDocumentFragment();
for (let i = 0; i < COUNT; i++) {
const tile = document.createElement('div');
tile.className = 'ar__tile';
const img = document.createElement('img');
img.loading = 'lazy';
img.alt = '';
img.src = `https://picsum.photos/seed/ar${i + 10}/400/400`;
const tag = document.createElement('span');
tag.textContent = `0${i + 1}`;
tile.append(img, tag);
frag.appendChild(tile);
}
grid.appendChild(frag);
// チップで比率を切り替え
const tiles = grid.querySelectorAll('.ar__tile');
const setRatio = (ratio) => {
tiles.forEach((t) => t.style.setProperty('--ratio', ratio));
};
setRatio('1 / 1'); // 初期比率
btnBox.addEventListener('click', (e) => {
const btn = e.target.closest('.ar__chip');
if (!btn) return;
btnBox.querySelectorAll('.ar__chip').forEach((b) => b.classList.remove('is-active'));
btn.classList.add('is-active');
setRatio(btn.dataset.ratio);
});
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。