3Dカードフリップ

transform-style: preserve-3d と backface-visibility を使い、クリックで表裏が回転するカード。会員証や商品カードの詳細表示に使えます。

#css#3d#animation#interaction

ライブデモ

使用例(お題: カフェ MOON BREW)

この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- MOON BREW:会員証カード。クリックで裏返り、特典・スタンプを表示 -->
<section class="mb-stage" aria-label="MOON BREW 会員証">
  <header class="mb-head">
    <span class="mb-logo">◐ MOON BREW</span>
    <span class="mb-head__note">メンバーズカード</span>
  </header>

  <div class="mb-scene">
    <!-- クリック / Enter で表裏フリップ -->
    <div class="mb-card" id="mbCard" tabindex="0" role="button" aria-pressed="false">
      <div class="mb-card__inner">
        <!-- 表面:会員ランクとロゴ -->
        <div class="mb-card__face mb-card__face--front">
          <span class="mb-card__kicker">GOLD MEMBER</span>
          <span class="mb-card__cup" aria-hidden="true">☕</span>
          <h2 class="mb-card__title">MOON BREW<br>COFFEE CLUB</h2>
          <span class="mb-card__hint">タップで裏面 ↻</span>
        </div>
        <!-- 裏面:スタンプと特典 -->
        <div class="mb-card__face mb-card__face--back">
          <span class="mb-card__bno">No. 0824&nbsp;1190</span>
          <div class="mb-stamps" aria-label="スタンプ8個中6個">
            <i class="on"></i><i class="on"></i><i class="on"></i>
            <i class="on"></i><i class="on"></i><i class="on"></i>
            <i></i><i></i>
          </div>
          <p class="mb-card__perk">あと2杯で<b>ドリップ1杯無料</b></p>
          <span class="mb-card__name">中村 はるか 様</span>
        </div>
      </div>
    </div>
  </div>
  <p class="mb-tip">表面のカードをクリックすると裏返ります</p>
</section>
CSS
/* MOON BREW:カフェ会員証の3Dフリップ */
:root {
  --cream: #f5ede1;
  --brown: #2b1d12;
  --amber: #c98a3b;
  --flip-dur: 0.7s;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 400px;
  display: flex;
  flex-direction: column;
  font-family: "Hiragino Mincho ProN", "Georgia", "Segoe UI", serif;
  background:
    radial-gradient(circle at 20% 15%, #fbf5ea 0%, var(--cream) 45%, #ecdfca 100%);
  color: var(--brown);
  overflow: hidden;
}

/* ヘッダー */
.mb-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 16px 22px 0;
}
.mb-logo { font-size: 16px; font-weight: 800; letter-spacing: 0.06em; }
.mb-head__note {
  font-size: 11px; letter-spacing: 0.18em; color: var(--amber); font-weight: 700;
}

/* perspective を親に与えるのが3Dの肝 */
.mb-stage { height: 100%; display: flex; flex-direction: column; }
.mb-scene {
  flex: 1;
  display: grid;
  place-items: center;
  perspective: 1200px;
  perspective-origin: 50% 42%;
}

.mb-card {
  width: 290px;
  height: 184px;
  cursor: pointer;
  border-radius: 16px;
  outline: none;
}
.mb-card:focus-visible { box-shadow: 0 0 0 3px var(--amber); }

.mb-card__inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d; /* 子要素を3D空間へ */
  transition: transform var(--flip-dur) cubic-bezier(.2,.8,.25,1);
  box-shadow: 0 22px 44px -14px rgba(43,29,18,.5);
  border-radius: 16px;
}
.mb-card.is-flipped .mb-card__inner { transform: rotateY(180deg); }

.mb-card__face {
  position: absolute;
  inset: 0;
  border-radius: 16px;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  backface-visibility: hidden; /* 裏面を隠す */
  overflow: hidden;
}

