Quick Setup
Add these scripts to your Webflow project's Custom Code section (Project Settings > Custom Code > Before </body> tag).
1. Expandable Blog Cards
This animation creates an interactive expandable blog card layout for the blog section. On larger screens, hovering over a blog card expands it while collapsing the others. It also reveals the description and read-more button with a smooth GSAP transition.
What it does
- Expands the hovered blog card
- Collapses the previously active card
- Reveals blog description and read-more button for the active card
- Applies image filter transitions for active/inactive states
- Adds entrance animation to the section subtitle, title, button, and cards
- Automatically disables the effect on tablet and mobile devices
Device support
Desktop only (992px and above)
This interaction is automatically disabled on:
- Tablet
- Mobile landscape
- Mobile portrait
On smaller screens, all cards return to their normal/default layout without GSAP width animation.
Required HTML structure
Example:
.blog-section
└─ .home-blog-flex-box
├─ .blog-card-wrapper.active
│ └─ .home-blog-list-card
│ ├─ .home-blog-thumbnile-block
│ │ └─ .blog-thumbnile
│ └─ .home-blog-list-content-block
│ └─ .home-blog-list-content-inner
│ └─ .home-blog-title-and-description-wrap
│ ├─ .home-blog-title
│ ├─ .home-blog-description-block
│ └─ .home-read-more-button-block
│ └─ .button-hover-line
├─ .blog-card-wrapper
├─ .blog-card-wrapper
└─ .blog-card-wrapper
Required class names
These class names are required for the script to work properly:
Main wrappers
.blog-card-wrapper— each blog card item.blog-card-wrapper.active— default active card on page load
Card content
.home-blog-description-block— hidden/revealed description area.home-read-more-button-block— hidden/revealed CTA area.home-blog-title— animated title.blog-thumbnile— blog image that receives filter transition.button-hover-line— animated line under the link button
Section intro animation
.section-subtitle.section-title.secondary-button
How it works
Default state
When the page loads on desktop:
- the card with
.activeclass becomes expanded - all other cards stay collapsed
- only the active card shows its description and read-more button
Hover interaction
When a user hovers over another card:
- previously active card shrinks
- new hovered card expands
- previous card content hides
- new card content becomes visible
- image filter updates for both cards
- title and hover line animate in
Resize behavior
When screen size goes below 992px:
- all width animations are removed
- hidden content is collapsed
- desktop interaction is disabled automatically
When resizing back to desktop:
- the section re-initializes again
Initial active card
To set the first expanded card on page load, add the class:
activeExample:
<div class="blog-card-wrapper active">Only one card should have the .active class initially.
GSAP requirements
Make sure GSAP is loaded in your Webflow project before this script runs.
If your project uses only core GSAP features from this snippet, standard GSAP is enough.
Since this blog card animation does not use Draggable, GSAP Draggable is not required for this section.
Recommended CSS setup
For smoother animation, make sure these elements can animate cleanly in your CSS/Webflow styles:
Blog description and CTA
Set these elements so hidden content does not overflow:
.home-blog-description-block.home-read-more-button-block
Recommended behavior:
overflow: hidden- default hidden state supported by
max-heightandopacity
Card layout
For best results:
- parent
.home-blog-flex-boxshould use a horizontal layout .blog-card-wrappershould sit side-by-side- widths should not be hard-locked in a conflicting way on desktop
Customization options
You can easily change the animation behavior by editing these values in the script:
Card widths
var ACTIVE_WIDTH = "40%";
var INACTIVE_WIDTH = "20%";Animation speed
var DURATION = 0.65;Easing
var EASE = "power3.inOut";Desktop breakpoint
var TABLET_BP = 992;If you want the effect to start only on larger desktop screens, increase the breakpoint.If you want to include tablet landscape, lower the breakpoint carefully.
Important notes
- This interaction is hover-based, so it is intended for desktop devices
- Do not remove the required class names unless you also update the script
- Only one card should be marked as
.activeon initial load - If no card has
.active, the section may not show the intended default expanded state - This script is written as a class-based setup only and does not require data attributes
Troubleshooting
Animation not working
Check the following:
- GSAP is loaded correctly
- script is placed before
</body> - class names match exactly
- at least one
.blog-card-wrapperexists - one card has the
.activeclass initially
Cards are not expanding
Make sure:
- screen width is
992pxor above - no conflicting Webflow interaction or CSS width rule is overriding GSAP
Description or button is always visible
Make sure:
.home-blog-description-blockand.home-read-more-button-blockare not forced open by CSS- overflow is hidden where needed
Mobile layout looks broken
This script already disables desktop interaction on smaller screens, but your mobile/tablet layout should still be designed separately inside Webflow.
Summary
This expandable blog card animation is designed to create a more interactive blog showcase on desktop screens. It highlights one card at a time, reveals more content on hover, and keeps the mobile/tablet experience clean by disabling the effect below 992px.
How to Update Variables
- Open your project in Designer.
- Access the Variables panel:
- Select the Variables icon in the Style panel (the small circle with a dot).
- Or press Shift + Cmd/Ctrl + M to open the Style Manager and switch to the Variables tab.
- Browse your variable groups such as color and typography. You’ll find items like
--Primary Coloror--Heading Font Size. - Select any variable to change its value.
Once updated, the change applies instantly across all elements that use that variable. - Optional:
You can still adjust colors or typography on individual elements directly from the Style panel if you prefer not to rely on variables.
How to Update Text and Images
Updating Text
- Select the text element on the canvas.
Click once to highlight the text field. - Enter edit mode.
Click again or press Enter to start editing. - Type your new content.
You can add, remove, or rewrite anything directly in place. - Apply styling if needed.
Use the Style panel to adjust font, size, color, spacing, or any typography settings for that element.
Updating Images
- Click the image you want to replace.
This selects the image element on the canvas. - Open the Settings panel.
Use the gear icon in the right sidebar to access image options. - Choose “Replace Image.”
This opens your asset selection window. - Upload a new file from your computer or select an existing image from the Assets panel.
- The new image updates instantly across the canvas.
If that image is used in multiple places (via Components or Symbols), those instances update as well.
How to Replace an Icon (SVG)
- Select the icon element on the canvas.
This highlights the Embed block that contains your current SVG code. - Open the Embed editor.
Click Edit Embed in the small toolbar that appears above the element or in the Settings panel. - Remove the existing SVG markup.
You’ll see code that looks like<svg>…</svg>. Select and delete it. - Paste your new SVG code.
Insert the full markup for your new icon, starting with<svg>and ending with</svg>. - Save your changes.
Click Save & Close to update the element. - Your icon refreshes immediately on the canvas, and if it's used inside a Component, all linked instances update automatically.
How to Adjust Animations
- Select the element that contains the animation.
Click it on the canvas to highlight the interaction connected to it. - Open the Interactions panel using the ⚡ icon in the right sidebar.
This panel shows all triggers and actions applied to the selected element. - Choose the interaction you want to edit.
You can adjust scroll, click, hover, load, or other triggers linked to that element. - Update the animation settings.
Change easing, timing, movement values, opacity, scale, or any other properties included in the action list. - Preview the result.
Use the eye icon in the panel to test how the interaction behaves before publishing.
How to Update Page SEO
- Open the Pages panel from the left sidebar.
- Find the page you want to edit and click the gear icon beside its name to open Page Settings.
- Scroll to the SEO section and update the following fields:
- SEO Title
- Meta Description
- Open Graph Image
- Save your changes. These details improve visibility on search engines and control how your page appears when shared.
- Open Page Settings or Custom Code from site setting → Before </body> tag or footer code.
- Paste the code bellow:
<script>
// counter animation
window.Webflow = window.Webflow || [];
window.Webflow.push(function () {
const counters = document.querySelectorAll(".counter-animate");
function runCounter(el) {
let raw = el.textContent.trim();
let numeric = raw.replace(/[^0-9.,-]/g, "").replace(/,/g, "");
let target = parseFloat(numeric);
if (isNaN(target)) return;
let decimals = (numeric.split(".")[1] || "").length;
let duration = parseFloat(el.getAttribute("data-duration")) || 2;
let obj = { value: 0 };
el.textContent = (0).toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
gsap.to(obj, {
value: target,
duration: duration,
ease: "power1.out",
onUpdate: () => {
el.textContent = obj.value
.toFixed(decimals)
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
});
}
// Intersection Observer for all counters in the viewport
let observer = new IntersectionObserver(entries => {
// Collect all visible counters
let visibleCounters = [];
entries.forEach(entry => {
if (entry.isIntersecting) visibleCounters.push(entry.target);
});
if (visibleCounters.length) {
// Animate counters **staggered**
visibleCounters.forEach((el, i) => {
setTimeout(() => runCounter(el), i * 300); // 300ms stagger
observer.unobserve(el); // Only run once
});
}
}, { threshold: 0.2 });
counters.forEach(el => observer.observe(el));
});
</script>- Select any heading or text block.
- Wrap only the number portion of the text in a
<span>. - Add the class name "Counter Animate" to that span.
Use the data-duration attribute to adjust the counter animation duration.
