Skip to main content

Stacking Cards — Pure CSS, No Plugins

Inspired by Mak’s great tutorial on stackable cards, I decided to put a little twist on it! I used his visual guide for the card layout in Divi 5, but handled the sticky effects and spacing purely through CSS. It was a fun way to practice using Divi 5’s custom CSS options versus the standard property panel. Definitely a solid exercise if you’re looking to sharpen your CSS skills within the new Divi 5 environment!

I’m using the KrafterPRO preset system — and honestly, I find myself constantly referring back to his excellent, updated Divi5 - Mastery II

A friendly guide for non-coders. You'll copy two snippets of CSS, type a few class names in Divi, and have a beautiful scroll effect live in under 10 minutes.

Beginner-friendly·No JavaScript·Divi 5 · with or without Divi5-Child Theme

As visitors scroll down, each card slides over the previous one — leaving a small peeking strip of the card behind it. The effect is popular on modern landing pages, feature sections, and storytelling layouts. Best of all: it's just CSS.

How it works in one sentence: Every card gets position: sticky — it "sticks" to the top of the viewport when scrolled past. Each card gets a slightly bigger top offset, so the ones behind it peek out. That's the entire trick.

Step 1 — Build the Structure in Divi 5

Mak's workflow: design one Row with a Group inside it (image left, content right). Then duplicate that Row 3, 4, or 5 times — one duplicate per card. The Row itself is the card. The CSS class goes directly on each Row.

Divi structure

CSS Class field (Advanced tab) -> Attributes ->Section ->no class needed

Row -> Attributes -> Add Atributes -> Attribut value

Row 1 ← this is Card 1 Attribut value: stack-card stack-card-1

Row 2 ← Card 2 — (duplicate of Row 1) Attribut value: stack-card stack-card-2

Row 3 ← Card 3 — duplicate again Attribut value: stack-card stack-card-3

Row 4 ← Card 4 — and so on… Attribut value stack-card stack-card-4

The smart workflow: Build Row 1 completely — design the Group with image, text, and buttons exactly how you want it. Then right-click the Row → Duplicate. Do this 3–5 times. Only then assign the CSS classes and swap out the content per card.

Inside the Row — why a Group?

Group inside the Row acts as a flexible layout container. Set the Group to Flexbox (row direction) so the image Group and the content Group sit side by side. Inside the content Group, a Button-Group set to Flexbox lets you arrange buttons next to each other, stacked, left- or right-aligned — all by clicking, no code needed.

Where exactly does the class name go?

The path in Divi 5: Row → Advanced → Attributes → Add Attribute → select "class". A small panel opens with four fields. Only one needs input:

1) Click on the Row → open its settings → click the Advanced tab.

2) Find the Attributes section, click Add Attribute, and from the dropdown select class.

3) A small panel opens — type your class names into Attribute Value only. Leave all other fields as they are.

Divi 5 · Row → Advanced → Attributes → Add Attribute → class

Attribute · class×

Admin Label

 Leave empty — or optional label just for your own reference in the builder.

Target Element

Module▾

Leave on "Module" — this targets the Row itself. Don't change it.

Attribute Name

class

Already filled in as "class" — leave it exactly as is.

Attribute Value ← type here

stack-card stack-card-1

This is the only field you fill in.
Two names separated by exactly one space — no dots, no commas.
Use stack-card-2 for Row 2, stack-card-3 for Row 3, etc.

Row Attribute Value — type exactly this Row 1 (1st card) stack-card stack-card-1 Row 2 (2nd card) stack-card stack-card-2 Row 3 (3rd card) stack-card stack-card-3 Row 4 (4th card) stack-card stack-card-4

Two class names — why? stack-card is shared by all Rows and activates position: stickystack-card-1stack-card-2 etc. sets the individual sticky offset for that specific card. Both go into Attribute Value, separated by one space — no dots.

stack-card stack-card-2

.stack-card .stack-card-2

The CSS

Step 2 — Add the CSS

You have two options. Use Option A if you want the quickest setup. Use Option B if you already have a Child Theme set up (recommended for the long run).

Option A — Divi Theme Options (quickest)

1

WordPress Dashboard → Divi → Theme Options

2

Stay on the General tab and scroll all the way to the bottom — you'll find the Custom CSS box there

