Parent Selectors in CSS and Sass

Abstract image used to represent Parent Selectors in CSS and Sass
Image by Sergey Zolkin.

A longstanding gap in CSS has been selecting a parent because of the children it contains. That is a different concern from how we name and organise classes: BEM can keep selectors predictable and specificity easier to manage, but a large performance advantage over nested selectors needs measurement in the actual page.

There is potential in the Selectors Level 4 draft with the (potential) introduction of :has() pseudoclass, which would allow you to target elements that have specific children. For example:

a:has(> img) {
  /* This would match an anchor with a direct child image */
}

li:has(a.current) {
  /* This would match an li element where it contained an
   * anchor with the class name 'current' */
}

section:not(:has(h1, h2, h3, h4, h5, h6)) {
  /* This would match a section element where it does not
   * contain any heading (h1 - h6) */
}

This is very exciting, but at the time of writing, not a single browser supports it, so we have to be smart about our CSS, or simply use additional classNames to bridge the gap.

Fortunately, Sass does offer a partial solution in their own parent selector: &.

This special character essentially maps back to the immediate parent selector, allowing you to build up nested selectors like this:

// Sass:
.element {
  color: lime;

  &:hover {
    color: purple;

    img {
      border: 1rem solid yellow;
    }
  }

  & > a {
    outline: 0.1rem solid navy;
  }
}

// Generated CSS:
.element {
  color: lime;
}

.element:hover {
  color: purple;
}

.element:hover img {
  border: 1rem solid yellow;
}

.element > a {
  outline: 0.1rem solid navy;
}

In this way you can still target elements based on their parent, by generating accurate nested selectors. It's not ideal, but it is very handy!

The Sass parent selector is also extremely useful for building up BEMtype selectors:

// Sass:
.block {
  color: lime;

  &__element {
    color: purple;

    &--modifier {
      color: navy;
    }
  }
}

// Generated CSS:
.block {
  color: lime;
}

.block__element {
  color: purple;
}

.block__element--modifier {
  color: navy;
}

Of course, the more recent significant adoption of CSSinsideJavaScript, combined with JSXtype syntax that allows you to change styles on the fly may well eventually make this obsolete without ever needing the 4th level of CSS selectors...

Postscript

December 2023: :has() became available across current major browsers. That changes the support picture described in the original September 2020 article. Sass & is still a compiletime authoring tool: it expands the current nesting context, whereas :has() lets the browser match an element based on related elements. For a modern support matrix, :has() can be useful for form states and component variants; keep the selectors readable and check the browsers the project needs to support.

Want to find out more?

If you need senior handson support with a complex React or Next.js platform, migration, performance issue, or technical SEO problem, send me the context and I'll tell you where I can help.