:matches()

The :matches pseudo-class is described as a functional pseudo-class by the official CSS Selectors Level 4 specifications. It doesn't serve any purpose in itself except making some complex selectors lighter by allowing them to be grouped. In a way, we can think as :matches as syntactic sugar.

Basically it keeps you away from repeating a compound selector when there is only one item that varies. I believe it is not only faster to write but also faster to parse for browsers but I have no solid information about this so as far as I know this pseudo-class does nothing more than helping writing selectors.

Syntax

:matches( selector[, selector]* )

:matches() accepts a list of valid selectors as argument. Like:

:matches(section, article, aside, nav) h1 {
  color: #BADA55;
}

/* Same thing as this... */
section h1,
article h1, 
aside h1,
nav h1 {
  color: #BADA55;
}

It makes some complex selectors a lot easier to write, for example:

:matches(section, article, aside, nav) :matches(h1, h2, h3, h4, h5, h6) {
  color: #BADA55;
}

/* ... which would be the equivalent of: */
section h1, section h2, section h3, section h4, section h5, section h6, 
article h1, article h2, article h3, article h4, article h5, article h6, 
aside h1, aside h2, aside h3, aside h4, aside h5, aside h6, 
nav h1, nav h2, nav h3, nav h4, nav h5, nav h6 {
  color: #BADA55;
}

And less repetitive:

.links:matches(:hover, :focus, :active) {
  color: #BADA55;
}

/* Same as */
.links:hover, .links:focus, .links:active {
  color: #BADA55;
}

Note that :matches() can't be nested and does not work with :not(). None of the following selectors will work:

/* Doesn't work */
:matches(:not(...))

/* Doesn't work */
:not(:matches(...))

/* Doesn't work */
:matches(:matches(...))

Nerd Alert

The specifications state that combinators (e.g. ~, >...) are not allowed in the passed selector in fast profile, but will be in complex profile. To put it simple, fast profile will be the first (and possible last) implementation of the specifications, while complex profile relates an hypothetical perfect future where performance doesn't matter much.

Update June 2015: Not sure how accurate the above paragraph is anymore. The spec we linked to has changed and that part is gone. So we removed the link.

Mimicking behaviour with Sass

It is possible to simulate a similar behaviour with Sass (or any CSS preprocessor for that matter):

// section h1, article h1, aside h1, nav h1
section, article, aside, nav {
  h1 {
    color: #BADA55;
  }
}

This creates the equivalent selector of :matches() by exploiting selector nesting. You could even create some sort of function to automate this at a higher level, but that's out of the scope here.

Other resources

Browser support

Chrome Safari Firefox Opera IE Android iOS
12+ (-webkit-) 5+ (-webkit-) 4+ (-moz-) 15+ (-webkit-) Nope ? 5+ (-webkit-)

This support chart reflets the old syntax, :any, with their respective prefix. It works the same way, so this article is just named for the final specced version.

Note: since CSS rejects the entire selector when there is a part it doesn't understand, you have to separate them to make it work everywhere. For instance:

/* This won't work in any browser because
 * Webkit browsers do not know `moz` and
 * Gecko browsers do not know `webkit`
 */
:-webkit-any(a, b) c, 
:-moz-any(a, b) c {
  color: #BADA55;
}

/* This however will work */
:-webkit-any(a, b) c {
  color: #BADA55;
}

:-moz-any(a, b) c {
  color: #BADA55;
}

Comments

  1. Jason Nickel
    Permalink to comment#

    Wouldn’t the Sass-equivalent simply be this?

    section, article, aside, nav {
      h1 {
        color: red;
      }
    }
    
  2. Louis Lazaris

    The “fast profile” section in the spec, linked to in the “Nerd Alert” section, doesn’t exist anymore. Is this found somewhere else in the spec now? I hate when the spec does that because you never know what happened to it unless you fish back through the different versions of the draft.

  3. Dominic Mayers
    Permalink to comment#

    Will it support the empty selector, say we want to have the equivalent of

    div 
    div a,
    div p {
    }
    

    could we use the following?

    div :matches(,a,p) {
    }
    
    • Chris Coyier
      Permalink to comment#

      Just a guess, but I would guess no. You’d probably comma separate like:

      div,
      div :matches(a, p) {
      } 
      
  4. Kai Middleton
    Permalink to comment#

    Could :matches() be used to refactor something like this:

    li.a.x .bullet { background-color: #purple; }
    li.a.y .bullet { background-color: #purple; }
    
    • Chris Coyier
      Permalink to comment#

      Kiiiiinda yeah. It would only save you from duplicating .bullet though:

      :matches(li.a.x, li.a.y) .bullet {
        background-color: purple;
      }
      

      And that’s not even supported in anything yet. So really you’d have to do:

      :-webkit-any(li.a.x, li.a.y) .bullet {
        background-color: purple;
      }
      :-moz-any(li.a.x, li.a.y) .bullet {
        background-color: purple;
      }
      :matches(li.a.x, li.a.y) .bullet {
        background-color: purple;
      }
      

      And then still not get IE/Edge support. Probably best to leave it be for now.

      Test case.

  5. Nao
    Permalink to comment#

    “Probably best to leave it be for now.”

    –> Definitely. Unless you’re using a preprocessor, very much ironically since :matches was introduced to make preprocessors redundant. I don’t know about other preprocessors, but the one I wrote years ago for my forum software (at wedge.org) will cache CSS files depending on the current browser, and thus supported browsers can use the appropriate :any or :matches selector without any repetition occurring.

    Regarding :matches in itself: I came here trying to find information on whether I could move everything to use :matches… Turns out only Safari 9+ is supporting it. Is it me, or has laziness taken over engine writers..? If yes, why is Firefox doing the same? Why is it that Edge doesn’t even begin to support :any/:matches? Isn’t it supposed to be the self-proclaimed new standards champ?

    When I stopped actively following the evolution of CSS/HTML, a couple years ago, everything was going so fast. Now I’m back into it, and I feel like not much has changed, except for Chrome now having IE6-level market shares. Oh, wait… Maybe there’s a connection to be made here. I think there’s an expression for that… Resting on your laurels.

Leave a Comment

Posting Code

We highly encourage you to post problematic HTML/CSS/JavaScript over on CodePen and include the link in your post. It's much easier to see, understand, and help with when you do that.

Markdown is supported, so you can write inline code like `<div>this</div>` or multiline blocks of code in triple backtick fences like this:

```
<script>
  function example() {
    element.innerHTML = "<div>code</div>";
  }
</script>
```

We have a pretty good* newsletter.