15 / 23
All 23 in 23 Tailwind CSS Flip Cards
- 01Tailwind Product Comparison Flip Card with Hover, Tap & Keyboard Reveal
- 02Tailwind Pricing Plan Flip Card with Gradient Price & Feature Reveal
- 03Tailwind Gift Card Flip with Copy-to-Clipboard Coupon Code Reveal
- 04Tailwind Meet the Team Flip Card with Bio, Socials & Contact Reveal
- 05Tailwind Portfolio Flip Card with Animated Skill Bar Proficiency Meters
- 06Tailwind Language Flashcard Flip with Phonetics, Example & Memory Tip
- 07Tailwind Trivia Quiz Flip Card with Answer Reveal & Score Readout
- 08Tailwind Gaming Trading Card Flip with Character Stats & Rarity Glow
- 09Tailwind Photo Gallery Flip Card with EXIF Metadata & Download CTA
- 10Tailwind Video Thumbnail Flip Card with Playlist, Stats & Watch CTA
- 11Tailwind Testimonial Flip Card with Star Rating & Reviewer Profile
- 12Tailwind Digital Business Card Flip with vCard Contacts & QR Panel
- 13Tailwind Restaurant Menu Card with Sliding Ingredient & Nutrition Reveal
- 14Tailwind Process Steps Flip Card Grid with Task Lists & Timelines
- 15Tailwind Vertical Axis Flip Card with rotateX Top-to-Bottom Reveal
- 16Tailwind Click-to-Flip Card with Persisted State & Keyboard Control
- 17Tailwind Auto-Height Flip Card with CSS Grid Stacking & Cross-Fade
- 18Tailwind Responsive Flip Card Grid with Per-Card 3D Perspective
- 19Tailwind Luxury Product Card with Hinged Book-Open 3D Unfold
- 20Tailwind Travel Postcard Flip Card with Diagonal rotate3d Twist
- 21Tailwind Album Card with clip-path Iris Aperture Reveal (No 3D)
- 22Tailwind Event Ticket Card with Bi-Fold Stub Reveal & QR Panel
- 23Tailwind Scratch Card with Diagonal clip-path Peel & Prize Reveal
Tailwind Vertical Axis Flip Card with rotateX Top-to-Bottom Reveal
The axis study: identical machinery to a horizontal flip with rotateY swapped for rotateX in exactly two places. Written as utilities, those two places are both visible in the class list — which is the point of the demo, because forgetting the second one is what renders the back face upside-down.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tfc-15 w-full min-h-svh grid place-items-center content-center gap-5 bg-tfc15-bg font-tfc15-sans px-5 py-10 text-tfc15-ink">
<div class="text-center">
<h2 class="text-lg font-extrabold tracking-[-0.01em] text-white">Vertical axis (rotateX) flip</h2>
<p class="mt-1 text-xs text-tfc15-muted">The card turns top-to-bottom instead of side-to-side</p>
<code class="mt-2 inline-block rounded border border-tfc15-teal/30 bg-tfc15-teal/15 px-2 py-0.5 font-tfc15-mono text-[11px] text-tfc15-teal">transform: rotateX(180deg)</code>
</div>
<div class="group relative mx-auto h-[250px] w-[360px] max-w-full [perspective:1200px] [perspective-origin:50%_80%] rounded-2xl has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-tfc15-teal has-[:focus-visible]:outline-offset-4">
<input id="tfc-15-flip" type="checkbox" class="peer sr-only">
<label for="tfc-15-flip" class="absolute inset-0 z-20 cursor-pointer rounded-2xl"><span class="sr-only">Flip the card on its vertical axis</span></label>
<div class="relative h-full w-full transition-transform duration-700 ease-[cubic-bezier(.4,0,.2,1)] [transform-style:preserve-3d] group-hover:[transform:rotateX(180deg)] group-has-checked:[transform:rotateX(180deg)] group-has-[:focus-visible]:[transform:rotateX(180deg)] motion-reduce:transition-none">
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-panel to-tfc15-panel2 border border-tfc15-teal/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.45_0.09_185/0.5)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-teal/30 bg-linear-135 from-tfc15-teal/20 to-tfc15-emerald/10 shadow-[0_0_20px_oklch(0.6_0.1_185/0.15)]">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-teal stroke-[1.8]" aria-hidden="true"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-teal">Feature · flip axis</p>
<h3 class="text-xl font-extrabold leading-tight tracking-[-0.01em] text-white">Vertical card flip</h3>
<p class="mt-1.5 text-xs text-tfc15-muted">Hover, tap or press Space to turn it top-to-bottom</p>
</div>
<span aria-hidden="true" class="absolute right-4 top-1/2 flex -translate-y-1/2 flex-col items-center gap-0.5 text-[10px] tracking-[0.08em] text-tfc15-teal opacity-35"><span>▲</span><span class="[writing-mode:vertical-rl]">flip</span><span>▼</span></span>
</div>
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-back to-tfc15-back2 border border-tfc15-emerald/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.5_0.12_165/0.45)] [transform:rotateX(180deg)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-emerald/30 bg-linear-135 from-tfc15-emerald/20 to-tfc15-teal/10">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-emerald stroke-[2]" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-emerald">Back face · revealed</p>
<h3 class="text-lg font-extrabold leading-tight text-white">rotateX flip complete</h3>
<ul class="mt-2.5 flex flex-wrap gap-1.5">
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">rotateX(180deg)</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">backface-visibility</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">perspective:1200px</li>
</ul>
</div>
</div>
</div>
</div>
</section><section class="tfc-15 w-full min-h-svh grid place-items-center content-center gap-5 bg-tfc15-bg font-tfc15-sans px-5 py-10 text-tfc15-ink">
<div class="text-center">
<h2 class="text-lg font-extrabold tracking-[-0.01em] text-white">Vertical axis (rotateX) flip</h2>
<p class="mt-1 text-xs text-tfc15-muted">The card turns top-to-bottom instead of side-to-side</p>
<code class="mt-2 inline-block rounded border border-tfc15-teal/30 bg-tfc15-teal/15 px-2 py-0.5 font-tfc15-mono text-[11px] text-tfc15-teal">transform: rotateX(180deg)</code>
</div>
<div class="group relative mx-auto h-[250px] w-[360px] max-w-full [perspective:1200px] [perspective-origin:50%_80%] rounded-2xl has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-tfc15-teal has-[:focus-visible]:outline-offset-4">
<input id="tfc-15-flip" type="checkbox" class="peer sr-only">
<label for="tfc-15-flip" class="absolute inset-0 z-20 cursor-pointer rounded-2xl"><span class="sr-only">Flip the card on its vertical axis</span></label>
<div class="relative h-full w-full transition-transform duration-700 ease-[cubic-bezier(.4,0,.2,1)] [transform-style:preserve-3d] group-hover:[transform:rotateX(180deg)] group-has-checked:[transform:rotateX(180deg)] group-has-[:focus-visible]:[transform:rotateX(180deg)] motion-reduce:transition-none">
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-panel to-tfc15-panel2 border border-tfc15-teal/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.45_0.09_185/0.5)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-teal/30 bg-linear-135 from-tfc15-teal/20 to-tfc15-emerald/10 shadow-[0_0_20px_oklch(0.6_0.1_185/0.15)]">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-teal stroke-[1.8]" aria-hidden="true"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-teal">Feature · flip axis</p>
<h3 class="text-xl font-extrabold leading-tight tracking-[-0.01em] text-white">Vertical card flip</h3>
<p class="mt-1.5 text-xs text-tfc15-muted">Hover, tap or press Space to turn it top-to-bottom</p>
</div>
<span aria-hidden="true" class="absolute right-4 top-1/2 flex -translate-y-1/2 flex-col items-center gap-0.5 text-[10px] tracking-[0.08em] text-tfc15-teal opacity-35"><span>▲</span><span class="[writing-mode:vertical-rl]">flip</span><span>▼</span></span>
</div>
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-back to-tfc15-back2 border border-tfc15-emerald/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.5_0.12_165/0.45)] [transform:rotateX(180deg)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-emerald/30 bg-linear-135 from-tfc15-emerald/20 to-tfc15-teal/10">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-emerald stroke-[2]" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-emerald">Back face · revealed</p>
<h3 class="text-lg font-extrabold leading-tight text-white">rotateX flip complete</h3>
<ul class="mt-2.5 flex flex-wrap gap-1.5">
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">rotateX(180deg)</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">backface-visibility</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">perspective:1200px</li>
</ul>
</div>
</div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tfc15-bg: #04100e;
--color-tfc15-bg: oklch(0.14 0.025 180);
--color-tfc15-panel: #071a16;
--color-tfc15-panel: oklch(0.19 0.04 180);
--color-tfc15-panel2: #04100e;
--color-tfc15-panel2: oklch(0.15 0.025 180);
--color-tfc15-back: #031a14;
--color-tfc15-back: oklch(0.19 0.045 165);
--color-tfc15-back2: #040e0c;
--color-tfc15-back2: oklch(0.14 0.02 175);
--color-tfc15-teal: #0d9488;
--color-tfc15-teal: oklch(0.62 0.1 185);
--color-tfc15-emerald: #10b981;
--color-tfc15-emerald: oklch(0.72 0.16 165);
--color-tfc15-ink: #f0fdf4;
--color-tfc15-ink: oklch(0.98 0.02 160);
--color-tfc15-muted: #9dbfb1;
--color-tfc15-muted: oklch(0.78 0.04 170);
--font-tfc15-sans: 'Sora', system-ui, sans-serif;
--font-tfc15-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tfc-15 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tfc-15 * {
transition-duration: 1ms !important;
}
}@import "tailwindcss";
@theme {
--color-tfc15-bg: #04100e;
--color-tfc15-bg: oklch(0.14 0.025 180);
--color-tfc15-panel: #071a16;
--color-tfc15-panel: oklch(0.19 0.04 180);
--color-tfc15-panel2: #04100e;
--color-tfc15-panel2: oklch(0.15 0.025 180);
--color-tfc15-back: #031a14;
--color-tfc15-back: oklch(0.19 0.045 165);
--color-tfc15-back2: #040e0c;
--color-tfc15-back2: oklch(0.14 0.02 175);
--color-tfc15-teal: #0d9488;
--color-tfc15-teal: oklch(0.62 0.1 185);
--color-tfc15-emerald: #10b981;
--color-tfc15-emerald: oklch(0.72 0.16 165);
--color-tfc15-ink: #f0fdf4;
--color-tfc15-ink: oklch(0.98 0.02 160);
--color-tfc15-muted: #9dbfb1;
--color-tfc15-muted: oklch(0.78 0.04 170);
--font-tfc15-sans: 'Sora', system-ui, sans-serif;
--font-tfc15-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tfc-15 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tfc-15 * {
transition-duration: 1ms !important;
}
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tfc15-bg: #04100e;
--color-tfc15-bg: oklch(0.14 0.025 180);
--color-tfc15-panel: #071a16;
--color-tfc15-panel: oklch(0.19 0.04 180);
--color-tfc15-panel2: #04100e;
--color-tfc15-panel2: oklch(0.15 0.025 180);
--color-tfc15-back: #031a14;
--color-tfc15-back: oklch(0.19 0.045 165);
--color-tfc15-back2: #040e0c;
--color-tfc15-back2: oklch(0.14 0.02 175);
--color-tfc15-teal: #0d9488;
--color-tfc15-teal: oklch(0.62 0.1 185);
--color-tfc15-emerald: #10b981;
--color-tfc15-emerald: oklch(0.72 0.16 165);
--color-tfc15-ink: #f0fdf4;
--color-tfc15-ink: oklch(0.98 0.02 160);
--color-tfc15-muted: #9dbfb1;
--color-tfc15-muted: oklch(0.78 0.04 170);
--font-tfc15-sans: 'Sora', system-ui, sans-serif;
--font-tfc15-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tfc-15 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tfc-15 * {
transition-duration: 1ms !important;
}
}@import "tailwindcss";
@theme {
--color-tfc15-bg: #04100e;
--color-tfc15-bg: oklch(0.14 0.025 180);
--color-tfc15-panel: #071a16;
--color-tfc15-panel: oklch(0.19 0.04 180);
--color-tfc15-panel2: #04100e;
--color-tfc15-panel2: oklch(0.15 0.025 180);
--color-tfc15-back: #031a14;
--color-tfc15-back: oklch(0.19 0.045 165);
--color-tfc15-back2: #040e0c;
--color-tfc15-back2: oklch(0.14 0.02 175);
--color-tfc15-teal: #0d9488;
--color-tfc15-teal: oklch(0.62 0.1 185);
--color-tfc15-emerald: #10b981;
--color-tfc15-emerald: oklch(0.72 0.16 165);
--color-tfc15-ink: #f0fdf4;
--color-tfc15-ink: oklch(0.98 0.02 160);
--color-tfc15-muted: #9dbfb1;
--color-tfc15-muted: oklch(0.78 0.04 170);
--font-tfc15-sans: 'Sora', system-ui, sans-serif;
--font-tfc15-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tfc-15 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tfc-15 * {
transition-duration: 1ms !important;
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@400;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@400;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The whole demo is a two-token diff from demo 01, and in utilities both tokens are readable in the markup rather than buried in two separate rules 40 lines apart.
perspective-originhas no Tailwind scale and should not: it is a per-composition value, so the arbitrary property is the idiomatic answer.- UI change from the CSS original: the teal palette was raised for contrast, the code chips became a real
<ul>in a mono@themefont, and the emoji tick became a stroked SVG so both faces share one icon language.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Vertical Axis Flip Card with rotateX Top-to-Bottom Reveal
Source: https://codefronts.com/components/tailwind-css-flip-cards/tailwind-vertical-axis-flip-card-rotatex-top-to-bottom/
The axis study: identical machinery to a horizontal flip with rotateY swapped for rotateX in exactly two places. Written as utilities, those two places are both visible in the class list — which is the point of the demo, because forgetting the second one is what renders the back face upside-down.
## HTML
```html
<section class="tfc-15 w-full min-h-svh grid place-items-center content-center gap-5 bg-tfc15-bg font-tfc15-sans px-5 py-10 text-tfc15-ink">
<div class="text-center">
<h2 class="text-lg font-extrabold tracking-[-0.01em] text-white">Vertical axis (rotateX) flip</h2>
<p class="mt-1 text-xs text-tfc15-muted">The card turns top-to-bottom instead of side-to-side</p>
<code class="mt-2 inline-block rounded border border-tfc15-teal/30 bg-tfc15-teal/15 px-2 py-0.5 font-tfc15-mono text-[11px] text-tfc15-teal">transform: rotateX(180deg)</code>
</div>
<div class="group relative mx-auto h-[250px] w-[360px] max-w-full [perspective:1200px] [perspective-origin:50%_80%] rounded-2xl has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-tfc15-teal has-[:focus-visible]:outline-offset-4">
<input id="tfc-15-flip" type="checkbox" class="peer sr-only">
<label for="tfc-15-flip" class="absolute inset-0 z-20 cursor-pointer rounded-2xl"><span class="sr-only">Flip the card on its vertical axis</span></label>
<div class="relative h-full w-full transition-transform duration-700 ease-[cubic-bezier(.4,0,.2,1)] [transform-style:preserve-3d] group-hover:[transform:rotateX(180deg)] group-has-checked:[transform:rotateX(180deg)] group-has-[:focus-visible]:[transform:rotateX(180deg)] motion-reduce:transition-none">
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-panel to-tfc15-panel2 border border-tfc15-teal/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.45_0.09_185/0.5)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-teal/30 bg-linear-135 from-tfc15-teal/20 to-tfc15-emerald/10 shadow-[0_0_20px_oklch(0.6_0.1_185/0.15)]">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-teal stroke-[1.8]" aria-hidden="true"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-teal">Feature · flip axis</p>
<h3 class="text-xl font-extrabold leading-tight tracking-[-0.01em] text-white">Vertical card flip</h3>
<p class="mt-1.5 text-xs text-tfc15-muted">Hover, tap or press Space to turn it top-to-bottom</p>
</div>
<span aria-hidden="true" class="absolute right-4 top-1/2 flex -translate-y-1/2 flex-col items-center gap-0.5 text-[10px] tracking-[0.08em] text-tfc15-teal opacity-35"><span>▲</span><span class="[writing-mode:vertical-rl]">flip</span><span>▼</span></span>
</div>
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-back to-tfc15-back2 border border-tfc15-emerald/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.5_0.12_165/0.45)] [transform:rotateX(180deg)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-emerald/30 bg-linear-135 from-tfc15-emerald/20 to-tfc15-teal/10">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-emerald stroke-[2]" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-emerald">Back face · revealed</p>
<h3 class="text-lg font-extrabold leading-tight text-white">rotateX flip complete</h3>
<ul class="mt-2.5 flex flex-wrap gap-1.5">
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">rotateX(180deg)</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">backface-visibility</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">perspective:1200px</li>
</ul>
</div>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfc15-bg: #04100e;
--color-tfc15-bg: oklch(0.14 0.025 180);
--color-tfc15-panel: #071a16;
--color-tfc15-panel: oklch(0.19 0.04 180);
--color-tfc15-panel2: #04100e;
--color-tfc15-panel2: oklch(0.15 0.025 180);
--color-tfc15-back: #031a14;
--color-tfc15-back: oklch(0.19 0.045 165);
--color-tfc15-back2: #040e0c;
--color-tfc15-back2: oklch(0.14 0.02 175);
--color-tfc15-teal: #0d9488;
--color-tfc15-teal: oklch(0.62 0.1 185);
--color-tfc15-emerald: #10b981;
--color-tfc15-emerald: oklch(0.72 0.16 165);
--color-tfc15-ink: #f0fdf4;
--color-tfc15-ink: oklch(0.98 0.02 160);
--color-tfc15-muted: #9dbfb1;
--color-tfc15-muted: oklch(0.78 0.04 170);
--font-tfc15-sans: 'Sora', system-ui, sans-serif;
--font-tfc15-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tfc-15 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tfc-15 * {
transition-duration: 1ms !important;
}
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Vertical Axis Flip Card with rotateX Top-to-Bottom Reveal
Source: https://codefronts.com/components/tailwind-css-flip-cards/tailwind-vertical-axis-flip-card-rotatex-top-to-bottom/
The axis study: identical machinery to a horizontal flip with rotateY swapped for rotateX in exactly two places. Written as utilities, those two places are both visible in the class list — which is the point of the demo, because forgetting the second one is what renders the back face upside-down.
## HTML
```html
<section class="tfc-15 w-full min-h-svh grid place-items-center content-center gap-5 bg-tfc15-bg font-tfc15-sans px-5 py-10 text-tfc15-ink">
<div class="text-center">
<h2 class="text-lg font-extrabold tracking-[-0.01em] text-white">Vertical axis (rotateX) flip</h2>
<p class="mt-1 text-xs text-tfc15-muted">The card turns top-to-bottom instead of side-to-side</p>
<code class="mt-2 inline-block rounded border border-tfc15-teal/30 bg-tfc15-teal/15 px-2 py-0.5 font-tfc15-mono text-[11px] text-tfc15-teal">transform: rotateX(180deg)</code>
</div>
<div class="group relative mx-auto h-[250px] w-[360px] max-w-full [perspective:1200px] [perspective-origin:50%_80%] rounded-2xl has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-tfc15-teal has-[:focus-visible]:outline-offset-4">
<input id="tfc-15-flip" type="checkbox" class="peer sr-only">
<label for="tfc-15-flip" class="absolute inset-0 z-20 cursor-pointer rounded-2xl"><span class="sr-only">Flip the card on its vertical axis</span></label>
<div class="relative h-full w-full transition-transform duration-700 ease-[cubic-bezier(.4,0,.2,1)] [transform-style:preserve-3d] group-hover:[transform:rotateX(180deg)] group-has-checked:[transform:rotateX(180deg)] group-has-[:focus-visible]:[transform:rotateX(180deg)] motion-reduce:transition-none">
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-panel to-tfc15-panel2 border border-tfc15-teal/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.45_0.09_185/0.5)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-teal/30 bg-linear-135 from-tfc15-teal/20 to-tfc15-emerald/10 shadow-[0_0_20px_oklch(0.6_0.1_185/0.15)]">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-teal stroke-[1.8]" aria-hidden="true"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-teal">Feature · flip axis</p>
<h3 class="text-xl font-extrabold leading-tight tracking-[-0.01em] text-white">Vertical card flip</h3>
<p class="mt-1.5 text-xs text-tfc15-muted">Hover, tap or press Space to turn it top-to-bottom</p>
</div>
<span aria-hidden="true" class="absolute right-4 top-1/2 flex -translate-y-1/2 flex-col items-center gap-0.5 text-[10px] tracking-[0.08em] text-tfc15-teal opacity-35"><span>▲</span><span class="[writing-mode:vertical-rl]">flip</span><span>▼</span></span>
</div>
<div class="absolute inset-0 flex items-center gap-5 rounded-2xl overflow-hidden px-8 py-7 bg-linear-160 from-tfc15-back to-tfc15-back2 border border-tfc15-emerald/25 shadow-[0_1px_2px_oklch(0_0_0/0.5),0_20px_44px_-18px_oklch(0.5_0.12_165/0.45)] [transform:rotateX(180deg)] [backface-visibility:hidden] [-webkit-backface-visibility:hidden]">
<span class="grid size-16 shrink-0 place-items-center rounded-[18px] border border-tfc15-emerald/30 bg-linear-135 from-tfc15-emerald/20 to-tfc15-teal/10">
<svg viewBox="0 0 24 24" class="size-7 fill-none stroke-tfc15-emerald stroke-[2]" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
</span>
<div class="flex-1">
<p class="mb-1.5 text-[10px] font-bold uppercase tracking-[0.15em] text-tfc15-emerald">Back face · revealed</p>
<h3 class="text-lg font-extrabold leading-tight text-white">rotateX flip complete</h3>
<ul class="mt-2.5 flex flex-wrap gap-1.5">
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">rotateX(180deg)</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">backface-visibility</li>
<li class="rounded-full border border-tfc15-emerald/20 bg-tfc15-emerald/10 px-2.5 py-1 font-tfc15-mono text-[11px] text-tfc15-ink/75">perspective:1200px</li>
</ul>
</div>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfc15-bg: #04100e;
--color-tfc15-bg: oklch(0.14 0.025 180);
--color-tfc15-panel: #071a16;
--color-tfc15-panel: oklch(0.19 0.04 180);
--color-tfc15-panel2: #04100e;
--color-tfc15-panel2: oklch(0.15 0.025 180);
--color-tfc15-back: #031a14;
--color-tfc15-back: oklch(0.19 0.045 165);
--color-tfc15-back2: #040e0c;
--color-tfc15-back2: oklch(0.14 0.02 175);
--color-tfc15-teal: #0d9488;
--color-tfc15-teal: oklch(0.62 0.1 185);
--color-tfc15-emerald: #10b981;
--color-tfc15-emerald: oklch(0.72 0.16 165);
--color-tfc15-ink: #f0fdf4;
--color-tfc15-ink: oklch(0.98 0.02 160);
--color-tfc15-muted: #9dbfb1;
--color-tfc15-muted: oklch(0.78 0.04 170);
--font-tfc15-sans: 'Sora', system-ui, sans-serif;
--font-tfc15-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tfc-15 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tfc-15 * {
transition-duration: 1ms !important;
}
}
```How this works
Two substitutions, no other change. Perspective, preserve-3d and backface-visibility are identical to the horizontal demos. Swap the rotation function on the card and on the back's resting transform and the card tumbles instead of spinning.
The vanishing point does the heavy lifting. [perspective-origin:50%_80%] puts the viewer below centre, so the top edge sweeps down toward them. With the default centre origin a vertical flip reads flatter than a horizontal one at the same perspective distance.
Why choose this axis. Content with a strong top-to-bottom hierarchy — a status change, a before/after, a calendar date revealing its event — reads more naturally when the reveal moves along the same axis as the reading order.
Make it yours
- Direction:
rotateX(-180deg)on both places flips bottom-over-top instead. - Diagonal tumble:
[transform:rotateX(180deg)_rotateY(10deg)]adds a slight twist — keep the back's pre-rotation matching. - Tall cards distort; raise perspective to
[perspective:2000px]above ~400px of height. - Pair with a
@themekeyframe that drops the back content in after the turn for a two-beat reveal.
Gotchas — read before shipping
- Forgetting the back's
rotateX(180deg)renders its text upside-down, not mirrored — a different symptom from therotateYversion's mirror, and easy to misdiagnose. - Transform order matters:
rotateXbefore anytranslate, or the translation happens in the rotated coordinate space. - On very tall cards the swept edges can leave the viewport mid-turn. More perspective distance, not less, is the fix.
perspective-origingoes on the SCENE (the element withperspective), never on the rotating card.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
rotateX is CSS Transforms Level 1 — universal since 2014. :has() gates the tap and keyboard flip.