3

Paste the code and click Save Changes

Good to know: Divi's Custom CSS box (Theme Options) and WordPress's own Additional CSS field (Appearance → Customize) are synced — they show the same content. Both apply the CSS globally to your entire site. The Divi path is just more direct.

Don't confuse this with the per-element CSS fields! Inside the Divi Builder, every Section, Row and Module has an Advanced tab with CSS fields called BeforeMain Element, and After. Those are for styling that one specific element only — and they correspond to CSS pseudo-elements (::before / ::after). They are not the right place for our global stacking card rules.

Option B — Child Theme style.css (recommended)

1) WordPress Dashboard → Appearance → Theme File Editor

2) Select your Child Theme in the top-right dropdown

3) Click style.css in the file list and paste the code at the bottom of the file

--> or use a FTP-Client (like FileZilla) and an Editor (like vsCode or else) for the style.css

Why a Child Theme? A Child Theme keeps your CSS safe when Divi updates. Without one, an update can erase everything in Theme Options. If you don't have one yet — search for "Divi Child Theme" — setup takes about 5 minutes.

The CSS — copy this entire block

/* =============================================
   STACKING CARDS — Divi 5  (final, tested)

   Row → Advanced → Attributes → Add Attribute → class
   Attribute Value:
   Row 1  →  stack-card stack-card-1
   Row 2  →  stack-card stack-card-2
   Row 3  →  stack-card stack-card-3
   (and so on — no class needed on the Section)
   ============================================= */

/* ── SETTINGS — only change these three values ────────────────
   --navbar:   height of your fixed navigation bar in px.
               Card 1 stops this far from the top edge.
               No fixed navbar? → set to 0px
   --peek:     visible strip of each card below the active one.
               40px–60px works well for most designs.
   --scroll:   how much scroll room each card gets before the
               next one slides over it. Use vh (viewport height)
               so it adapts to every screen size automatically.
   ──────────────────────────────────────────────────────────── */
:root {
  --navbar: 80px;   /* ← your navbar height                  */
  --peek:   40px;   /* ← visible strip per card              */
  --scroll: 60vh;   /* ← scroll room per card (see below)    */
}

/* All Rows — Divi's own class combined with ours so our
   rules win against Divi's built-in styles               */
.et_pb_row.stack-card {
  position: sticky !important;
  border-radius: 16px;
  margin-bottom: var(--scroll) !important; /* scroll room */
  box-shadow: 0 -4px 20px rgba(0,0,0,0.12); /* card depth */
}

/* Each card sticks at: navbar + (card index × peek gap)
   Formula: top = var(--navbar) + (n-1) × var(--peek)    */
.et_pb_row.stack-card-1 { top: calc(var(--navbar) + 0 * var(--peek)); z-index: 1; }
.et_pb_row.stack-card-2 { top: calc(var(--navbar) + 1 * var(--peek)); z-index: 2; }
.et_pb_row.stack-card-3 { top: calc(var(--navbar) + 2 * var(--peek)); z-index: 3; }
.et_pb_row.stack-card-4 { top: calc(var(--navbar) + 3 * var(--peek)); z-index: 4; }
.et_pb_row.stack-card-5 { top: calc(var(--navbar) + 4 * var(--peek)); z-index: 5; }
/* More cards? top = calc(var(--navbar) + (n-1) * var(--peek)), z-index = n */


/* Overflow fix — prevents Divi from clipping sticky cards */
.et_pb_section:has(.stack-card) {
  overflow: visible !important;
}

/* Mobile: disable stacking on small screens */
<a data-user_name="media" class="fcom_mention fcom_route" href="https://diviuniversity.com/portal/u/media/">mitchell royster</a> (max-width: 768px) {
  .et_pb_row.stack-card {
    position: relative !important;
    top: auto !important;
    margin-bottom: 20px !important;
  }
}

♫ "...it's only CSS code, but I like it..." ♩♪ 👄

So far, so good. But remember what I preach: Always use a functional backup strategy and test first in a staging environment—otherwise, you're playing with fire!"

What is calc()? CSS can do simple math. `calc(80px + 2 40px) gives 160px. Combined with the variables at the top, you only ever change the three numbers in :root` — all card positions adjust automatically.*