/* 表面 */
.mb-card__face--front {
  background: linear-gradient(140deg, #3a2817 0%, var(--brown) 100%);
  color: var(--cream);
  border: 1px solid rgba(201,138,59,.4);
}
.mb-card__face--front::after {
  content: "";
  position: absolute;
  top: -30%; right: -25%;
  width: 80%; height: 160%;
  background: radial-gradient(circle, rgba(201,138,59,.4), transparent 68%);
  filter: blur(4px);
}
.mb-card__kicker {
  font-size: 11px; letter-spacing: 0.28em; color: var(--amber); font-weight: 700;
}
.mb-card__cup { position: absolute; top: 16px; right: 20px; font-size: 30px; }
.mb-card__title {
  margin: auto 0 0; font-size: 25px; line-height: 1.12;
  font-weight: 800; letter-spacing: 0.03em;
}
.mb-card__hint { margin-top: 8px; font-size: 11px; color: rgba(245,237,225,.6); }

/* 裏面 */
.mb-card__face--back {
  background: linear-gradient(140deg, #fffaf2 0%, #f0e2cd 100%);
  border: 1px solid rgba(201,138,59,.35);
  transform: rotateY(180deg); /* 初期から裏返しておく */
  justify-content: space-between;
}
.mb-card__bno {
  font-family: "Courier New", monospace;
  font-size: 13px; letter-spacing: 0.1em; color: #8a6a3e;
}
.mb-stamps {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
  margin: 2px 0;
}
.mb-stamps i {
  width: 26px; height: 26px; border-radius: 50%;
  border: 1.5px dashed var(--amber);
  display: block;
}
.mb-stamps i.on {
  background: radial-gradient(circle at 35% 30%, #e0a85a, var(--amber));
  border-style: solid;
  box-shadow: inset 0 2px 4px rgba(255,255,255,.4);
}
.mb-card__perk { margin: 0; font-size: 13px; color: var(--brown); }
.mb-card__perk b { color: #a8651f; }
.mb-card__name { font-size: 12px; letter-spacing: 0.08em; color: #6b5238; }

.mb-tip {
  margin: 0; padding: 0 22px 16px;
  text-align: center; font-size: 11px; color: #9a8569;
}

@media (prefers-reduced-motion: reduce) {
  .mb-card__inner { transition-duration: .01ms; }
}
JavaScript
// MOON BREW 会員証:クリック/キーボードで表裏フリップ状態をトグル
(() => {
  const card = document.getElementById("mbCard");
  if (!card) return; // null安全

  const toggle = () => {
    const flipped = card.classList.toggle("is-flipped");
    card.setAttribute("aria-pressed", String(flipped));
  };

  card.addEventListener("click", toggle);
  // Enter / Space でも操作可能に
  card.addEventListener("keydown", (e) => {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      toggle();
    }
  });
})();

コード

HTML
<div class="scene" aria-label="3Dカードフリップのデモ">
  <!-- data-flip 属性をJSでトグルしてフリップ -->
  <div class="flip-card" id="flipCard" tabindex="0" role="button" aria-pressed="false">
    <div class="flip-card__inner">
      <div class="flip-card__face flip-card__face--front">
        <span class="flip-card__kicker">MEMBERSHIP</span>
        <h2 class="flip-card__title">AURORA<br>CLUB</h2>
        <span class="flip-card__hint">クリック / Enter で反転</span>
      </div>
      <div class="flip-card__face flip-card__face--back">
        <span class="flip-card__no">4921&nbsp;8830&nbsp;1175&nbsp;0042</span>
        <span class="flip-card__name">HARUKA M.</span>
        <span class="flip-card__valid">VALID THRU&nbsp;&nbsp;08/29</span>
      </div>
    </div>
  </div>
</div>
CSS
/* ===== 3Dカードフリップ ===== */
:root {
  --c-bg-1: #1b2735;
  --c-bg-2: #090a0f;
  --c-accent: #6ee7ff;
  --c-gold: #f6d6a8;
  --flip-dur: 0.7s;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 360px;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  background: radial-gradient(circle at 30% 20%, #2a3a52 0%, var(--c-bg-1) 40%, var(--c-bg-2) 100%);
  color: #fff;
  overflow: hidden;
}

/* perspective を親に与えるのが3Dの肝 */
.scene {
  perspective: 1200px;
  perspective-origin: 50% 40%;
}

.flip-card {
  width: 300px;
  height: 188px;
  cursor: pointer;
  border-radius: 18px;
  outline: none;
}
.flip-card:focus-visible { box-shadow: 0 0 0 3px var(--c-accent); border-radius: 18px; }

.flip-card__inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d; /* 子要素を3D空間に */
  transition: transform var(--flip-dur) cubic-bezier(.2,.8,.25,1);
  box-shadow: 0 24px 50px -12px rgba(0,0,0,.6);
  border-radius: 18px;
}
/* JSが付与する反転状態 */
.flip-card.is-flipped .flip-card__inner { transform: rotateY(180deg); }

.flip-card__face {
  position: absolute;
  inset: 0;
  border-radius: 18px;
  padding: 22px;
  display: flex;
  flex-direction: column;
  backface-visibility: hidden; /* 裏面を隠す */
  overflow: hidden;
}

.flip-card__face--front {
  background: linear-gradient(135deg, #233a5e 0%, #0f1b2e 100%);
  border: 1px solid rgba(110,231,255,.25);
}
.flip-card__face--front::after {
  content: "";
  position: absolute;
  top: -40%; right: -20%;
  width: 70%; height: 180%;
  background: radial-gradient(circle, rgba(110,231,255,.35), transparent 70%);
  filter: blur(6px);
}

.flip-card__kicker {
  font-size: 11px; letter-spacing: .3em; color: var(--c-accent);
  font-weight: 600;
}
.flip-card__title {
  margin: auto 0 0; font-size: 30px; line-height: 1.05;
  font-weight: 800; letter-spacing: .02em;
}
.flip-card__hint {
  margin-top: 10px; font-size: 11px; color: rgba(255,255,255,.5);
}

.flip-card__face--back {
  background: linear-gradient(135deg, #1a2a44 0%, #0a121f 100%);
  border: 1px solid rgba(246,214,168,.2);
  transform: rotateY(180deg); /* 初期から裏返しておく */
  justify-content: flex-end;
  gap: 6px;
}
.flip-card__face--back::before {
  content: "";
  position: absolute;
  top: 22px; left: 0; right: 0; height: 34px;
  background: #050a12;
}
.flip-card__no {
  font-family: "Courier New", monospace;
  font-size: 16px; letter-spacing: .08em; color: var(--c-gold);
}
.flip-card__name { font-size: 13px; letter-spacing: .12em; }
.flip-card__valid { font-size: 10px; color: rgba(255,255,255,.55); letter-spacing: .1em; }

@media (prefers-reduced-motion: reduce) {
  .flip-card__inner { transition-duration: .01ms; }
}
JavaScript
// 3Dカードフリップ: クリック/キーボードで状態クラスをトグル
(() => {
  const card = document.getElementById('flipCard');
  if (!card) return; // null安全

  const toggle = () => {
    const flipped = card.classList.toggle('is-flipped');
    card.setAttribute('aria-pressed', String(flipped));
  };

  card.addEventListener('click', toggle);
  // Enter / Space でも操作可能に
  card.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      toggle();
    }
  });
})();

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

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

# 追加してほしい効果
3Dカードフリップ(3D & パースペクティブ)
transform-style: preserve-3d と backface-visibility を使い、クリックで表裏が回転するカード。会員証や商品カードの詳細表示に使えます。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<div class="scene" aria-label="3Dカードフリップのデモ">
  <!-- data-flip 属性をJSでトグルしてフリップ -->
  <div class="flip-card" id="flipCard" tabindex="0" role="button" aria-pressed="false">
    <div class="flip-card__inner">
      <div class="flip-card__face flip-card__face--front">
        <span class="flip-card__kicker">MEMBERSHIP</span>
        <h2 class="flip-card__title">AURORA<br>CLUB</h2>
        <span class="flip-card__hint">クリック / Enter で反転</span>
      </div>
      <div class="flip-card__face flip-card__face--back">
        <span class="flip-card__no">4921&nbsp;8830&nbsp;1175&nbsp;0042</span>
        <span class="flip-card__name">HARUKA M.</span>
        <span class="flip-card__valid">VALID THRU&nbsp;&nbsp;08/29</span>
      </div>
    </div>
  </div>
</div>

【CSS】
/* ===== 3Dカードフリップ ===== */
:root {
  --c-bg-1: #1b2735;
  --c-bg-2: #090a0f;
  --c-accent: #6ee7ff;
  --c-gold: #f6d6a8;
  --flip-dur: 0.7s;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 360px;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  background: radial-gradient(circle at 30% 20%, #2a3a52 0%, var(--c-bg-1) 40%, var(--c-bg-2) 100%);
  color: #fff;
  overflow: hidden;
}

/* perspective を親に与えるのが3Dの肝 */
.scene {
  perspective: 1200px;
  perspective-origin: 50% 40%;
}

.flip-card {
  width: 300px;
  height: 188px;
  cursor: pointer;
  border-radius: 18px;
  outline: none;
}
.flip-card:focus-visible { box-shadow: 0 0 0 3px var(--c-accent); border-radius: 18px; }

.flip-card__inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d; /* 子要素を3D空間に */
  transition: transform var(--flip-dur) cubic-bezier(.2,.8,.25,1);
  box-shadow: 0 24px 50px -12px rgba(0,0,0,.6);
  border-radius: 18px;
}
/* JSが付与する反転状態 */
.flip-card.is-flipped .flip-card__inner { transform: rotateY(180deg); }

.flip-card__face {
  position: absolute;
  inset: 0;
  border-radius: 18px;
  padding: 22px;
  display: flex;
  flex-direction: column;
  backface-visibility: hidden; /* 裏面を隠す */
  overflow: hidden;
}

.flip-card__face--front {
  background: linear-gradient(135deg, #233a5e 0%, #0f1b2e 100%);
  border: 1px solid rgba(110,231,255,.25);
}
.flip-card__face--front::after {
  content: "";
  position: absolute;
  top: -40%; right: -20%;
  width: 70%; height: 180%;
  background: radial-gradient(circle, rgba(110,231,255,.35), transparent 70%);
  filter: blur(6px);
}

.flip-card__kicker {
  font-size: 11px; letter-spacing: .3em; color: var(--c-accent);
  font-weight: 600;
}
.flip-card__title {
  margin: auto 0 0; font-size: 30px; line-height: 1.05;
  font-weight: 800; letter-spacing: .02em;
}
.flip-card__hint {
  margin-top: 10px; font-size: 11px; color: rgba(255,255,255,.5);
}

.flip-card__face--back {
  background: linear-gradient(135deg, #1a2a44 0%, #0a121f 100%);
  border: 1px solid rgba(246,214,168,.2);
  transform: rotateY(180deg); /* 初期から裏返しておく */
  justify-content: flex-end;
  gap: 6px;
}
.flip-card__face--back::before {
  content: "";
  position: absolute;
  top: 22px; left: 0; right: 0; height: 34px;
  background: #050a12;
}
.flip-card__no {
  font-family: "Courier New", monospace;
  font-size: 16px; letter-spacing: .08em; color: var(--c-gold);
}
.flip-card__name { font-size: 13px; letter-spacing: .12em; }
.flip-card__valid { font-size: 10px; color: rgba(255,255,255,.55); letter-spacing: .1em; }

@media (prefers-reduced-motion: reduce) {
  .flip-card__inner { transition-duration: .01ms; }
}

【JavaScript】
// 3Dカードフリップ: クリック/キーボードで状態クラスをトグル
(() => {
  const card = document.getElementById('flipCard');
  if (!card) return; // null安全

  const toggle = () => {
    const flipped = card.classList.toggle('is-flipped');
    card.setAttribute('aria-pressed', String(flipped));
  };

  card.addEventListener('click', toggle);
  // Enter / Space でも操作可能に
  card.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      toggle();
    }
  });
})();

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

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