オートフィット・カードグリッド

grid-template-columns の repeat(auto-fit, minmax()) で列数が自動増減するカードグリッド。幅ボタンで折返しの変化を確認できる。

#css#grid#responsive

ライブデモ

コード

HTML
<!-- オートフィット・カードグリッド:repeat(auto-fit, minmax()) で列数が自動増減 -->
<div class="afc" id="afcRoot">
  <div class="afc__toolbar">
    <p class="afc__label">コンテナ幅</p>
    <div class="afc__btns">
      <button type="button" data-width="narrow">狭い</button>
      <button type="button" data-width="medium" class="is-active">中</button>
      <button type="button" data-width="wide">広い</button>
    </div>
  </div>

  <div class="afc__stage">
    <div class="afc__grid" id="afcGrid">
      <div class="afc-card"><span class="afc-card__no">01</span><p>ダッシュボード</p></div>
      <div class="afc-card"><span class="afc-card__no">02</span><p>プロジェクト管理</p></div>
      <div class="afc-card"><span class="afc-card__no">03</span><p>チームメンバー</p></div>
      <div class="afc-card"><span class="afc-card__no">04</span><p>請求と支払い</p></div>
      <div class="afc-card"><span class="afc-card__no">05</span><p>通知設定</p></div>
      <div class="afc-card"><span class="afc-card__no">06</span><p>アクセス権限</p></div>
      <div class="afc-card"><span class="afc-card__no">07</span><p>レポート出力</p></div>
      <div class="afc-card"><span class="afc-card__no">08</span><p>APIキー管理</p></div>
    </div>
  </div>
</div>
CSS
/* 全体:カードグリッドの折返しをコンテナ幅トグルで実演 */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: "Hiragino Sans", system-ui, sans-serif;
  background: #f4f6fb;
  color: #1b2233;
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}

/* ルート:width:100% が無いと親の flex 中央寄せで潰れ、auto-fit の列計算も崩れるため必須 */
.afc { width: 100%; max-width: 920px; }

