Hello Mak
I have written to support but have not received any response. I have been logged out of Divi 5 Mastery ll. I am a life time member, I would be grateful if I could continue the course.
Br
Roland
This is a response to our Divi University member @kendold.
Huge thank you to Frank Schlatter for helping out!
I will be releasing Version 2.0.1 Tomorrow.
If you've ever imported a design system into Divi 5 and watched your global variables disappear — you know the panic.
I've been there.
One bad import. Server times out halfway through. Blank state. Everything gone.
That's exactly why I built this into KrafterPRO 2.0.
I just dropped a sneak peek video showing you what's new — and honestly, this update changes how you manage Divi 5 sites at a fundamental level.
Here's a taste of what's inside:
- Atomic imports — nothing gets written to your DB until everything is verified (no more half-broken imports)
- Rollback history — one click to undo any import instantly
- Full preview before you commit — see every color, font, and spacing token before a single thing changes
Please give me your thoughts and feedback. I will be releasing KrafterPRO 2.0 Tomorrow.
Part 3 of our series on building collapsible content cards in Divi 5. Parts 1 and 2 covered the visual approach using Divi 5 Interactions. This part shows the CSS and JavaScript solution — smoother, cleaner, and with no duplicate content in your HTML.
Why This Approach Is Better Than the Visual Method
In Part 2 we built the expand and collapse behaviour using Divi 5 Interactions with two Text modules — one cropped preview and one full version. That approach works without any code, but it has a fundamental drawback: the same text content exists twice in your HTML. Search engines and screen readers see duplicated content, and every time you duplicate the card you have to manually update the Interaction targets in both buttons.
The CSS and JavaScript approach in this article uses a single Text module. The full text lives in one place. CSS limits how much is visible. JavaScript toggles a class to reveal or hide the rest. No duplicate content, no manual target updates after duplicating.
Additional improvements over the visual method:
- Smooth expand and collapse animation — no snapping
- Soft fade gradient at the bottom of the truncated text
- A single button that dynamically changes its own label
- Works automatically for every card on the page without individual configuration
The Module Structure in Divi 5
The structure is flat and clean. The outer Group module wraps everything and receives the CSS class that the JavaScript uses as its reference point:
Section
└── Row
└── Column → CSS Class: card-outer
└── Group module
├── Heading module
├── Image module
├── Text module → CSS Class: card-text
└── Group module → CSS Class: card-buttons
└── Button → CSS Class: btn-toggle
One Text module. One Button. No Interactions configured anywhere on any element.
How to Assign the CSS Classes
For each element above, open its settings and go to Advanced → Attributes → Add Attribute:
- Attribute Name:
class - Attribute Value: the class name shown in the structure above
The card-outer class goes on the Column, not the Group. To access the Column in Divi 5: click the Row gear icon → properties panel → Content → Elements → Column.
Set an Admin Label in the Content tab of every module. This keeps the Layers panel readable and makes duplication manageable.
The Complete CSS
Paste this into Divi → Theme Options → General → Custom CSS, or into your child theme's style.css:
/* ================================================
STACK CARDS — sticky card layout
================================================ */
/* Settings */
:root {
--navbar: 120px;
--peek: 40px;
}
/* Combine Divi's own class with custom class
to win against Divi's CSS specificity */
.et_pb_row.stack-card {
position: sticky !important;
border-radius: 16px;
margin-bottom: 60vh !important;
z-index: 1;
}
.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; }
/* Overflow fix for the section container */
.et_pb_section:has(.stack-card) {
overflow: visible !important;
}
/* ================================================
CARD TEXT — collapsible text block
================================================ */
/* Limit the visible height of the text block */
.card-text {
max-height: 96px;
overflow: hidden;
position: relative;
transition: max-height 0.5s ease;
}
/* Soft fade gradient at the bottom edge */
.card-text::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 56px;
background: linear-gradient(transparent, #ffffff);
/* Change #ffffff to match your card background colour */
pointer-events: none;
transition: opacity 0.3s ease;
}
/* Expanded state: release the height limit */
.card-text.is-open {
max-height: 2000px;
}
/* Expanded state: fade the gradient out */
.card-text.is-open::after {
opacity: 0;
}
/* ================================================
CARD BUTTON — toggle button label
================================================ */
/* Stabilise button position and prevent flash on load */
.btn-toggle {
position: relative;
overflow: hidden;
}
/* Freeze the border so it does not animate on load */
.btn-toggle,
.btn-toggle:hover,
.btn-toggle:focus,
.btn-toggle:active {
border-width: 2px !important;
border-style: solid !important;
}
/* Hide the original label set in the Divi builder */
.btn-toggle .et_pb_button_text {
visibility: hidden;
}
/* Show the initial label via CSS using ::before
— leaves Divi's own ::after hover effect untouched */
.btn-toggle::before {
content: 'Read more \2193';
visibility: visible;
position: absolute;
left: 0;
right: 0;
text-align: center;
z-index: 2;
}
/* Switch label when card is in expanded state */
.btn-toggle.is-active::before {
content: 'Read less \2191';
}
The Complete JavaScript
Way 1 — Divi Theme Options (no child theme needed)
Go to Divi → Theme Options → Integration and paste the following into the field "Add code to the \ of your blog":
<script>
document.addEventListener('DOMContentLoaded', function() {
/* Find all toggle buttons on the page */
document.querySelectorAll('.btn-toggle').forEach(function(btn) {
/* Target the inner text element — Divi wraps button text in a span */
var btnText = btn.querySelector('.et_pb_button_text') || btn;
/* Set initial label directly on the text element */
btnText.textContent = 'Read more \u2193';
btn.addEventListener('click', function(e) {
/* Prevent the browser from following the button link */
e.preventDefault();
/* Prevent the click event from bubbling up to parent elements */
e.stopPropagation();
/* Find the card container that holds this button */
var card = this.closest('.card-outer');
/* Find the text block inside that card */
var text = card.querySelector('.card-text');
/* Toggle the expanded state on the text block */
var isOpen = text.classList.toggle('is-open');
/* Update the inner text element directly */
var btnText = this.querySelector('.et_pb_button_text') || this;
btnText.textContent = isOpen ? 'Read less \u2191' : 'Read more \u2193';
});
});
});
</script>
Way 2 — Child Theme (recommended for professional builds)
Create the file wp-content/themes/[your-child-theme]/js/card-toggle.js with this content (no script tags):
document.addEventListener('DOMContentLoaded', function() {
/* Find all toggle buttons on the page */
document.querySelectorAll('.btn-toggle').forEach(function(btn) {
/* Target the inner text element — Divi wraps button text in a span */
var btnText = btn.querySelector('.et_pb_button_text') || btn;
/* Set initial label directly on the text element */
btnText.textContent = 'Read more \u2193';
btn.addEventListener('click', function(e) {
/* Prevent the browser from following the button link */
e.preventDefault();
/* Prevent the click event from bubbling up to parent elements */
e.stopPropagation();
/* Find the card container that holds this button */
var card = this.closest('.card-outer');
/* Find the text block inside that card */
var text = card.querySelector('.card-text');
/* Toggle the expanded state on the text block */
var isOpen = text.classList.toggle('is-open');
/* Update the inner text element directly */
var btnText = this.querySelector('.et_pb_button_text') || this;
btnText.textContent = isOpen ? 'Read less \u2191' : 'Read more \u2193';
});
});
});
Then add this to your child theme's functions.php:
function mytheme_enqueue_card_scripts() {
wp_enqueue_script(
'card-toggle',
get_stylesheet_directory_uri() . '/js/card-toggle.js',
array(),
'1.0.0',
true /* load in footer, before </body> */
);
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_card_scripts' );
Important: if your functions.php already contains a wp_enqueue_scripts function, add the wp_enqueue_script line inside the existing function — do not create a second function with the same name.
Security Notes
Only paste code you have read and understand. JavaScript added to your WordPress site runs on every page for every visitor. Code from unknown sources can steal session data, inject unwanted content, or open your site to attacks. The code in this guide contains no external requests, no data collection, and no eval() calls.
Never use eval(). The eval() function executes arbitrary strings as JavaScript. It is rarely necessary in legitimate code and is a common vector for injected malicious scripts. If you encounter code containing eval(), treat it with caution.
Use wp_enqueue_script, not direct script tags. In functions.php, always register scripts through WordPress's enqueue system. Never output <script> tags directly from PHP — this bypasses WordPress's dependency and caching management.
Increment the version number after updates. When you modify card-toggle.js, change '1.0.0' to '1.0.1' in the enqueue call. Browsers cache JavaScript aggressively — without a version change, visitors may see the old version for hours after your update.
What to Test After Adding the Code
Always test in the live frontend — not in the Divi Visual Builder. The builder loads its own scripts that can interfere with the results. Use a private / incognito window for the most accurate test.
- Initial state — text cropped to approximately four lines, fade gradient visible, button shows "Read more ↓"
- Expand — smooth animation, fade disappears, button switches to "Read less ↑"
- Collapse — smooth animation back, fade reappears, button returns to "Read more ↓"
- Multiple cards — duplicate the card, confirm each button only affects its own card
- Background colour — if your card background is not white, update
#ffffffin the CSS gradient - Mobile — check that 96px still shows approximately four lines at your mobile font size
- No page jump on click — the button should not scroll the page
- No flash on load — button border and label should appear immediately without flickering
One Text Module vs. Two Text Modules — Summary
Visual method (Part 2) CSS + JS method (this article) Code required None CSS + JS Text modules needed Two (duplicate content) One Duplicate content in HTML Yes No After duplicating the card Manual target updates needed Nothing to update Smooth animation No — snaps Yes Fade gradient No Yes Single button with dynamic label No — two buttons needed YesThis post is part of a series on building smarter, more interactive layouts in Divi 5
I’ve put together a small collection of tests on this page. Just a heads-up: it’s all about functionality, not aesthetics! This page isn't going to win any beauty contests, nor is it meant to. Its purpose is simply to show whether certain elements work for the blog posts on diviuniversity.com.
If you’re interested, feel free to rebuild these setups yourself and then use the KrafterPRO presets to create a truly beautiful design. This link will only be active for a short time. Thanks for understanding!
I just got a message from ET on how to build an image hover reveal effect. When I watched the video (10 minutes) it showed all the steps needed to format the example. Two things stood out to me. First, it used individual formatting elements, all of which are automated with KrafterPRO. Second, none of the settings were part of a system. They were just random choices. This video illustrated all of the things Mak has shown to be the least effective way to build in Divi 5. If you have a few minutes to spare, it's worth the time to see an independent video that proves the value of KrafterPRO.
The Divi Mastery Course has New lessons 😅

