By the end of this lesson you’ll have built a hover interaction that looks like it needs a whole Interactions timeline — hover a card and its border darkens, its image scales up while staying perfectly clipped, and every other card fades toward black and white — using no Webflow Interactions at all. Just the style panel, hover states, a few variables, and a tiny bit of CSS.
That’s really the point of this one. It’s a cool effect, sure, but it’s also a small lesson in how a few lines of modern CSS can make your Webflow projects feel more powerful, flexible, and intentional than dragging out an interaction for everything.
The starting point is an ordinary CMS grid of blog cards. Two structural details matter up front: the card link has overflow: hidden with a border-radius (so the scaled image stays clipped), and the pieces that will animate — border color, image transform, and filter — already have transitions on them (800ms, ease-out expo) so everything moves consistently.
How it works
The border is the easy part and the one thing the native hover state can do. Select the card link, switch to its hover state, and set the border color to a value from your variables group. Done — it animates on hover using the transition you already defined.
The catch shows up with the image: Webflow’s hover state only styles the element you selected — it can’t reach a child. So to scale the image inside the hovered card, you need a little custom CSS. The clean way to hook into it is a custom attribute, not a class. The card link gets an attribute like card-link-hover, which detaches the behavior from the class name — any element with that attribute gets the interaction, even if its class is different. That turns the interaction into a reusable behavior instead of a one-off style.
With the attribute in place, one rule scales the image:
[card-link-hover]:hover img {
transform: scale(var(--card-on-hover--image-scale));
}
In plain English: when an element with card-link-hover is hovered, scale the img inside it. The value comes from a Webflow variable — and you don’t translate the name by hand, you use Copy CSS on the variable and paste it straight into scale().
The most satisfying piece — desaturating every card except the hovered one — is also just three lines, thanks to the modern :has() selector:
body:has([card-link-hover]:hover) [card-link-hover]:not(:hover) {
filter: saturate(var(--card-on-hover--saturation));
}
Read it in parts: body:has([card-link-hover]:hover) means “only when the page contains a hovered card,” and [card-link-hover]:not(:hover) means “target every card except the one being hovered.” Together: when one card is hovered, desaturate all the others. Instead of building an interaction system, you’re describing the relationship directly — if this exists and it’s hovered, style all the others. That’s what makes modern CSS so expressive here.
How to use it
- Prep the structure — give the card link
overflow: hiddenplus a border-radius so the image stays clipped, and add transitions onborder-color,filter, and the image’stransform(e.g.800ms, ease-out expo). - Store your values as variables — a small group (here “card on hover”) holding the sibling saturation, the image scale factor, and the hover border color.
- Animate the border — select the card link, switch to its hover state, and set the border color to your variable. That one’s fully native.
- Add the attribute — give the card link a custom attribute like
card-link-hoverso the interaction targets a behavior, not a class. - Add an embed for CSS — drop an Embed element (class
global-styles) near the top of the page and open it to add a<style>block. - Scale the image — paste the
[card-link-hover]:hover img { transform: scale(...) }rule, using Copy CSS to grab the exact variable name. - Desaturate the siblings — add the
body:has(...)rule withfilter: saturate(...), again pasting the variable via Copy CSS. Save, and test right in the Designer.
That’s a clean, reusable card interaction with almost no code — and a good first taste of how far a few lines of CSS can take your Webflow builds.