Actually Using the General Sibling Combinator
Tags: css
Published: 2020-07-31 15:49:44

---
title: Actually Using the General Sibling Combinator
published: true
date: 2020-07-30 23:43:30 UTC
tags: css
canonical_url: https://www.chrisdeluca.me/article/actually-using-the-general-sibling-combinator/
---

_This post originally appeared on [Chris DeLuca's blog](https://www.chrisdeluca.me/article/actually-using-the-general-sibling-combinator/)_

I’ve vaguely known about CSS’s [general sibling combinator <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" viewbox="0 -256 1850 1850"><path d="M1438.373 818.95v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z" fill="currentcolor"></path></svg>](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator)for a while, but I have never found a practical use case for it, until now. And let me tell you, the results are, get ready for it, underwhelming.

First, a quick review of what the general sibling combinator is. Or, [skip ahead to the next section](#using-the-general-sibling-combinator)if this is old hat for you.

## What is the general sibling combinator? [¶](#what-is-the-general-sibling-combinator)

The general sibling combinator—invoked with `~` between two selectors—allows you to select every sibling element following the first selectorthat matches the second selector.

The general sibling combinator differs from the more common [adjacent sibling combinator <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" viewbox="0 -256 1850 1850"><path d="M1438.373 818.95v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z" fill="currentcolor"></path></svg>](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator)—invoked with `+` between two selectors—in that it selects every following element despite interceding elements, while the adjacent sibling combinator only matches the first sibling after the first selector that also matches the second.

To better illustrate the general sibling combinator, this CSS:

```css
h2 ~ p { /* styles */ }
```

Will match the following elements in this markup.

```html
<p>Eyebrow</p>
<h2>My title</h2>
<p>First paragraph</p><!-- matches -->
<img src="/my/image.jpg" alt="My image">
<p>Second paragraph</p><!-- matches -->
<blockquote>Give me quotes, or give me death.</blockquote>
<p>Third paragraph</p><!-- matches -->
```

While the same code using adjacent sibling combinator:

```css
h2 + p { /* styles */ }
```

Will only match `<p>First paragraph</p>`.

## Using the general sibling combinator [¶](#using-the-general-sibling-combinator)

The behavior of this combinator always felt too greedy to be helpful, or too easily replaced by simply targeting a group of classes, and that’s still the case most of the time; this is a rare pull in the tool belt.

However, I was doing work for the [astoria.digital <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" viewbox="0 -256 1850 1850"><path d="M1438.373 818.95v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z" fill="currentcolor"></path></svg>](https://astoria.digital)website, a hacker group I volunteer with, and found it useful when working with [CSS Grid <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" viewbox="0 -256 1850 1850"><path d="M1438.373 818.95v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z" fill="currentcolor"></path></svg>](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout).

![Astoria Digital design](https://www.chrisdeluca.me/article/actually-using-the-general-sibling-combinator/images/design.png) <figcaption>
<p>Astoria Digital design, with me egregiously highlighting the grid lines in red.</p>
</figcaption>

The design called for a border along the grid content columns. If I were using [Flex <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" viewbox="0 -256 1850 1850"><path d="M1438.373 818.95v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z" fill="currentcolor"></path></svg>](https://developer.mozilla.org/en-US/docs/Web/CSS/flex), I could nest each column inside a wrapper and add the border to that.

```html
<!-- …header markup… -->
<div class="wrapper">
  <div class="column">
    <h2 class="title"></h2>
    <p class="item"></p>
    <p class="item"></p>
    <p class="item"></p>
  </div>
  <div class="column">
    <h2 class="title"></h2>
    <p class="item"></p>
    <p class="item"></p>
    <p class="item"></p>
  </div>
  <div class="column">
    <h2 class="title"></h2>
    <p class="item"></p>
    <p class="item"></p>
    <p class="item"></p>
  </div>
</div>
<!-- …footer markup… -->
```

```css
.column {
  border-right: 3px solid #000;
}
```

This way, I could set `display: flex` on `.wrapper`, and each `.column` becomes a flex item with the proper border.

Similarly with grid, only direct children of `.wrapper` become grid items, aka items that can be laid out using grid. And since I wanted the header, content columns and footer to all align, they all needed to be grid items. My code changed to this.

```html
<div class="wrapper">
  <!-- …header markup… -->
  <h2 class="title"></h2>
  <p class="item"></p>
  <p class="item"></p>
  <p class="item"></p>
  <h2 class="title"></h2>
  <p class="item"></p>
  <p class="item"></p>
  <p class="item"></p>
  <h2 class="title"></h2>
  <p class="item"></p>
  <p class="item"></p>
  <p class="item"></p>
  <!-- …footer markup… -->
</div>
```

Now my grid worked, but I could no longer target `.column` for the border, since it’s real hard to target an element that doesn’t exist(believe me, I’ve tried).

I could simply target each class in question…

```css
.title,
.item {
  border-right: 3px solid #000;
}
```

And that works! The only problem is that there’s a border surrounding `.wrapper`, and we don’t want the last column’s border to “double up”with it.

I could have re-factored my styles so that each grid cell abutting an edge provides the border the single `.wrapper` border does, but that requires more effort to maintain, and was looking like a lot of effort to do in the first place, given that the actual code is more complicated than the simplified examples I’ve contrived for this post.

So what about adding classes? A classic technique, just slap a `.last` class on every element you don’t want the final border, then zero it outwith `border-right: none`.

And yes, that works, but I’ve never liked utility classes like that,since they need to be applied to each element they operate on, which makes code more cumbersome to maintain.

Instead, I often like to use one of the pseudo-classes like `:last-child` or `:last-of-type`, or the [lobotomized owl selector <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" viewbox="0 -256 1850 1850"><path d="M1438.373 818.95v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z" fill="currentcolor"></path></svg>](https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/), which uses our more popular friend, the adjacent sibling combinator.

Yet the lack of nesting was really crimping my options. I could have made it work with the above methods, but what I really wanted to do was to cancel the border for the _last column_, not some random selector.

Yes, it is the moment you’ve all been waiting for. I used… _The General Sibling Combinator!_

It’s about time this sucker showed up, because this post is almost over.

Why does it work? Since each column starts with a heading, and is an otherwise unique element (`h2`), we can target the final one with `:last-of-type`. That just targets the title, however, so we add the general sibling combinator to target all the following items.

Putting it all together, the code looks like this:

```css
.title:last-of-type,
.title:last-of-type ~ .item {
  border-right: none;
}
```

And only targets this:

```html
<div class="wrapper">
  <!-- …header markup… -->
  <h2 class="title"></h2>
  <p class="item"></p>
  <p class="item"></p>
  <p class="item"></p>
  <h2 class="title"></h2>
  <p class="item"></p>
  <p class="item"></p>
  <p class="item"></p>
  <h2 class="title"></h2><!-- targets this -->
  <p class="item"></p><!-- targets this -->
  <p class="item"></p><!-- targets this -->
  <p class="item"></p><!-- targets this -->
  <!-- …footer markup… -->
</div>
```

And there you have it. One long-winded explanation for a very specific, opinionated use. However, if you’ve read this far, first off, **Cheese Frogs**; that will be our secret code to identify each other.

Second, I do think it’s worth having this one in my tool box, as I have a feeling it’ll come more in handy as I delve deeper into grid.

Thanks for reading.