0

Sibling Scopes in CSS, thanks to :has()

 1 year ago
source link: https://www.bram.us/2023/01/12/sibling-scopes-in-css-thanks-to-has/?ref=sidebar
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
sibling-scoped-styles-bramus-560x331.png

Leverage CSS :has() to select all siblings between two element boundaries.

# Creating a Sibling Scope

Say you have markup like this:

<ul>
  <li>outside</li>
  <li class="upper">upper</li>
  <li>inside</li>
  <li>inside</li>
  <li>inside</li>
  <li>inside</li>
  <li class="lower">lower</li>
  <li>outside</li>
</ul>

If you want to select all elements between that .upper and .lower element, you can do so using this selector powered by the almighty :has() selector:

.upper ~ :has(~ .lower) {
	outline: 1px solid red;
}

It works as follows:

  • .upper ~ * will select all elements that are preceded by .upper.
  • :has(~ .lower) will select all elements that are followed by a .lower.
  • By combining both, you can clamp the selection and create a scope between the .upper and .lower siblings.

# Technical Demo

# Limitations

As shown in the technical demo, the .upper ~ :has(~ .lower) is greedy. If you have two adjacent sets of boundaries that are also siblings from each other, the selector will select everything in between the first .upper up to the last .lower.

<ul>
  <li>outside</li>
  <li class="upper">upper</li>
  <li>inside</li>
  <li>inside</li>
  <li class="lower">lower</li>
  <li class="upper">upper</li>
  <li>inside</li>
  <li>inside</li>
  <li class="lower">lower</li>
  <li>outside</li>
</ul>

Depending on the use-case, this might or might not be considered a limitation.

# Browser Support

These selectors are supported by all browsers that have :has() support. At the time of writing this does not include Firefox.

👨‍🔬 Flipping on the experimental :has() support in Firefox doesn’t do the trick either. Its implementation is still experimental as it doesn’t support all types of selection yet. Relative Selector Parsing (i.e. a:has(~ b)) is one of those features that’s not supported yet – Tracking bug: #1774588

UPDATE: As reader Paweł Grzybek points out, you can use the following selector in browsers that don’t support :has()

.upper ~ :not(.lower):not(.lower ~ *) {
    outline: 1px solid red;
}

The selector doesn’t play nice with the last example of the technical demo – it only targets the first group – but depending on the use-case that might be acceptable.

# Practical Application

My colleague Jhey built a date picker that highlights the days in between your preferred start and end day.

Because they need to select elements across <tr> elements, the code is pretty wild and a bit more difficult to grasp. Basically it targets all cells between the one that has a :checked input and the cell your are currently hovering.

🤔 This would be much easier to style if a list + CSS grid were used to build the calendar. But that probably has some accessibility implications so yeah, no, … It Depends™, right?

# Spread the word

To help spread the contents of this post, feel free to retweet its announcement tweet:

Sibling Scopes in CSS, thanks to :has()

🔗 https://t.co/SVTUAzvxgp

🏷 #css #selectors pic.twitter.com/qdtOuwgLzm

— Bram.us (@bramusblog) January 12, 2023


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK