7

To select an element and all its descendant elements:

.media, .media * {color: #f00;}

Is there just one selector I can use instead of two selectors separated by a comma? I'm looking for a more efficient way to type this.

j08691
  • 204,283
  • 31
  • 260
  • 272
chharvey
  • 8,580
  • 9
  • 56
  • 95
  • Is there a better solution today, in 2020? – Andrey Mishchenko Nov 25 '20 at 18:41
  • 1
    Basically, CSS is not intended to work this way. Applying same properties on every descendants is a bad practice. `.media {color: #f00}` is enough, as every descendant will "inherit" this property instead of "setting" it, unless an other rule apply (like color of a link, for instance). This is a part of what is called "cascading" stylesheet. – Charles-Édouard Coste Nov 26 '20 at 05:36
  • @Charles-ÉdouardCoste Yes, that would work for the properties that are inherited. However, not all CSS properties are inherited, such as `display` for example. – chharvey Nov 26 '20 at 23:49
  • 1
    Sure. But it would be weird to indiscriminately apply such property on *every* descendant instead of grouping by classes… By experience, when something seems missing in CSS, I realize later that I was not supposed to do what I wanted to do with it. :D – Charles-Édouard Coste Nov 27 '20 at 03:57
  • In 2023, recent developments in the CSS standard offer interesting alternatives. See my answer below. – mesr May 18 '23 at 20:07

2 Answers2

6

With XPath you have the descendant-or-self axis

But there is no such selector in CSS.

2

In 2023, recent additions to CSS syntax and available pseudo-classes bring some interesting alternative ways to tackle this (although still not as ideal as a truly native self-or-descendant combinator).

Let's consider a hypothetical .prose class that identifies a site's authored content, which can be delivered either in e.g. <article> or <section> blocks with nested structure, or in e.g. <p> or <blockquote> elements that directly contain text.

With only strict descendant combinators and traditional CSS syntax, elements such as <p> and <blockquote> require the addition of a (potentially un-semantic) wrapper, such as a <div> element.

Alternatively, the CSS rules can be written in a way that matches both self and descendants, but this involves repetitive (and error-prone) boilerplate:

h1.prose, .prose h1 {...}
h2.prose, .prose h2 {...}
h3.prose, .prose h3 {...}
h4.prose, .prose h4 {...}
h5.prose, .prose h5 {...}
h6.prose, .prose h6 {...}

p.prose, .prose p {...}

blockquote.prose, .prose blockquote {...}

/* And so forth... */

Leveraging recent CSS developments, the above can be re-written in the following way:

.prose, .prose * {
    
    &:is(h1) {...}
    &:is(h2) {...}
    &:is(h3) {...}
    &:is(h4) {...}
    &:is(h5) {...}
    &:is(h6) {...}
    
    &:is(p) {...}

    &:is(blockquote) {...}

    /* Alternatively, the :where() pseudo-class can also be used,
       which differs in how specificity is calculated. */
}

Obviously, the &:is(...) part is still boilerplate, but repetition has been cut down significantly, which can be considered an improvement.

Caveat: As of May 2023, browser support for native CSS nesting is still a work-in-progress (see https://caniuse.com/css-nesting) and CSS code that takes advantage of it should be converted to non-nested syntax (using, for example, the postcss-nesting plugin). The :is() pseudo-class enjoys more widespread support (see https://caniuse.com/?search=is).

mesr
  • 83
  • 5