Subgrid カード整列
grid-template-rows: subgrid で各カードが親グリッドの行ラインを継承し、本文の長さが違っても見出し・価格・ボタンの高さがピタリと揃う料金表。
ライブデモ
使用例(お題: SaaS FlowDesk)
この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- FlowDesk 料金表:subgrid で各カードの行(見出し/価格/機能/ボタン)を整列 -->
<section class="fd-pricing">
<h2 class="fd-pricing__head">シンプルな <span>料金プラン</span></h2>
<div class="fd-pricing__grid">
<article class="fd-plan">
<span class="fd-plan__badge">STARTER</span>
<h3 class="fd-plan__name">スターター</h3>
<p class="fd-plan__desc">個人や小さなチームで、まず試したい方に。</p>
<p class="fd-plan__price"><b>¥0</b><small>/月</small></p>
<ul class="fd-plan__feats">
<li>✓ メンバー 3名まで</li>
<li>✓ プロジェクト 2件</li>
<li>✓ 基本レポート</li>
</ul>
<button class="fd-plan__btn" type="button">無料で始める</button>
</article>
<article class="fd-plan fd-plan--hot">
<span class="fd-plan__badge">人気</span>
<h3 class="fd-plan__name">プロ</h3>
<p class="fd-plan__desc">成長中のチームに最適な、機能をひと通り揃えた標準プラン。</p>
<p class="fd-plan__price"><b>¥1,480</b><small>/月</small></p>
<ul class="fd-plan__feats">
<li>✓ メンバー 25名</li>
<li>✓ プロジェクト 無制限</li>
<li>✓ 高度な分析ダッシュボード</li>
<li>✓ 優先サポート</li>
</ul>
<button class="fd-plan__btn" type="button">14日間お試し</button>
</article>
<article class="fd-plan">
<span class="fd-plan__badge">SCALE</span>
<h3 class="fd-plan__name">エンタープライズ</h3>
<p class="fd-plan__desc">大規模組織向け。</p>
<p class="fd-plan__price"><b>応相談</b></p>
<ul class="fd-plan__feats">
<li>✓ SSO / 監査ログ</li>
<li>✓ 専任担当者</li>
<li>✓ SLA保証</li>
</ul>
<button class="fd-plan__btn" type="button">問い合わせる</button>
</article>
</div>
</section>
CSS
/* FlowDesk:grid-template-rows: subgrid で料金カードの各行をピタリ整列 */
:root {
--navy: #0f1b34;
--blue: #4f7cff;
--line: #e4e9f5;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", system-ui, sans-serif;
background: linear-gradient(160deg, #0f1b34, #16264a 60%, #0f1b34);
color: #1b2742;
height: 400px;
display: grid;
place-items: center;
padding: 16px;
}
.fd-pricing { width: 100%; max-width: 760px; }
.fd-pricing__head {
text-align: center;
font-size: 20px;
font-weight: 800;
margin-bottom: 14px;
color: #fff;
}
.fd-pricing__head span { color: #8fb0ff; }
/* 親グリッド:3カラム+カード内部の5行を定義 */
.fd-pricing__grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto auto 1fr auto; /* name / desc / price / feats / btn */
gap: 12px;
}
/* カード:親の5行にまたがり、subgrid で行ラインを継承 */
.fd-plan {
grid-row: span 5;
display: grid;
grid-template-rows: subgrid;
gap: 8px;
background: #fff;
border: 1px solid var(--line);
border-radius: 14px;
padding: 16px;
box-shadow: 0 14px 30px -20px rgba(15, 27, 52, 0.6);
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.fd-plan:hover { transform: translateY(-4px); box-shadow: 0 22px 38px -20px rgba(15, 27, 52, 0.55); }
.fd-plan--hot {
border-color: var(--blue);
box-shadow: 0 18px 38px -16px rgba(79, 124, 255, 0.5);
}
.fd-plan__badge {
justify-self: start;
font-size: 9px;
letter-spacing: 0.18em;
font-weight: 800;
color: var(--blue);
background: #eef3ff;
padding: 4px 10px;
border-radius: 999px;
}
.fd-plan--hot .fd-plan__badge { color: #fff; background: var(--blue); }
.fd-plan__name { font-size: 16px; font-weight: 800; color: var(--navy); }
.fd-plan__desc { font-size: 11.5px; line-height: 1.6; color: #5a6a8c; }
.fd-plan__price { display: flex; align-items: baseline; gap: 4px; }
.fd-plan__price b { font-size: 26px; font-weight: 800; color: var(--navy); }
.fd-plan__price small { font-size: 11px; color: #7e8cae; }
.fd-plan__feats { list-style: none; font-size: 11.5px; line-height: 1.9; color: #46557a; }
.fd-plan__btn {
font: inherit;
font-weight: 700;
font-size: 13px;
cursor: pointer;
border: 1px solid var(--blue);
border-radius: 9px;
padding: 9px;
color: var(--blue);
background: #fff;
transition: background 0.2s, transform 0.1s;
}
.fd-plan__btn:hover { background: #eef3ff; }
.fd-plan--hot .fd-plan__btn { color: #fff; background: var(--blue); border-color: var(--blue); }
.fd-plan--hot .fd-plan__btn:hover { background: #3f6cf0; }
.fd-plan__btn:active { transform: scale(0.97); }
@media (prefers-reduced-motion: reduce) {
.fd-plan, .fd-plan__btn { transition: none; }
}
JavaScript
// プラン選択ボタン:押されたら一時的に確認表示(subgrid のボタン行が揃うのを確認できる)
const buttons = document.querySelectorAll(".fd-plan__btn");
buttons.forEach((btn) => {
const original = btn.textContent;
btn.addEventListener("click", () => {
btn.textContent = "✓ 選択中";
setTimeout(() => { btn.textContent = original; }, 1300);
});
});
コード
HTML
<!-- Subgrid カード整列:各カードが親グリッドの行ラインを共有し見出し/本文/価格を揃える -->
<div class="sg">
<h1 class="sg__head">Subgrid <span>Pricing</span></h1>
<div class="sg__grid">
<article class="card">
<span class="card__badge">STARTER</span>
<h2 class="card__title">はじめの一歩</h2>
<p class="card__desc">小さく始めたい個人向け。基本機能をすべて。</p>
<div class="card__price"><b>¥0</b><small>/月</small></div>
<button class="card__btn">選ぶ</button>
</article>
<article class="card card--hot">
<span class="card__badge">PRO</span>
<h2 class="card__title">伸びるチーム向けプラン</h2>
<p class="card__desc">行をまたいでも見出し・価格の高さが揃う。</p>
<div class="card__price"><b>¥1,200</b><small>/月</small></div>
<button class="card__btn">選ぶ</button>
</article>
<article class="card">
<span class="card__badge">SCALE</span>
<h2 class="card__title">大規模</h2>
<p class="card__desc">高度な権限管理と無制限のシート。説明文の長さが違ってもズレない。</p>
<div class="card__price"><b>¥4,800</b><small>/月</small></div>
<button class="card__btn">選ぶ</button>
</article>
</div>
</div>
CSS
/* 全体:明るいミント地の料金表 */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Hiragino Sans", system-ui, sans-serif;
background: linear-gradient(160deg, #ecfeff, #d9f1ee 60%, #cfeae0);
color: #103530; min-height: 100vh; padding: 18px 16px;
display: grid; place-items: center;
}
/* ラッパーに明示幅を与える(中央寄せのgridアイテムは縮むため、
中の % が0に潰れてカードが消えるのを防ぐ) */
.sg { width: 100%; max-width: 880px; margin-inline: auto; }
.sg__head {
text-align: center; font-size: clamp(18px, 3.4vw, 26px);
font-weight: 800; margin-bottom: 16px; color: #0f3d36;
}
.sg__head span {
background: linear-gradient(90deg, #14b8a6, #0ea5e9);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
/* 親グリッド:3カラム+カード内部で使う5行を定義 */
.sg__grid {
width: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto 1fr auto auto; /* badge/title/desc/price/btn */
gap: 14px;
}
@media (max-width: 640px) {
.sg__grid { grid-template-columns: 1fr; grid-template-rows: none; }
}
/* カード:親の5行にまたがり、subgrid で行ラインを継承 */
.card {
grid-row: span 5;
display: grid;
grid-template-rows: subgrid;
gap: 10px;
background: #ffffff;
border: 1px solid #c7e7e0;
border-radius: 16px;
padding: 18px;
box-shadow: 0 14px 30px -20px rgba(13,80,70,.5);
transition: transform .25s ease, box-shadow .25s ease;
}
@media (max-width: 640px) { .card { grid-row: auto; grid-template-rows: none; } }
.card:hover { transform: translateY(-5px); box-shadow: 0 22px 40px -22px rgba(13,80,70,.55); }
.card--hot {
border-color: #14b8a6;
background: linear-gradient(180deg, #ffffff, #f0fdfa);
box-shadow: 0 18px 38px -18px rgba(20,184,166,.55);
}
.card__badge {
justify-self: start; font-size: 10px; letter-spacing: .2em; font-weight: 800;
color: #0d9488; background: #ccfbf1; padding: 5px 11px; border-radius: 20px;
}
.card--hot .card__badge { color: #fff; background: #14b8a6; }
.card__title { font-size: 17px; font-weight: 800; line-height: 1.3; color: #0f3d36; }
.card__desc { font-size: 13px; line-height: 1.65; color: #4b6b65; }
.card__price { display: flex; align-items: baseline; gap: 4px; }
.card__price b { font-size: 28px; font-weight: 800; color: #0f3d36; }
.card__price small { font-size: 12px; color: #6b8c85; }
.card__btn {
font: inherit; font-weight: 700; font-size: 14px; cursor: pointer;
border: none; border-radius: 10px; padding: 11px;
color: #062e29; background: #5eead4; transition: background .2s, transform .1s;
}
.card--hot .card__btn { color: #fff; background: linear-gradient(90deg, #14b8a6, #0ea5e9); }
.card__btn:hover { background: #2dd4bf; }
.card--hot .card__btn:hover { filter: brightness(1.05); }
.card__btn:active { transform: scale(.97); }
@media (prefers-reduced-motion: reduce) {
.card, .card__btn { transition: none; }
}
JavaScript
// 選択中のプランをハイライト(クリックで切り替え・null安全)
(() => {
const cards = document.querySelectorAll('.card');
if (!cards.length) return;
cards.forEach((card) => {
const btn = card.querySelector('.card__btn');
if (!btn) return;
btn.addEventListener('click', () => {
cards.forEach((c) => c.classList.remove('card--hot'));
card.classList.add('card--hot');
});
});
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「Subgrid カード整列」の効果を追加してください。
# 追加してほしい効果
Subgrid カード整列(レイアウト & グリッド)
grid-template-rows: subgrid で各カードが親グリッドの行ラインを継承し、本文の長さが違っても見出し・価格・ボタンの高さがピタリと揃う料金表。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- Subgrid カード整列:各カードが親グリッドの行ラインを共有し見出し/本文/価格を揃える -->
<div class="sg">
<h1 class="sg__head">Subgrid <span>Pricing</span></h1>
<div class="sg__grid">
<article class="card">
<span class="card__badge">STARTER</span>
<h2 class="card__title">はじめの一歩</h2>
<p class="card__desc">小さく始めたい個人向け。基本機能をすべて。</p>
<div class="card__price"><b>¥0</b><small>/月</small></div>
<button class="card__btn">選ぶ</button>
</article>
<article class="card card--hot">
<span class="card__badge">PRO</span>
<h2 class="card__title">伸びるチーム向けプラン</h2>
<p class="card__desc">行をまたいでも見出し・価格の高さが揃う。</p>
<div class="card__price"><b>¥1,200</b><small>/月</small></div>
<button class="card__btn">選ぶ</button>
</article>
<article class="card">
<span class="card__badge">SCALE</span>
<h2 class="card__title">大規模</h2>
<p class="card__desc">高度な権限管理と無制限のシート。説明文の長さが違ってもズレない。</p>
<div class="card__price"><b>¥4,800</b><small>/月</small></div>
<button class="card__btn">選ぶ</button>
</article>
</div>
</div>
【CSS】
/* 全体:明るいミント地の料金表 */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Hiragino Sans", system-ui, sans-serif;
background: linear-gradient(160deg, #ecfeff, #d9f1ee 60%, #cfeae0);
color: #103530; min-height: 100vh; padding: 18px 16px;
display: grid; place-items: center;
}
/* ラッパーに明示幅を与える(中央寄せのgridアイテムは縮むため、
中の % が0に潰れてカードが消えるのを防ぐ) */
.sg { width: 100%; max-width: 880px; margin-inline: auto; }
.sg__head {
text-align: center; font-size: clamp(18px, 3.4vw, 26px);
font-weight: 800; margin-bottom: 16px; color: #0f3d36;
}
.sg__head span {
background: linear-gradient(90deg, #14b8a6, #0ea5e9);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
/* 親グリッド:3カラム+カード内部で使う5行を定義 */
.sg__grid {
width: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto 1fr auto auto; /* badge/title/desc/price/btn */
gap: 14px;
}
@media (max-width: 640px) {
.sg__grid { grid-template-columns: 1fr; grid-template-rows: none; }
}
/* カード:親の5行にまたがり、subgrid で行ラインを継承 */
.card {
grid-row: span 5;
display: grid;
grid-template-rows: subgrid;
gap: 10px;
background: #ffffff;
border: 1px solid #c7e7e0;
border-radius: 16px;
padding: 18px;
box-shadow: 0 14px 30px -20px rgba(13,80,70,.5);
transition: transform .25s ease, box-shadow .25s ease;
}
@media (max-width: 640px) { .card { grid-row: auto; grid-template-rows: none; } }
.card:hover { transform: translateY(-5px); box-shadow: 0 22px 40px -22px rgba(13,80,70,.55); }
.card--hot {
border-color: #14b8a6;
background: linear-gradient(180deg, #ffffff, #f0fdfa);
box-shadow: 0 18px 38px -18px rgba(20,184,166,.55);
}
.card__badge {
justify-self: start; font-size: 10px; letter-spacing: .2em; font-weight: 800;
color: #0d9488; background: #ccfbf1; padding: 5px 11px; border-radius: 20px;
}
.card--hot .card__badge { color: #fff; background: #14b8a6; }
.card__title { font-size: 17px; font-weight: 800; line-height: 1.3; color: #0f3d36; }
.card__desc { font-size: 13px; line-height: 1.65; color: #4b6b65; }
.card__price { display: flex; align-items: baseline; gap: 4px; }
.card__price b { font-size: 28px; font-weight: 800; color: #0f3d36; }
.card__price small { font-size: 12px; color: #6b8c85; }
.card__btn {
font: inherit; font-weight: 700; font-size: 14px; cursor: pointer;
border: none; border-radius: 10px; padding: 11px;
color: #062e29; background: #5eead4; transition: background .2s, transform .1s;
}
.card--hot .card__btn { color: #fff; background: linear-gradient(90deg, #14b8a6, #0ea5e9); }
.card__btn:hover { background: #2dd4bf; }
.card--hot .card__btn:hover { filter: brightness(1.05); }
.card__btn:active { transform: scale(.97); }
@media (prefers-reduced-motion: reduce) {
.card, .card__btn { transition: none; }
}
【JavaScript】
// 選択中のプランをハイライト(クリックで切り替え・null安全)
(() => {
const cards = document.querySelectorAll('.card');
if (!cards.length) return;
cards.forEach((card) => {
const btn = card.querySelector('.card__btn');
if (!btn) return;
btn.addEventListener('click', () => {
cards.forEach((c) => c.classList.remove('card--hot'));
card.classList.add('card--hot');
});
});
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。