31 CSS Hero Sections27 / 31

Pure CSSMIT licensed

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

Live Demo
Try it

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>
.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;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 #a8ff60 neon; 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 #f5f0e8 cream base, keep #c69e5a champagne, deepen text to #1a0a08 for AAA contrast. Cards flip to linear-gradient(135deg, #f0e0c8 0%, #e8d0b8 100%) with a #c69e5a border and #0d0a0a numerals — 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-5xl on h2, [transform-style:preserve-3d] on the stack. Cards need arbitrary [transform:translateX(-90px)_rotateY(20deg)_translateZ(-40px)] — Tailwind's rotate-y-* plugin (v3.7+) is cleaner if you ship many 3D layouts.

Gotchas — read before shipping

  • NEVER animate rotateY, translateZ, or translateX using JavaScript-driven requestAnimationFrame — 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: 1200px creates a shared vanishing point, but if you nest the section inside a parent with its own transform (a scaled Framer Motion container, a CSS transform: translateZ(0) for compositing), the perspective becomes relative to that ancestor's coordinate space and the fan-out effect flattens. Keep .hs-27 at 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), keep tabindex="-1" off — screen readers already skip aria-hidden and Tab keys shouldn't focus decoration (WCAG 2.4.3 focus order).
  • The champagne #c69e5a on jet #0d0a0a contrast is ~5.8:1 — passes WCAG AA (1.4.3) for normal text and AAA for large. But the 0.24em letter-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 to 0.12em or 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> with whileInView re-mounts the perspective context on scroll and the fan-out jumps. Use layoutId only on non-transformed children, or move the perspective onto a sibling wrapper. In Vue 3 + <Transition>, the enter-active class overrides the cubic-bezier — scope your transition to .hs-27__card directly.

Techniques used in this demo

Search CodeFronts

Loading…