.afc__toolbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 16px;
}
.afc__label { font-size: 12px; font-weight: 800; color: #5b6478; }
.afc__btns { display: flex; gap: 6px; }
.afc__btns button {
  font-size: 12.5px; font-weight: 700; padding: 7px 14px; border-radius: 999px;
  border: 1px solid #d8dee9; background: #fff; color: #47506b; cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.afc__btns button:hover { background: #eef2ff; }
.afc__btns button.is-active { background: #4338ca; border-color: #4338ca; color: #fff; }

.afc__stage {
  border: 1px dashed #c7cfdf; border-radius: 16px; padding: 20px;
  display: flex; justify-content: center; overflow: hidden;
}

/* 列数はここで自動計算される:コンテナ幅 ÷ minmax の下限で決まる */
.afc__grid {
  width: var(--afc-w, 290px);   /* 初期値は「中」ボタンと一致させる(既定選択との齟齬を防ぐ) */
  max-width: 100%;
  display: grid;
  /* カード下限を 110px と小さめにして、狭いプレビュー(一覧カード約393px幅)でも 1→2→3 列の変化が出せるようにする */
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 14px;
  transition: width .3s ease;
}

.afc-card {
  background: #fff; border: 1px solid #e6eaf2; border-radius: 12px;
  padding: 18px 14px; display: flex; flex-direction: column; gap: 8px;
  box-shadow: 0 10px 24px -18px rgba(15, 23, 42, .4);
}
.afc-card__no { font-size: 11px; font-weight: 800; letter-spacing: .1em; color: #a5adc0; }
.afc-card p { font-size: 13.5px; font-weight: 700; color: #262d3f; }

@media (prefers-reduced-motion: reduce) {
  .afc__grid { transition: none; }
  .afc__btns button { transition: none; }
}
JavaScript
// コンテナ幅トグル:ボタンでグリッドの幅を変え、auto-fit の折返しを実演(null安全)
(() => {
  const grid = document.getElementById('afcGrid');
  const root = document.getElementById('afcRoot');
  if (!grid || !root) return;

  // 各段でグリッドの列数が確実に変わる幅にする:狭い=1列 / 中=2列 / 広い=コンテナ一杯(3列以上)。
  // 親より広い値は max-width:100% でコンテナ幅に丸められ「中」と「広い」が同化するため、狭い一覧カード
  // (グリッド領域 約393px)でも収まる 170/290px にする。minmax(110px) と組で 170→1列・290→2列・100%→3列。
  const widths = { narrow: '170px', medium: '290px', wide: '100%' };
  const buttons = root.querySelectorAll('[data-width]');
  if (!buttons.length) return;

  buttons.forEach((btn) => {
    btn.addEventListener('click', () => {
      const key = btn.getAttribute('data-width');
      const w = widths[key] || widths.medium;
      grid.style.setProperty('--afc-w', w);
      buttons.forEach((b) => b.classList.remove('is-active'));
      btn.classList.add('is-active');
    });
  });
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「オートフィット・カードグリッド」の効果を追加してください。

# 追加してほしい効果
オートフィット・カードグリッド(レイアウト & グリッド)
grid-template-columns の repeat(auto-fit, minmax()) で列数が自動増減するカードグリッド。幅ボタンで折返しの変化を確認できる。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- オートフィット・カードグリッド:repeat(auto-fit, minmax()) で列数が自動増減 -->
<div class="afc" id="afcRoot">
  <div class="afc__toolbar">
    <p class="afc__label">コンテナ幅</p>
    <div class="afc__btns">
      <button type="button" data-width="narrow">狭い</button>
      <button type="button" data-width="medium" class="is-active">中</button>
      <button type="button" data-width="wide">広い</button>
    </div>
  </div>

  <div class="afc__stage">
    <div class="afc__grid" id="afcGrid">
      <div class="afc-card"><span class="afc-card__no">01</span><p>ダッシュボード</p></div>
      <div class="afc-card"><span class="afc-card__no">02</span><p>プロジェクト管理</p></div>
      <div class="afc-card"><span class="afc-card__no">03</span><p>チームメンバー</p></div>
      <div class="afc-card"><span class="afc-card__no">04</span><p>請求と支払い</p></div>
      <div class="afc-card"><span class="afc-card__no">05</span><p>通知設定</p></div>
      <div class="afc-card"><span class="afc-card__no">06</span><p>アクセス権限</p></div>
      <div class="afc-card"><span class="afc-card__no">07</span><p>レポート出力</p></div>
      <div class="afc-card"><span class="afc-card__no">08</span><p>APIキー管理</p></div>
    </div>
  </div>
</div>

【CSS】
/* 全体:カードグリッドの折返しをコンテナ幅トグルで実演 */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: "Hiragino Sans", system-ui, sans-serif;
  background: #f4f6fb;
  color: #1b2233;
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}

/* ルート:width:100% が無いと親の flex 中央寄せで潰れ、auto-fit の列計算も崩れるため必須 */
.afc { width: 100%; max-width: 920px; }

.afc__toolbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 16px;
}
.afc__label { font-size: 12px; font-weight: 800; color: #5b6478; }
.afc__btns { display: flex; gap: 6px; }
.afc__btns button {
  font-size: 12.5px; font-weight: 700; padding: 7px 14px; border-radius: 999px;
  border: 1px solid #d8dee9; background: #fff; color: #47506b; cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.afc__btns button:hover { background: #eef2ff; }
.afc__btns button.is-active { background: #4338ca; border-color: #4338ca; color: #fff; }

.afc__stage {
  border: 1px dashed #c7cfdf; border-radius: 16px; padding: 20px;
  display: flex; justify-content: center; overflow: hidden;
}

/* 列数はここで自動計算される:コンテナ幅 ÷ minmax の下限で決まる */
.afc__grid {
  width: var(--afc-w, 290px);   /* 初期値は「中」ボタンと一致させる(既定選択との齟齬を防ぐ) */
  max-width: 100%;
  display: grid;
  /* カード下限を 110px と小さめにして、狭いプレビュー(一覧カード約393px幅)でも 1→2→3 列の変化が出せるようにする */
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 14px;
  transition: width .3s ease;
}

.afc-card {
  background: #fff; border: 1px solid #e6eaf2; border-radius: 12px;
  padding: 18px 14px; display: flex; flex-direction: column; gap: 8px;
  box-shadow: 0 10px 24px -18px rgba(15, 23, 42, .4);
}
.afc-card__no { font-size: 11px; font-weight: 800; letter-spacing: .1em; color: #a5adc0; }
.afc-card p { font-size: 13.5px; font-weight: 700; color: #262d3f; }

@media (prefers-reduced-motion: reduce) {
  .afc__grid { transition: none; }
  .afc__btns button { transition: none; }
}

【JavaScript】
// コンテナ幅トグル:ボタンでグリッドの幅を変え、auto-fit の折返しを実演(null安全)
(() => {
  const grid = document.getElementById('afcGrid');
  const root = document.getElementById('afcRoot');
  if (!grid || !root) return;

  // 各段でグリッドの列数が確実に変わる幅にする:狭い=1列 / 中=2列 / 広い=コンテナ一杯(3列以上)。
  // 親より広い値は max-width:100% でコンテナ幅に丸められ「中」と「広い」が同化するため、狭い一覧カード
  // (グリッド領域 約393px)でも収まる 170/290px にする。minmax(110px) と組で 170→1列・290→2列・100%→3列。
  const widths = { narrow: '170px', medium: '290px', wide: '100%' };
  const buttons = root.querySelectorAll('[data-width]');
  if (!buttons.length) return;

  buttons.forEach((btn) => {
    btn.addEventListener('click', () => {
      const key = btn.getAttribute('data-width');
      const w = widths[key] || widths.medium;
      grid.style.setProperty('--afc-w', w);
      buttons.forEach((b) => b.classList.remove('is-active'));
      btn.classList.add('is-active');
    });
  });
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。