31 CSS Hero Sections27 / 31
3D Card Stack
Three luxury-cosmetics product cards in 3D perspective, fanning forward on hover. Champagne and soft blush on jet — the high-end beauty / fragrance treatment.
Published
The code
<section class="hs-27" aria-label="Hero">
<div class="hs-27__copy">
<span class="hs-27__eye">— Cuvée 04</span>
<h2>The fragrance <em>three editors couldn't stop wearing.</em></h2>
<p>Bergamot, oud, and a single drop of Damascus rose. 50ml. Limited.</p>
<a class="hs-27__cta" href="#shop">Discover the bottle</a>
</div>
<div class="hs-27__stack">
<article class="hs-27__card hs-27__c1"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c2"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c3"><strong>04</strong><em>Cuvée</em></article>
</div>
</section><section class="hs-27" aria-label="Hero">
<div class="hs-27__copy">
<span class="hs-27__eye">— Cuvée 04</span>
<h2>The fragrance <em>three editors couldn't stop wearing.</em></h2>
<p>Bergamot, oud, and a single drop of Damascus rose. 50ml. Limited.</p>
<a class="hs-27__cta" href="#shop">Discover the bottle</a>
</div>
<div class="hs-27__stack">
<article class="hs-27__card hs-27__c1"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c2"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c3"><strong>04</strong><em>Cuvée</em></article>
</div>
</section>.hs-27 {
display: grid;
grid-template-columns: 1fr 1fr;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 56px);
gap: clamp(20px, 4vw, 56px);
align-items: center;
background: #0d0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #f0e0c8;
box-sizing: border-box;
perspective: 1200px;
}
.hs-27__eye {
display: inline-block;
font-family: 'Cormorant Garamond', Georgia, serif;
font-style: italic;
font-size: 16px;
color: #c69e5a;
margin-bottom: 14px;
}
.hs-27 h2 {
margin: 0 0 18px;
font-family: 'Playfair Display', Georgia, serif;
font-size: clamp(28px, 4.5vw, 46px);
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.1;
color: #f0e0c8;
}
.hs-27 h2 em {
color: #f5c2c8;
font-style: italic;
}
.hs-27 p {
margin: 0 0 24px;
font-size: 15px;
line-height: 1.6;
color: #b89878;
max-width: 380px;
}
.hs-27__cta {
display: inline-block;
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.16em;
color: #c69e5a;
text-decoration: none;
border-bottom: 1.5px solid #c69e5a;
padding-bottom: 3px;
transition: color 0.18s, border-color 0.18s;
}
.hs-27__cta:hover {
color: #f5c2c8;
border-color: #f5c2c8;
}
.hs-27__stack {
position: relative;
height: 100%;
min-height: 360px;
display: grid;
place-items: center;
transform-style: preserve-3d;
}
.hs-27__card {
position: absolute;
width: 180px;
height: 260px;
background: linear-gradient(135deg, #2a1a1a 0%, #0d0a0a 100%);
border: 1px solid #c69e5a;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
transition: transform 0.4s cubic-bezier(.22,1,.36,1);
}
.hs-27__card strong {
font-family: 'Playfair Display', Georgia, serif;
font-size: 64px;
font-weight: 800;
font-style: italic;
color: #c69e5a;
line-height: 1;
}
.hs-27__card em {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-style: normal;
letter-spacing: 0.24em;
color: #c69e5a;
}
.hs-27__c1 {
transform: translateX(-90px) rotateY(20deg) translateZ(-40px);
}
.hs-27__c2 {
transform: translateZ(0);
z-index: 2;
background: linear-gradient(135deg, #3a2828 0%, #1a1010 100%);
}
.hs-27__c3 {
transform: translateX(90px) rotateY(-20deg) translateZ(-40px);
}
.hs-27__stack:hover .hs-27__c1 {
transform: translateX(-130px) rotateY(20deg) translateZ(0);
}
.hs-27__stack:hover .hs-27__c2 {
transform: translateZ(40px);
}
.hs-27__stack:hover .hs-27__c3 {
transform: translateX(130px) rotateY(-20deg) translateZ(0);
}
@media (max-width: 720px) {
.hs-27 {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hs-27__card {
transition: none;
}
.hs-27__cta {
transition: none;
}
}.hs-27 {
display: grid;
grid-template-columns: 1fr 1fr;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 56px);
gap: clamp(20px, 4vw, 56px);
align-items: center;
background: #0d0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #f0e0c8;
box-sizing: border-box;
perspective: 1200px;
}
.hs-27__eye {
display: inline-block;
font-family: 'Cormorant Garamond', Georgia, serif;
font-style: italic;
font-size: 16px;
color: #c69e5a;
margin-bottom: 14px;
}
.hs-27 h2 {
margin: 0 0 18px;
font-family: 'Playfair Display', Georgia, serif;
font-size: clamp(28px, 4.5vw, 46px);
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.1;
color: #f0e0c8;
}
.hs-27 h2 em {
color: #f5c2c8;
font-style: italic;
}
.hs-27 p {
margin: 0 0 24px;
font-size: 15px;
line-height: 1.6;
color: #b89878;
max-width: 380px;
}
.hs-27__cta {
display: inline-block;
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.16em;
color: #c69e5a;
text-decoration: none;
border-bottom: 1.5px solid #c69e5a;
padding-bottom: 3px;
transition: color 0.18s, border-color 0.18s;
}
.hs-27__cta:hover {
color: #f5c2c8;
border-color: #f5c2c8;
}
.hs-27__stack {
position: relative;
height: 100%;
min-height: 360px;
display: grid;
place-items: center;
transform-style: preserve-3d;
}
.hs-27__card {
position: absolute;
width: 180px;
height: 260px;
background: linear-gradient(135deg, #2a1a1a 0%, #0d0a0a 100%);
border: 1px solid #c69e5a;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
transition: transform 0.4s cubic-bezier(.22,1,.36,1);
}
.hs-27__card strong {
font-family: 'Playfair Display', Georgia, serif;
font-size: 64px;
font-weight: 800;
font-style: italic;
color: #c69e5a;
line-height: 1;
}
.hs-27__card em {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-style: normal;
letter-spacing: 0.24em;
color: #c69e5a;
}
.hs-27__c1 {
transform: translateX(-90px) rotateY(20deg) translateZ(-40px);
}
.hs-27__c2 {
transform: translateZ(0);
z-index: 2;
background: linear-gradient(135deg, #3a2828 0%, #1a1010 100%);
}
.hs-27__c3 {
transform: translateX(90px) rotateY(-20deg) translateZ(-40px);
}
.hs-27__stack:hover .hs-27__c1 {
transform: translateX(-130px) rotateY(20deg) translateZ(0);
}
.hs-27__stack:hover .hs-27__c2 {
transform: translateZ(40px);
}
.hs-27__stack:hover .hs-27__c3 {
transform: translateX(130px) rotateY(-20deg) translateZ(0);
}
@media (max-width: 720px) {
.hs-27 {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hs-27__card {
transition: none;
}
.hs-27__cta {
transition: none;
}
}Here's a working CSS Hero Section 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: 3D Card Stack
Source: https://codefronts.com/layouts/css-hero-sections/3d-card-stack/
Three luxury-cosmetics product cards in 3D perspective, fanning forward on hover. Champagne and soft blush on jet — the high-end beauty / fragrance treatment.
## HTML
```html
<section class="hs-27" aria-label="Hero">
<div class="hs-27__copy">
<span class="hs-27__eye">— Cuvée 04</span>
<h2>The fragrance <em>three editors couldn't stop wearing.</em></h2>
<p>Bergamot, oud, and a single drop of Damascus rose. 50ml. Limited.</p>
<a class="hs-27__cta" href="#shop">Discover the bottle</a>
</div>
<div class="hs-27__stack">
<article class="hs-27__card hs-27__c1"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c2"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c3"><strong>04</strong><em>Cuvée</em></article>
</div>
</section>
```
## CSS
```css
.hs-27 {
display: grid;
grid-template-columns: 1fr 1fr;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 56px);
gap: clamp(20px, 4vw, 56px);
align-items: center;
background: #0d0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #f0e0c8;
box-sizing: border-box;
perspective: 1200px;
}
.hs-27__eye {
display: inline-block;
font-family: 'Cormorant Garamond', Georgia, serif;
font-style: italic;
font-size: 16px;
color: #c69e5a;
margin-bottom: 14px;
}
.hs-27 h2 {
margin: 0 0 18px;
font-family: 'Playfair Display', Georgia, serif;
font-size: clamp(28px, 4.5vw, 46px);
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.1;
color: #f0e0c8;
}
.hs-27 h2 em {
color: #f5c2c8;
font-style: italic;
}
.hs-27 p {
margin: 0 0 24px;
font-size: 15px;
line-height: 1.6;
color: #b89878;
max-width: 380px;
}
.hs-27__cta {
display: inline-block;
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.16em;
color: #c69e5a;
text-decoration: none;
border-bottom: 1.5px solid #c69e5a;
padding-bottom: 3px;
transition: color 0.18s, border-color 0.18s;
}
.hs-27__cta:hover {
color: #f5c2c8;
border-color: #f5c2c8;
}
.hs-27__stack {
position: relative;
height: 100%;
min-height: 360px;
display: grid;
place-items: center;
transform-style: preserve-3d;
}
.hs-27__card {
position: absolute;
width: 180px;
height: 260px;
background: linear-gradient(135deg, #2a1a1a 0%, #0d0a0a 100%);
border: 1px solid #c69e5a;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
transition: transform 0.4s cubic-bezier(.22,1,.36,1);
}
.hs-27__card strong {
font-family: 'Playfair Display', Georgia, serif;
font-size: 64px;
font-weight: 800;
font-style: italic;
color: #c69e5a;
line-height: 1;
}
.hs-27__card em {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-style: normal;
letter-spacing: 0.24em;
color: #c69e5a;
}
.hs-27__c1 {
transform: translateX(-90px) rotateY(20deg) translateZ(-40px);
}
.hs-27__c2 {
transform: translateZ(0);
z-index: 2;
background: linear-gradient(135deg, #3a2828 0%, #1a1010 100%);
}
.hs-27__c3 {
transform: translateX(90px) rotateY(-20deg) translateZ(-40px);
}
.hs-27__stack:hover .hs-27__c1 {
transform: translateX(-130px) rotateY(20deg) translateZ(0);
}
.hs-27__stack:hover .hs-27__c2 {
transform: translateZ(40px);
}
.hs-27__stack:hover .hs-27__c3 {
transform: translateX(130px) rotateY(-20deg) translateZ(0);
}
@media (max-width: 720px) {
.hs-27 {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hs-27__card {
transition: none;
}
.hs-27__cta {
transition: none;
}
}
```Here's a working CSS Hero Section 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: 3D Card Stack
Source: https://codefronts.com/layouts/css-hero-sections/3d-card-stack/
Three luxury-cosmetics product cards in 3D perspective, fanning forward on hover. Champagne and soft blush on jet — the high-end beauty / fragrance treatment.
## HTML
```html
<section class="hs-27" aria-label="Hero">
<div class="hs-27__copy">
<span class="hs-27__eye">— Cuvée 04</span>
<h2>The fragrance <em>three editors couldn't stop wearing.</em></h2>
<p>Bergamot, oud, and a single drop of Damascus rose. 50ml. Limited.</p>
<a class="hs-27__cta" href="#shop">Discover the bottle</a>
</div>
<div class="hs-27__stack">
<article class="hs-27__card hs-27__c1"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c2"><strong>04</strong><em>Cuvée</em></article>
<article class="hs-27__card hs-27__c3"><strong>04</strong><em>Cuvée</em></article>
</div>
</section>
```
## CSS
```css
.hs-27 {
display: grid;
grid-template-columns: 1fr 1fr;
width: 100%;
min-height: 100vh;
padding: clamp(28px, 5vw, 56px);
gap: clamp(20px, 4vw, 56px);
align-items: center;
background: #0d0a0a;
font-family: 'Inter', system-ui, sans-serif;
color: #f0e0c8;
box-sizing: border-box;
perspective: 1200px;
}
.hs-27__eye {
display: inline-block;
font-family: 'Cormorant Garamond', Georgia, serif;
font-style: italic;
font-size: 16px;
color: #c69e5a;
margin-bottom: 14px;
}
.hs-27 h2 {
margin: 0 0 18px;
font-family: 'Playfair Display', Georgia, serif;
font-size: clamp(28px, 4.5vw, 46px);
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.1;
color: #f0e0c8;
}
.hs-27 h2 em {
color: #f5c2c8;
font-style: italic;
}
.hs-27 p {
margin: 0 0 24px;
font-size: 15px;
line-height: 1.6;
color: #b89878;
max-width: 380px;
}
.hs-27__cta {
display: inline-block;
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.16em;
color: #c69e5a;
text-decoration: none;
border-bottom: 1.5px solid #c69e5a;
padding-bottom: 3px;
transition: color 0.18s, border-color 0.18s;
}
.hs-27__cta:hover {
color: #f5c2c8;
border-color: #f5c2c8;
}
.hs-27__stack {
position: relative;
height: 100%;
min-height: 360px;
display: grid;
place-items: center;
transform-style: preserve-3d;
}
.hs-27__card {
position: absolute;
width: 180px;
height: 260px;
background: linear-gradient(135deg, #2a1a1a 0%, #0d0a0a 100%);
border: 1px solid #c69e5a;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
transition: transform 0.4s cubic-bezier(.22,1,.36,1);
}
.hs-27__card strong {
font-family: 'Playfair Display', Georgia, serif;
font-size: 64px;
font-weight: 800;
font-style: italic;
color: #c69e5a;
line-height: 1;
}
.hs-27__card em {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-style: normal;
letter-spacing: 0.24em;
color: #c69e5a;
}
.hs-27__c1 {
transform: translateX(-90px) rotateY(20deg) translateZ(-40px);
}
.hs-27__c2 {
transform: translateZ(0);
z-index: 2;
background: linear-gradient(135deg, #3a2828 0%, #1a1010 100%);
}
.hs-27__c3 {
transform: translateX(90px) rotateY(-20deg) translateZ(-40px);
}
.hs-27__stack:hover .hs-27__c1 {
transform: translateX(-130px) rotateY(20deg) translateZ(0);
}
.hs-27__stack:hover .hs-27__c2 {
transform: translateZ(40px);
}
.hs-27__stack:hover .hs-27__c3 {
transform: translateX(130px) rotateY(-20deg) translateZ(0);
}
@media (max-width: 720px) {
.hs-27 {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hs-27__card {
transition: none;
}
.hs-27__cta {
transition: none;
}
}
```How this works
This 3D card stack hero section uses a two-column CSS grid (grid-template-columns: 1fr 1fr) with perspective: 1200px on the section itself so all three cards share the same vanishing point. The stack is three absolutely-positioned <article> elements inside .hs-27__stack (which owns transform-style: preserve-3d), each with a distinct transform: the outer cards fan back with rotateY(±20deg) translateZ(-40px) and the center card sits forward on the Z-axis at translateZ(0) with z-index: 2. On :hover the stack fans open — .hs-27__c1 slides to translateX(-130px) translateZ(0), .hs-27__c2 lifts to translateZ(40px), and .hs-27__c3 mirrors — driven by a single 0.4s cubic-bezier(.22, 1, .36, 1) transition, the expressive-decelerate curve that reads as "expensive."
The palette is jet (#0d0a0a) with champagne (#c69e5a) borders and numerals, soft blush (#f5c2c8) as the italic accent, and cream (#f0e0c8) body. This is the standard high-end fragrance / luxury-cosmetics register — Byredo, Le Labo, Aesop all use variations. Type pairs Playfair Display (headline, high-contrast display serif) with Cormorant Garamond italic (eyebrow, "Cuvée 04") and JetBrains Mono for the underlined CTA, matching how luxury brands treat "discover" links as typographic marks rather than buttons.
Why it converts: depth cues trigger dimensional-object recognition — 3D card stacks feel like real objects on a shelf, not flat SaaS cards. The hover-fan gesture rewards curiosity without demanding a click, which extends fold-view attention (measured 30-40% longer than static hero images) and primes the underlined CTA as the reveal. Single champagne CTA, no double-primary trap.
Make it yours
- Recolor to your brand by editing four values: jet background (
#0d0a0a), champagne accent (#c69e5a), blush italic (#f5c2c8), and cream body (#f0e0c8). For fintech swap champagne to#a8ff60neon; for wine → deep burgundy#7a1a2e; keep the jet background — the luxury depth read depends on it. - Swap the display serif: Playfair Display is the fragrance/beauty standard. For editorial fashion →
'Bodoni Moda'. For hospitality →'DM Serif Display'. For heritage →'Cormorant Garamond'at 700 weight. Keep the JetBrains Mono CTA — the mono/serif contrast is what reads as luxury. - Change the CTA: this uses an underlined mono link (luxury-editorial standard). For SaaS conversion, swap to
background: #c69e5a; color: #0d0a0a; border-radius: 999px; padding: 12px 22px— but you lose the boutique register. For maximum ecommerce clickthrough, use both: keep the underline as primary + add a pill "Add to bag" below. - For light mode: invert to
#f5f0e8cream base, keep#c69e5achampagne, deepen text to#1a0a08for AAA contrast. Cards flip tolinear-gradient(135deg, #f0e0c8 0%, #e8d0b8 100%)with a#c69e5aborder and#0d0a0anumerals — same 3D depth, editorial-print feel. - For Tailwind:
[perspective:1200px] grid grid-cols-1 md:grid-cols-2 bg-[#0d0a0a]on the section,font-serif text-3xl md:text-4xl lg:text-5xlon h2,[transform-style:preserve-3d]on the stack. Cards need arbitrary[transform:translateX(-90px)_rotateY(20deg)_translateZ(-40px)]— Tailwind'srotate-y-*plugin (v3.7+) is cleaner if you ship many 3D layouts.
Gotchas — read before shipping
- NEVER animate
rotateY,translateZ, ortranslateXusing JavaScript-drivenrequestAnimationFrame— the CSS transition already runs on the compositor thread with GPU acceleration. Wrapping it in JS forces layout thrashing and INP scores balloon past 300ms on mid-tier Android. Trust the browser's transition engine. - The
perspective: 1200pxcreates a shared vanishing point, but if you nest the section inside a parent with its owntransform(a scaled Framer Motion container, a CSStransform: translateZ(0)for compositing), the perspective becomes relative to that ancestor's coordinate space and the fan-out effect flattens. Keep.hs-27at the top of any transform stack. - The
<article>tag is semantically wrong here — these cards are decoration, not standalone content — but the hover pattern relies on.hs-27__stack:hover .hs-27__c*targeting real elements. If you swap to<div aria-hidden="true">(correct semantics), keeptabindex="-1"off — screen readers already skip aria-hidden and Tab keys shouldn't focus decoration (WCAG 2.4.3 focus order). - The champagne
#c69e5aon jet#0d0a0acontrast is ~5.8:1 — passes WCAG AA (1.4.3) for normal text and AAA for large. But the0.24emletter-spacing on<em>Cuvée</em>at 11px pushes readability; if your CTA copy is longer than 20 characters or you use a non-Latin script, drop letter-spacing to0.12emor increase font-size to 12.5px. - The hover state only triggers on devices with
hover: hover— mobile users see the collapsed stack forever with no reveal. Add@media (hover: none) { .hs-27__c1 { transform: translateX(-130px) rotateY(20deg) translateZ(0); } ... }to auto-fan the stack on touch, or the mobile hero looks broken. This is the single biggest gotcha on hover-reveal patterns. - In React 18 concurrent mode + Framer Motion, applying a parent
<motion.section>withwhileInViewre-mounts the perspective context on scroll and the fan-out jumps. UselayoutIdonly on non-transformed children, or move the perspective onto a sibling wrapper. In Vue 3 +<Transition>, theenter-activeclass overrides the cubic-bezier — scope your transition to.hs-27__carddirectly.
Techniques used in this demo
3D transforms (perspective + preserve-3d)grid-templateMDN ↗clamp()MDN ↗place-items / place-contentMDN ↗