DEV Community

Cover image for ↔️ Sideway selection in CSS with :has()
Francesco Vetere
Francesco Vetere

Posted on

↔️ Sideway selection in CSS with :has()

Hi folks! 👋 Today I would like to share with you this codepen I created in order to showcase a simple but really cool use for the recently introduced :has() selector.

If you hover with your mouse over any of the emojis, you'll notice that not only the hovered emoji smoothly pops up, but its previous and next siblings also get affected a little bit, creating a very pleasant effect.

This cool effect is only possible thanks to the :has() selector, recently introduced in the CSS world and now available for all the major browsers.

➡️ :has() is a functional pseudo-class that allows us to style an element based on its descendants or any succeeding elements.

  • Basically, :has() allows to style the element it is attached to — otherwise known as the target element. This is similar to other pseudo-classes like :hover, where a:hover is intended to style the <a> element in an hovered state.

  • However, :has() is also similar to :is(), :where(), and :not(), in that it accepts a a list of relative selectors within its parentheses. This allows :has() to create complex criteria to test against, making it a very powerful selector.

For example, if I wanted to select all the <article> elements that contain an <img> as child, :has() would make this quite a simple task:

article:has(img) {
  /* ... */
}
Enter fullscreen mode Exit fullscreen mode

This use of :has() is certainly one of the most common: it basically acts as a parent selector, something that really was missing in the web platform and was highly demanded for years by developers.

BUT, because :has() actually accepts a relative selector list as its argument, you can select so much more than just the parent element! Using various CSS combinators, it is now possible to not only go up ⬆️ the DOM tree, but also do sideway selections! ↔️

For example, article:has(+ article) will select any <article> element that has another <article> as a direct sibling. This apparently simple selection would just not be possible without :has(), and it would have required some extra JavaScript.

💡 The codepen I realized takes advantage of this very idea. I basically want to apply some styling (a scaling and a translation) for the currently hovered emoji, but also for its previous and next sibling.

➡️ :has() makes this really easy:

.dock li:hover {
  /* scale and translate the hovered emoji */
}

.dock li:hover + li {
  /* scale and translate the next emoji */
}

.dock li:has(+ li:hover) {
  /* scale and translate the previous emoji */
}
Enter fullscreen mode Exit fullscreen mode

Of course the full code is a little bit more complex than that (you can check out the codepen if you're curious), but the main idea really is based on this simple snippet. Having a way to select not only an element and its next sibling, but also its previous one, opens up a whole new world of possibilities.


And that's it! Feel free to leave a comment and let me know what other cool stuff you built using :has() 😉

Till next time! 👋

Top comments (6)

Collapse
 
morganney profile image
Morgan Ney

This has me excited.

Collapse
 
francescovetere profile image
Francesco Vetere

😂😂😂

Collapse
 
ben profile image
Ben Halpern

This is really helpful, thanks!

Collapse
 
francescovetere profile image
Francesco Vetere

Glad it helped! :)

Collapse
 
abdulmuminyqn profile image
Abdulmumin yaqeen

I super duper love the :has() 🫶

@devcanvas_ have a super cool demo on something like this.

you can find it here

Collapse
 
francescovetere profile image
Francesco Vetere

Cool stuff! 😍