No fixed navbar? Set --navbar: 0px. Card 1 will then stick right at the top of the browser window. ( 0px is not the ideal solution if you're using a menu bar. A sticky or fixed navbar needs an offset here exp.: 40 px)

How much scroll room?

The --scroll value: how long each card stays visible

The --scroll value controls how much the visitor has to scroll before the next card slides over the current one. Think of it as the reading time you give each card.

Using vh (viewport height) instead of fixed pixels means it automatically adapts — on a large desktop monitor the card gets more scroll room, on a small phone less. A card set to 60vh always feels about the same regardless of screen size.

Value Feel Best for 40vh Fast — cards flip quickly Short cards, simple content, quick overview 60vh Comfortable — natural reading pace Most designs ✓ good starting point 80vh Slow — visitor fully reads each card Long text, detailed features, storytelling 100vh One card per "screen page" Dramatic reveals, full-screen cards

How the stacking plays out end-to-end: Cards stack up one by one as you scroll down — each new card slides over the previous one, leaving a visible --peek strip. Once all cards are stacked, the visitor sees the complete fan of cards. When they continue scrolling, the entire stack leaves the screen together as one unit. This is correct behaviour — position: sticky holds each card only while its parent Section is still on screen.

Troubleshooting

If the effect is not working

The CSS above uses .et_pb_row.stack-card — Divi's own class name combined with yours. This is important: without it, Divi's own styles have higher priority and silently override position: sticky. If you copied an earlier version of this CSS that only used .stack-card (without .et_pb_row in front), replace it with the final version above.

Check with the Browser Inspector

1) Press F12 to open the Inspector. Click the cursor icon, then click your Row on the page.

2) Check the class="..." on the Row div — you should see stack-card stack-card-1 in there. If not, the class was not saved correctly.

3) In the Styles panel (right side), look for position. If sticky is crossed out, a Divi rule is winning. The fix: make sure your CSS uses .et_pb_row.stack-card, not just .stack-card.

4) Look for overflow: hidden on any parent element. The :has() rule in the CSS above handles this automatically — but only if your browser supports it (all modern browsers since 2023 do).

In my test setup— didn't encounter any issues with overflow: hidden. However, during my research, I came across a note regarding overflow: hidden and wanted to mention it.


Child Theme Tip - if you use the style.css in a Divi5 - Child Theme

I’d like to show how to manage organized CSS code within a style.css file instead of using the Divi 5 CSS options. This clarifies why a Divi 5 child theme is far from outdated.

Keeping your style.css tidy

As your site grows, your style.css will grow too. A Table of Contents at the top makes it easy to navigate:

/*
 Theme Name:   My Divi Child Theme
 Template:     Divi
*/

/* =============================================
   TABLE OF CONTENTS

   1. Global Styles
   2. Header & Navigation
   3. Homepage
   4. Stacking Cards          ← your new section
   5. Blog
   6. Footer
   7. Mobile / Responsive
   ============================================= */


/* ─── 4. STACKING CARDS ───────────────────────
   Last updated: [your date here]
   ─────────────────────────────────────────── */

/* paste the stacking cards CSS here */

Add a "Last updated" note inside the comment block each time you change something. Three months from now, you'll thank yourself for it.

Quick Reference — Class Names at a Glance

Divi element CSS Class field Note Section nothing needed Leave CSS Class empty Row 1 (Card 1) stack-card stack-card-1 Two names, one space Row 2 (Card 2) stack-card stack-card-2 Two names, one space Row 3 (Card 3) stack-card stack-card-3 Two names, one space Row 4+ (Card 4+) stack-card stack-card-N Keep the pattern going

Three settings control everything:
--navbar = fixed nav height · --peek = strip between cards · --scroll = reading time per card (40–100vh)

The key to making it work in Divi 5: always use .et_pb_row.stack-card as the CSS selector — not just .stack-card alone. Divi's own styles would otherwise silently win. Everything else is just three numbers at the top of the CSS: navbar height, peek gap, and scroll room. Happy building.

Mak .

That's really awesome 🤩 Do you have a link to the final design?

Frank Schlatter

Mak . 

Hi Mak!

I’ve polished the cards a bit so they don’t look as ugly as they did when they were just placeholders for the CSS functionality. This is a test page (WordPress & Divi 5) featuring stackable cards, where the sticky and stack effects are controlled via custom CSS in the Divi options.

