New Sep 16, 2024

Selecting Previous Siblings

More Front-end Bloggers All from Frontend Masters Boost RSS Feed View Selecting Previous Siblings on frontendmasters.com

I saw a video going around recently with a title that makes me feel very old: “Crazy CSS Using By Master CSS”. The voice in it is also weird/fake/AI or something? Part of me finds it annoying and part of me finds it cool as I have a feeling English probably isn’t the creators native language and by making it English through the power of technology the video probably gets a broader audience. I guess it’s proof that if you make something super cool, the title doesn’t matter.

Look at me — I’ve digressed before I even started.

The demo they build in the video is fantastically cool. It’s a row of cards with a hover effect. The card being hovered has an effect where is appears to move toward you in 3D. Not only that, the two cards after the hovered card have their own unique effects, enhancing the 3D. But the ultimate touch is that the two cards before the hovered card also do, which has long been in “impossible” territory in CSS. No more!

Screengrab of the awesome hover effect from the video.

Let’s cover that selector approach in CSS.

The element being hovered. Simple.

.card:hover {

}

Now the next element after it:

.card:hover + .card {

}

To be a bit more flexible and write a bit less code, we could write that like:

.card {
  &:hover {

    /* next card */
    & + * {

    }
  }
}

And we can keep going to the 2nd card after the hovered card:

.card {
  &:hover {

    /* next card */
    & + * {

    }
    /* next card after that */
    & + * + * {

    }
  }
}

So now we have selected the hovered card itself and essentially the next two to the right. What about those cards on the left? The “previous siblings”, as it were?

Consider what we’ve already done. We’ve proven we can select the next card after a selector. So what if we could say “if the next card is hovered, select me?”. That’s what we can do with :has() now in CSS.

.card {
  &:hover {

    /* Select previous sibling! */
    :has(+ &) {

    }
  }
}
  

If you find the nesting confusing, it’s just like this

:has(+ .card:hover) {

}

Which says “any element where the next element is a hovered card.”

I’ll go one level deeper there, and combine it all:

.card {
  &:hover {

    /* next card */
    & + * {

    }
    /* next card after that */
    & + * + * {

    }

    /* previous card */
    :has(+ &) {

    }
    /* previous card before that */
    :has(+ * + &) {

    }
  }
}

The selector part is the hard part here. Now that we’ve got that going, the styling could be anything. Keeping it a smidge simple at first, I’ll apply scale(1.3) to the hovered element, scale(1.2) to the next & previous sibling, then scale(1.1) to the next & previous siblings after that. Plus a smidge of brightness filtering and we get:

(Just to prove how cool and smart I am, here’s me playing with this very idea two years ago.)

The video used some even cooler CSS to apply to the sibling cards, beginning with some 3D transform setup. The big idea was to use translateZ() rather than scale() to make the cards bigger (e.g. bring them “closer to the viewer”) which has a cool side effect of having an origin point that the cards appear to move away from. Coupled with a little rotateY() on the siblings, the effect makes what looks like a mound of cards that follows the cursor. Bravo!

I didn’t see a live demo attached to the video in any way, so here’s my own re-creation (view big):

And a video in case you’re not on a device with a fine pointer.

 

Scroll to top