23 CSS Flip Cards 21 / 23
Click-to-Flip JavaScript Toggle Card
A card that flips strictly on a click event using a JS-toggled helper class rather than a hover pseudo-class — ideal for touch and keyboard interfaces.
The code
<div class="fc-16">
<div class="fc-16__meta">
<h2>Click-to-Flip · JavaScript Toggle</h2>
<p>Uses a CSS class toggle — no hover required</p>
<div class="fc-16__click-pill"><span class="fc-16__click-dot"></span>Click anywhere on the card</div>
</div>
<div class="fc-16__scene" id="fc-16-scene">
<div class="fc-16__card" id="fc-16-card">
<div class="fc-16__front">
<div class="fc-16__click-icon">
<svg viewBox="0 0 24 24"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
</div>
<div class="fc-16__label">Click Trigger · JS Toggle</div>
<div class="fc-16__title">Click to Flip</div>
<div class="fc-16__sub">This card flips on click, not hover — great for mobile touch interfaces where hover states don't exist.</div>
<button class="fc-16__click-cta" id="fc-16-flip-btn">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="m9 18 6-6-6-6"/></svg>
Click to Reveal
</button>
</div>
<div class="fc-16__back">
<div class="fc-16__back-icon">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#4ade80" stroke-width="1.8"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="fc-16__back-title">Flip Complete ✓</div>
<div class="fc-16__back-sub">The JS toggles <code style="color:#4ade80;background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px">.is-flipped</code> on the card element — no hover pseudo-class needed.</div>
<div class="fc-16__code-block">
<span class="fc-16__cmt">// toggle flip on click</span><br>
card.<span class="fc-16__kw">classList</span>.<br>
<span class="fc-16__str">toggle</span>(<span class="fc-16__str">'is-flipped'</span>);
</div>
<button class="fc-16__flip-back-btn" id="fc-16-back-btn">← Flip Back</button>
</div>
</div>
</div>
</div> <div class="fc-16">
<div class="fc-16__meta">
<h2>Click-to-Flip · JavaScript Toggle</h2>
<p>Uses a CSS class toggle — no hover required</p>
<div class="fc-16__click-pill"><span class="fc-16__click-dot"></span>Click anywhere on the card</div>
</div>
<div class="fc-16__scene" id="fc-16-scene">
<div class="fc-16__card" id="fc-16-card">
<div class="fc-16__front">
<div class="fc-16__click-icon">
<svg viewBox="0 0 24 24"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
</div>
<div class="fc-16__label">Click Trigger · JS Toggle</div>
<div class="fc-16__title">Click to Flip</div>
<div class="fc-16__sub">This card flips on click, not hover — great for mobile touch interfaces where hover states don't exist.</div>
<button class="fc-16__click-cta" id="fc-16-flip-btn">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="m9 18 6-6-6-6"/></svg>
Click to Reveal
</button>
</div>
<div class="fc-16__back">
<div class="fc-16__back-icon">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#4ade80" stroke-width="1.8"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="fc-16__back-title">Flip Complete ✓</div>
<div class="fc-16__back-sub">The JS toggles <code style="color:#4ade80;background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px">.is-flipped</code> on the card element — no hover pseudo-class needed.</div>
<div class="fc-16__code-block">
<span class="fc-16__cmt">// toggle flip on click</span><br>
card.<span class="fc-16__kw">classList</span>.<br>
<span class="fc-16__str">toggle</span>(<span class="fc-16__str">'is-flipped'</span>);
</div>
<button class="fc-16__flip-back-btn" id="fc-16-back-btn">← Flip Back</button>
</div>
</div>
</div>
</div>.fc-16,.fc-16 *,.fc-16 *::before,.fc-16 *::after{box-sizing:border-box;margin:0;padding:0}
.fc-16 ::selection{background:#f97316;color:#fff}
.fc-16{
--bg:#0c0800;--orange:#f97316;--yellow:#fbbf24;--white:#fffbf0;
--card-w:340px;--card-h:420px;
font-family:'Segoe UI',system-ui,sans-serif;
background:radial-gradient(ellipse at 50% 40%,#1a0e00,#0c0800 65%);
min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;
padding:40px 20px;gap:20px;color:var(--white);
}
.fc-16__meta{text-align:center}
.fc-16__meta h2{font-size:16px;font-weight:700;color:var(--white)}
.fc-16__meta p{font-size:11px;color:rgba(255,251,240,.4);margin-top:4px}
.fc-16__click-pill{
display:inline-flex;align-items:center;gap:6px;margin-top:8px;
padding:6px 14px;border-radius:20px;
background:rgba(249,115,22,.12);border:1px solid rgba(249,115,22,.25);
font-size:11px;color:var(--orange);cursor:default;
}
.fc-16__click-dot{width:7px;height:7px;border-radius:50%;background:var(--orange);animation:fc-16-pulse 1.5s ease-in-out infinite}
@keyframes fc-16-pulse{0%,100%{opacity:.4;transform:scale(.8)}50%{opacity:1;transform:scale(1)}}
/* Card */
.fc-16__scene{width:var(--card-w);height:var(--card-h);perspective:1200px;cursor:pointer;flex-shrink:0}
.fc-16__card{
width:100%;height:100%;position:relative;
transform-style:preserve-3d;
transition:transform .7s cubic-bezier(.4,0,.2,1);
}
/* JS toggle via class */
.fc-16__card.is-flipped{transform:rotateY(180deg)}
.fc-16__front,.fc-16__back{position:absolute;inset:0;border-radius:20px;backface-visibility:hidden;-webkit-backface-visibility:hidden;overflow:hidden}
/* FRONT */
.fc-16__front{
background:linear-gradient(145deg,#1a0e00,#0f0900);
border:1px solid rgba(249,115,22,.25);
display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:16px;padding:36px 28px;
}
.fc-16__front::before{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 40%,rgba(249,115,22,.08),transparent 60%)}
.fc-16__click-icon{
width:80px;height:80px;border-radius:24px;
background:linear-gradient(135deg,rgba(249,115,22,.15),rgba(251,191,36,.08));
border:1px solid rgba(249,115,22,.25);
display:flex;align-items:center;justify-content:center;
position:relative;z-index:1;
box-shadow:0 0 30px rgba(249,115,22,.12);
}
.fc-16__click-icon svg{width:36px;height:36px;stroke:var(--orange);stroke-width:1.8;fill:none}
.fc-16__label{font-size:10px;letter-spacing:.15em;text-transform:uppercase;color:var(--orange);font-weight:700;position:relative;z-index:1}
.fc-16__title{font-size:24px;font-weight:900;color:var(--white);text-align:center;position:relative;z-index:1}
.fc-16__sub{font-size:13px;color:rgba(255,251,240,.45);text-align:center;position:relative;z-index:1;line-height:1.6}
.fc-16__click-cta{
position:relative;z-index:1;
display:flex;align-items:center;gap:8px;margin-top:8px;
padding:12px 24px;border-radius:12px;border:none;cursor:pointer;
background:linear-gradient(135deg,var(--orange),var(--yellow));
color:#000;font-size:13px;font-weight:800;letter-spacing:.04em;
transition:transform .15s,box-shadow .15s;
box-shadow:0 4px 20px rgba(249,115,22,.3);
}
.fc-16__click-cta:hover{box-shadow:0 8px 30px rgba(249,115,22,.5);filter:brightness(1.08)}
.fc-16__click-cta:active{filter:brightness(.95)}
/* BACK */
.fc-16__back{
background:linear-gradient(145deg,#0e1a0a,#081008);
border:1px solid rgba(74,222,128,.25);
transform:rotateY(180deg);
display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:16px;padding:36px 28px;
}
.fc-16__back::before{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 40%,rgba(74,222,128,.06),transparent 60%)}
.fc-16__back-icon{
width:80px;height:80px;border-radius:24px;
background:rgba(74,222,128,.1);border:1px solid rgba(74,222,128,.2);
display:flex;align-items:center;justify-content:center;position:relative;z-index:1;
}
.fc-16__back-title{font-size:22px;font-weight:900;color:var(--white);text-align:center;position:relative;z-index:1}
.fc-16__back-sub{font-size:13px;color:rgba(255,251,240,.5);text-align:center;position:relative;z-index:1;line-height:1.6}
.fc-16__code-block{
position:relative;z-index:1;
background:rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.08);
border-radius:10px;padding:12px 16px;
font-family:'Courier New',monospace;font-size:11px;color:rgba(255,251,240,.6);
line-height:1.8;width:100%;text-align:left;
}
.fc-16__kw{color:#4ade80}.fc-16__str{color:#fb923c}.fc-16__cmt{color:rgba(255,251,240,.3)}
.fc-16__flip-back-btn{
position:relative;z-index:1;
padding:11px 24px;border-radius:12px;border:1px solid rgba(74,222,128,.3);
background:rgba(74,222,128,.1);color:#4ade80;font-size:13px;font-weight:700;
cursor:pointer;transition:background .2s;
}
.fc-16__flip-back-btn:hover{background:rgba(74,222,128,.18)}
@media(prefers-reduced-motion:reduce){.fc-16__card{transition:none}.fc-16__click-dot{animation:none}} .fc-16,.fc-16 *,.fc-16 *::before,.fc-16 *::after{box-sizing:border-box;margin:0;padding:0}
.fc-16 ::selection{background:#f97316;color:#fff}
.fc-16{
--bg:#0c0800;--orange:#f97316;--yellow:#fbbf24;--white:#fffbf0;
--card-w:340px;--card-h:420px;
font-family:'Segoe UI',system-ui,sans-serif;
background:radial-gradient(ellipse at 50% 40%,#1a0e00,#0c0800 65%);
min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;
padding:40px 20px;gap:20px;color:var(--white);
}
.fc-16__meta{text-align:center}
.fc-16__meta h2{font-size:16px;font-weight:700;color:var(--white)}
.fc-16__meta p{font-size:11px;color:rgba(255,251,240,.4);margin-top:4px}
.fc-16__click-pill{
display:inline-flex;align-items:center;gap:6px;margin-top:8px;
padding:6px 14px;border-radius:20px;
background:rgba(249,115,22,.12);border:1px solid rgba(249,115,22,.25);
font-size:11px;color:var(--orange);cursor:default;
}
.fc-16__click-dot{width:7px;height:7px;border-radius:50%;background:var(--orange);animation:fc-16-pulse 1.5s ease-in-out infinite}
@keyframes fc-16-pulse{0%,100%{opacity:.4;transform:scale(.8)}50%{opacity:1;transform:scale(1)}}
/* Card */
.fc-16__scene{width:var(--card-w);height:var(--card-h);perspective:1200px;cursor:pointer;flex-shrink:0}
.fc-16__card{
width:100%;height:100%;position:relative;
transform-style:preserve-3d;
transition:transform .7s cubic-bezier(.4,0,.2,1);
}
/* JS toggle via class */
.fc-16__card.is-flipped{transform:rotateY(180deg)}
.fc-16__front,.fc-16__back{position:absolute;inset:0;border-radius:20px;backface-visibility:hidden;-webkit-backface-visibility:hidden;overflow:hidden}
/* FRONT */
.fc-16__front{
background:linear-gradient(145deg,#1a0e00,#0f0900);
border:1px solid rgba(249,115,22,.25);
display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:16px;padding:36px 28px;
}
.fc-16__front::before{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 40%,rgba(249,115,22,.08),transparent 60%)}
.fc-16__click-icon{
width:80px;height:80px;border-radius:24px;
background:linear-gradient(135deg,rgba(249,115,22,.15),rgba(251,191,36,.08));
border:1px solid rgba(249,115,22,.25);
display:flex;align-items:center;justify-content:center;
position:relative;z-index:1;
box-shadow:0 0 30px rgba(249,115,22,.12);
}
.fc-16__click-icon svg{width:36px;height:36px;stroke:var(--orange);stroke-width:1.8;fill:none}
.fc-16__label{font-size:10px;letter-spacing:.15em;text-transform:uppercase;color:var(--orange);font-weight:700;position:relative;z-index:1}
.fc-16__title{font-size:24px;font-weight:900;color:var(--white);text-align:center;position:relative;z-index:1}
.fc-16__sub{font-size:13px;color:rgba(255,251,240,.45);text-align:center;position:relative;z-index:1;line-height:1.6}
.fc-16__click-cta{
position:relative;z-index:1;
display:flex;align-items:center;gap:8px;margin-top:8px;
padding:12px 24px;border-radius:12px;border:none;cursor:pointer;
background:linear-gradient(135deg,var(--orange),var(--yellow));
color:#000;font-size:13px;font-weight:800;letter-spacing:.04em;
transition:transform .15s,box-shadow .15s;
box-shadow:0 4px 20px rgba(249,115,22,.3);
}
.fc-16__click-cta:hover{box-shadow:0 8px 30px rgba(249,115,22,.5);filter:brightness(1.08)}
.fc-16__click-cta:active{filter:brightness(.95)}
/* BACK */
.fc-16__back{
background:linear-gradient(145deg,#0e1a0a,#081008);
border:1px solid rgba(74,222,128,.25);
transform:rotateY(180deg);
display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:16px;padding:36px 28px;
}
.fc-16__back::before{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 40%,rgba(74,222,128,.06),transparent 60%)}
.fc-16__back-icon{
width:80px;height:80px;border-radius:24px;
background:rgba(74,222,128,.1);border:1px solid rgba(74,222,128,.2);
display:flex;align-items:center;justify-content:center;position:relative;z-index:1;
}
.fc-16__back-title{font-size:22px;font-weight:900;color:var(--white);text-align:center;position:relative;z-index:1}
.fc-16__back-sub{font-size:13px;color:rgba(255,251,240,.5);text-align:center;position:relative;z-index:1;line-height:1.6}
.fc-16__code-block{
position:relative;z-index:1;
background:rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.08);
border-radius:10px;padding:12px 16px;
font-family:'Courier New',monospace;font-size:11px;color:rgba(255,251,240,.6);
line-height:1.8;width:100%;text-align:left;
}
.fc-16__kw{color:#4ade80}.fc-16__str{color:#fb923c}.fc-16__cmt{color:rgba(255,251,240,.3)}
.fc-16__flip-back-btn{
position:relative;z-index:1;
padding:11px 24px;border-radius:12px;border:1px solid rgba(74,222,128,.3);
background:rgba(74,222,128,.1);color:#4ade80;font-size:13px;font-weight:700;
cursor:pointer;transition:background .2s;
}
.fc-16__flip-back-btn:hover{background:rgba(74,222,128,.18)}
@media(prefers-reduced-motion:reduce){.fc-16__card{transition:none}.fc-16__click-dot{animation:none}}(function(){
var card=document.getElementById('fc-16-card');
var scene=document.getElementById('fc-16-scene');
var backBtn=document.getElementById('fc-16-back-btn');
scene.addEventListener('click',function(){card.classList.toggle('is-flipped')});
backBtn.addEventListener('click',function(e){e.stopPropagation();card.classList.remove('is-flipped')});
})(); (function(){
var card=document.getElementById('fc-16-card');
var scene=document.getElementById('fc-16-scene');
var backBtn=document.getElementById('fc-16-back-btn');
scene.addEventListener('click',function(){card.classList.toggle('is-flipped')});
backBtn.addEventListener('click',function(e){e.stopPropagation();card.classList.remove('is-flipped')});
})();How this works
The flip is driven by toggling an .is-flipped class on the card element via a single card.classList.toggle() call in a click listener. The CSS applies transform: rotateY(180deg) whenever .is-flipped is present — the same 3D setup as the hover demos, just triggered differently. A separate Flip Back button calls classList.remove() with stopPropagation() to prevent the parent click from immediately re-toggling.
This pattern solves two real-world problems: touch interfaces where :hover is either unreliable or fires once on first tap, and accessibility contexts where keyboard users expect Enter or Space on a button to trigger actions. Making the trigger a native <button> element gives free keyboard access without extra ARIA.
Customize
- Make the entire card area a click target by attaching the listener to the scene element rather than just the internal button, as shown in this demo.
- Add keyboard support by listening for
keydownwithkey === "Enter" || key === " "on the scene element alongside the click handler. - Persist the flip state across page loads by saving
card.classList.contains("is-flipped")tolocalStorageand restoring it onDOMContentLoaded. - Animate the click CTA button with a brief
scale(0.95)on:activefor tactile press feedback before the card starts flipping. - Build a multi-card Memory game using this pattern — flip pairs on click, compare values, and lock matched pairs open with a permanent class.
Watch out for
- Adding the click listener to the scene wrapper means any click on links inside will also trigger the flip — always
stopPropagation()on inner interactive elements. - Do not add
:hoverflip rules alongside the JS class toggle — the two triggers fight each other on desktop when the user mouses over then clicks. - The
.is-flippedclass approach breaks if the card is inside a framework that resets the DOM on re-render — store flip state in component state, not on the DOM node.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 36+ | 9+ | 16+ | 36+ |
classList.toggle is universally supported; no modern-browser-only APIs are used in this demo.