The link will be available for a limited time only.

Link for Demo "Stackable Cards with CSS-Code"

https://discover.schlatter-waldshut.de/test/

Greetings Frank

Frank Schlatter

Stacking Cards with CSS-Code PART 2

Please note: This article in no way questions Mak's excellent video or his visual approach! It is simply intended to show that there are many different ways to work with Divi 5 and use Mak's KrafterPRO presets.

--

(CSS: Not as complex as it looks & this article also decodes Divi 5 identifiers such as et_pb_ and others for you and what happens in Divi5.)

Rest assured, you don’t need to be a coder or a CSS pro to try out CSS snippets in a staging environment—even if the detailed explanation feels like a 'book with seven seals' at first. With every attempt, your confidence grows, and your learning curve rises. Over time, a deeper understanding will naturally follow.

--

Using the CSS code below and pasting it into the designated CSS input area in the Divi 5 options is not complicated. Anyone who has quickly implemented the first part will only notice the issue when adding more rows and sections below the cards on their page.

But why does this happen? Why did it work before, and why are additional elements suddenly being stacked?

For a better understanding of why things happen the way they do in Divi 5 and its rendering engine, check out this detailed article.

Stacking Cards in Divi 5 — The Cards Work, But Now Other Things Stack Too

Once the stacking effect is live you may notice two unexpected side effects: elements without a class get overlapped, and the section below the stack slides under the cards. Here is why that happens and the exact CSS that fixes it.

Follows Part 1·Divi 5 · CSS·Tested & confirmed

After getting everything working from Part 1, two side effects can appear:

Symptom 1

Rows inside the stack section that have no CSS class get visually overlapped by the sticky cards

Symptom 2

The section after the stack slides underneath the cards instead of coming cleanly on top

Both problems have the same root cause — and neither is caused by a mistake in your setup.

Why this happens

When you give an element position: sticky and a z-index, it creates something CSS calls a stacking context. Think of it as a Z-layer competition: every element on the page now has to declare where it sits in that layer order.

Your stack cards declared their position (z-index: 1–5). Everything else — the next section, any un-classed rows — never declared theirs, so CSS assigns them the default: z-index: 0. Zero is lower than 1. They slide under the cards.

Z-layer order — what the browser sees

z-index: 3

Row — stack-card-3

← highest layer, on top

z-index: 2

Row — stack-card-2

z-index: 1

Row — stack-card-1

z-index: 0

Next section — no z-index set

← buried under the cards ⚠

z-index: 0

Row without class — no z-index set

← buried under the cards ⚠

There is a second reason it looks bad: elements in Divi without an explicit background colour are transparent. A transparent element sitting above the cards doesn't visually block them — the cards just show straight through.

Fix 1

The section after the stack slides under the cards

The fix: give the next section a z-index higher than any card. The CSS selector :has(.stack-card) + .et_pb_section means "the section directly after a section that contains stack cards" — no class needs to be added in Divi, CSS finds it automatically.

/* The section directly after the card stack.
   z-index: 10 is higher than any card (cards go up to 5)
   so it slides cleanly on top when scrolled into view.

   "+" = the section immediately after — not all sections. */
.et_pb_section:has(.stack-card) + .et_pb_section {
  position: relative;  /* required — z-index only works on positioned elements */
  z-index: 10;
}

Also set a background colour on that section in Divi. Even with the correct z-index, a transparent section lets the cards show through it. In Divi: click the section → Design → Background → pick any colour. White or your page background colour is fine.

Fix 2

Rows without a class get overlapped

The cleanest solution: move those rows into their own separate section. The stack section should only contain the stacking rows — nothing else. This is also better for the Divi builder layout long-term.

Best practice: one section, one purpose. Put only the stack rows inside the stack section. Any other content — intro text, headings, unrelated rows — belongs in its own section before or after.

If moving the rows is not possible, this rule pulls the un-classed rows above the cards using :not() to leave the stack-card rows untouched:

/* Only use this if you cannot move the rows to
   a separate section.
   Targets rows inside the stack section that do
   NOT have the stack-card class — leaves the
   stack rows completely untouched.               */
.et_pb_section:has(.stack-card) .et_pb_row:not(.stack-card) {
  position: relative;
  z-index: 10;
}

