Day 91: a previous sibling selector with :has()

posted on

It’s time to get me up to speed with modern CSS. There’s so much new in CSS that I know too little about. To change that I’ve started #100DaysOfMoreOrLessModernCSS. Why more or less modern CSS? Because some topics will be about cutting-edge features, while other stuff has been around for quite a while already, but I just have little to no experience with it.


I’ve already shown much appreciation for the :has() pseudo-class in this series, but that we can use it as a previous sibling selector tops it all of.

Since this is not an official selector, but more something like a hack, it can be hard to read and interpret. So, let’s start nice and easy.

We have three buttons. If we hover/focus one button and we want to highlight it and the next adjacent button in the DOM at the same time, we can use the adjacent sibling selector.

  button {
    outline-width: 8px;
    outline-offset: 4px;
    outline-color: hotpink;
  }

  button:is(:hover, :focus-visible) {
    outline-style: solid;
  }

  button:is(:hover, :focus-visible) + button {
    outline-style: dashed;
  }

There's no previous item selector, but using :has() we can select an item that comes before another item. I've written about next-sibling combinators and :has() on day 26. Here’s an example from that post: The following code sets the block-end margin of all <h2> to 0 if they're followed by a <time> element.

h2 {
  margin-block-end: 0.7em; 
}

h2:has(+ time) {
  margin-block-end: 0;
}
<h2>Heading</h2>
<p>Teaser text</p>

<h2>Heading</h2>
<time>31.10.2022</time>
<p>Teaser text</p>

Heading

Teaser text

Heading

Teaser text

If we want to use this in our button example, we have to select a <button> followed by a <button> in the :hover or :focus-visible state.

  button:is(:hover, :focus-visible) {
    outline-style: solid;
  }

  button:is(:hover, :focus-visible) + button {
    outline-style: dashed;
  }

  button:has(+ button:is(:hover, :focus-visible)) {
    outline-style: dotted;
  }

If we have more buttons and we want to select the button that comes before the previous button, we can extend our selector.

  button:has(+ button + button:is(:hover, :focus-visible)) {
    /* styles */
  }

What a beauty!

Here are a couple of other demos:

See the Pen Day 91: previous sibling selector with :has() #100DaysOfMoreOrLessModernCSS by Manuel Matuzovic (@matuzo) on CodePen.

See the Pen selecting previous item using CSS by pourya (@pouriversal) on CodePen.

See the Pen BUBBLE (previous siblings!) by Chris Coyier (@chriscoyier) on CodePen.

See on CodePen

Further reading

Overview: 100 Days Of More Or Less Modern CSS