3Dカードフリップ
transform-style: preserve-3d と backface-visibility を使い、クリックで表裏が回転するカード。会員証や商品カードの詳細表示に使えます。
ライブデモ
使用例(お題: カフェ 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 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 8830 1175 0042</span>
<span class="flip-card__name">HARUKA M.</span>
<span class="flip-card__valid">VALID THRU 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 8830 1175 0042</span>
<span class="flip-card__name">HARUKA M.</span>
<span class="flip-card__valid">VALID THRU 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で提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。