Complete CSS

Full updated code — replace your Part 1 CSS with this

The two new blocks are marked with ← Part 2. Everything else is identical to Part 1.

/* =============================================
   STACKING CARDS — Divi 5  (Part 1 + Part 2)
   ============================================= */

/* ── SETTINGS ─────────────────────────────────
   --navbar   Fixed nav height. No fixed nav? → 0px
   --peek     Visible strip per card (40–60px)
   --scroll   Scroll room per card before next
              card slides over. vh adapts to
              screen size automatically.
              40vh = fast · 60vh = comfortable
              80vh = slow · 100vh = one per screen
   ──────────────────────────────────────────── */
:root {
  --navbar: 80px;
  --peek:   40px;
  --scroll: 60vh;
}

/* ── ALL STACK ROWS ───────────────────────────
   .et_pb_row combined with .stack-card gives
   our rules higher specificity than Divi's own
   stylesheet — without it Divi silently wins.  */
.et_pb_row.stack-card {
  position: sticky !important;
  border-radius: 16px;
  margin-bottom: var(--scroll) !important;
  box-shadow: 0 -4px 20px rgba(0,0,0,0.12);
}

/* ── CARD POSITIONS ───────────────────────────
   Formula: top = navbar + (n-1) × peek         */
.et_pb_row.stack-card-1 { top: calc(var(--navbar) + 0 * var(--peek)); z-index: 1; }
.et_pb_row.stack-card-2 { top: calc(var(--navbar) + 1 * var(--peek)); z-index: 2; }
.et_pb_row.stack-card-3 { top: calc(var(--navbar) + 2 * var(--peek)); z-index: 3; }
.et_pb_row.stack-card-4 { top: calc(var(--navbar) + 3 * var(--peek)); z-index: 4; }
.et_pb_row.stack-card-5 { top: calc(var(--navbar) + 4 * var(--peek)); z-index: 5; }
/* More cards: top = calc(var(--navbar) + (n-1) * var(--peek)), z-index = n */

/* ── OVERFLOW FIX ─────────────────────────────
   Prevents Divi's overflow: hidden from
   breaking position: sticky on the section.    */
.et_pb_section:has(.stack-card) {
  overflow: visible !important;
}

/* ── FIX 1: SECTION AFTER THE STACK  ← Part 2 ─
   Cards have z-index 1–5. Without this rule the
   next section defaults to z-index 0 and slides
   under the cards when scrolled into view.
   "+" = the section directly after — not all.

   Also set a background colour on that section
   in Divi (Design → Background) — transparent
   sections still show the cards through them.  */
.et_pb_section:has(.stack-card) + .et_pb_section {
  position: relative;
  z-index: 10;
}

/* ── FIX 2: ROWS WITHOUT CLASS  ← Part 2 ──────
   Only needed if you have non-stack rows inside
   the same section. Better to move them to their
   own section. If not possible, this pulls them
   above the cards. :not() leaves stack rows alone. */
.et_pb_section:has(.stack-card) .et_pb_row:not(.stack-card) {
  position: relative;
  z-index: 10;
}

/* ── MOBILE ───────────────────────────────────
   Stacking disabled below 768px.               */
<a data-user_name="media" class="fcom_mention fcom_route" href="https://diviuniversity.com/portal/u/media/">mitchell royster</a> (max-width: 768px) {
  .et_pb_row.stack-card {
    position: relative !important;
    top: auto !important;
    margin-bottom: 20px !important;
  }
}

The two new selectors — what they do

Selector What it fixes :has(.stack-card) + .et_pb_section Section after the stack comes cleanly on top instead of sliding under the cards. No class needed in Divi — CSS finds it automatically. .et_pb_row:not(.stack-card) Un-classed rows inside the stack section are pulled above the cards. Use only if moving the rows to a separate section is not possible.

The stacking effect itself was already working correctly. These two additions are purely about layer order — telling the elements around the stack where they belong in the visual hierarchy. The golden rule: give every section a background colour, and make sure the stack section contains only the stacking rows.

--

I tested the code, and it works fine in my environment. BUT PLEASE NOTE: Always use a functional backup strategy (backup plugin) you can rely on. And please test code in a staging environment first, not on a live site.