How can SASS be used to emulate the use of :matches(a, b) + :matches(a, b):nth-child(2)?

Question

In answering the question "CSS/SCSS Adjacent sibling selector between certain types," in which the OP wanted to have the second-child of a parent-element styled if it, and the preceding sibling, was an element of various types.

I proposed an answer using the matches() selector (albeit both Firefox and Webkit browsers are – at the time of writing – using vendor-prefixed implementations of the :any() pseudo-class).

Somewhat as a tradition of times gone by both Internet Explorer and Edge fail to implement any version of the selector, either :any() or :matches(), although since it's an experimental feature I can't attach blame for that decision or 'failure.'

However, for compatibility's sake I'd like to ask: is there a means by which SASS might be used to form the appropriate selectors to reliably style the following:

a::before {
  content: 'link';
}
span::before {
  content: 'span';
}
b::before {
  content: 'b';
}
em::before {
  content: 'em';
}
:-webkit-any(a, b, span) + :-webkit-any(a, b, span):nth-child(2) {
  color: #f90;
}
:-moz-any(a, b, span) + :-moz-any(a, b, span):nth-child(2) {
  color: #f90;
}
:matches(a, b, span) + :matches(a, b, span):nth-child(2) {
  color: #f90;
}
<div>
  <h2>The second element child of each of these following &lt;div&gt; elements should be styled</h2>
  <div>
    <span></span>
    <a href="#"></a>
  </div>
  <div>
    <a href="#"></a>
    <span></span>
  </div>
  <div>
    <a href="#"></a>
    <span></span>
    <span></span>
  </div>
</div>
<div>
  <h2>The second element child of each of these following &lt;div&gt; elements should <em>not</em> be styled</h2>
  <div>
    <a href="#"></a>
    <em></em>
  </div>
  <div>
    <em></em>
    <a href="#"></a>
  </div>
</div>

With SASS I'd expect, or at least imagine, that the groupings should be expanded from the following form (though I'm unsure, hence this question, as to how to emulate the :matches() syntax or what the SASS syntax might be):

:matches(a, b, span) + :matches(a, b, span):nth-child(2) {
  color: #f90;
}

Into an explicit selector, such as:

a + a:nth-child(2),
a + b:nth-child(2),
a + span:nth-child(2),
b + a:nth-child(2),
b + b:nth-child(2),
b + span:nth-child(2),
span + a:nth-child(2),
span + b:nth-child(2),
span + span:nth-child(2) {
  color: #f90;
}

a::before {
  content: 'link';
}
span::before {
  content: 'span';
}
b::before {
  content: 'b';
}
em::before {
  content: 'em';
}
a + a:nth-child(2),
a + b:nth-child(2),
a + span:nth-child(2),
b + a:nth-child(2),
b + b:nth-child(2),
b + span:nth-child(2),
span + a:nth-child(2),
span + b:nth-child(2),
span + span:nth-child(2) {
  color: #f90;
}
<div>
  <h2>The second element child of each of these following &lt;div&gt; elements should be styled</h2>
  <div>
    <span></span>
    <a href="#"></a>
  </div>
  <div>
    <a href="#"></a>
    <span></span>
  </div>
  <div>
    <a href="#"></a>
    <span></span>
    <span></span>
  </div>
</div>
<div>
  <h2>The second element child of each of these following &lt;div&gt; elements should <em>not</em> be styled</h2>
  <div>
    <a href="#"></a>
    <em></em>
  </div>
  <div>
    <em></em>
    <a href="#"></a>
  </div>
</div>

References:


Show source
| css   | css-selectors   | sass   2017-01-03 15:01 2 Answers

Answers ( 2 )

  1. 2017-01-03 15:01

    This could be a solution, not as concise as using :matches, but near enough:

    a, b, span {
        & + a, & + b, & + span {
            &:nth-child(2) {
                color: #f90;
            }
       }
    }
    

    Another one would be to explicitly do the expanding loops:

    $tags: a, b, span;
    @each $a in $tags {
       @each $b in $tags {
            #{$a} + #{$b}:nth-child(2) {
                color: #f90;
            }
       }
    }
    

    (which could also be written using a mixin, but that wouldn't make it look much better...)

    But really at this point I would work upstream to ensure there's a class instead of those 3 tags.

  2. 2017-01-06 08:01

    You can use postcss with the level4 plugin. This will transform :matches to their equivalent CSS3 queries.

    li:matches(:last-child, .fancy) { ... }
    
    /* compiles to */
    
    li:last-child, li.fancy { ... }
    

    Documentation and examples (such as the above) can be found on the Github page of level4: https://github.com/stephenway/level4.

◀ Go back