:is() (:matches(), :any())

This is an experimental technology
Check the Browser compatibility table carefully before using this in production.

Note: :matches() was renamed to :is() in CSSWG issue #3258.

The :is() CSS pseudo-class function takes a selector list as its argument, and selects any element that can be selected by one of the selectors in that list. This is useful for writing large selectors in a more compact form.

Note that currently browsers support this functionality as :matches(), or through an older, prefixed pseudo-class — :any(), including older versions of Chrome, Firefox, and Safari. :any() works in exactly the same way as :matches()/:is(), except that it requires vendor prefixes and doesn't support complex selectors.

/* Selects any paragraph inside a header, main
   or footer element that is being hovered */
:is(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

/* The above is equivalent to the following */
header p:hover,
main p:hover,
footer p:hover {
  color: red;
  cursor: pointer;
}

/* Backwards-compatible version with :-*-any() and :matches()
   (It is not possible to group selectors into single rule,
   because presence of invalid selector would invalidate whole rule.) */
:-webkit-any(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}
:-moz-any(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}
:matches(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

Syntax

:is( <complex-selector-list> )

where
<complex-selector-list> = <complex-selector>#

where
<complex-selector> = <compound-selector> [ <combinator>? <compound-selector> ]*

where
<compound-selector> = [ <type-selector>? <subclass-selector>* [ <pseudo-element-selector> <pseudo-class-selector>* ]* ]!
<combinator> = '>' | '+' | '~' | [ '|' ]

where
<type-selector> = <wq-name> | <ns-prefix>? '*'
<subclass-selector> = <id-selector> | <class-selector> | <attribute-selector> | <pseudo-class-selector>
<pseudo-element-selector> = ':' <pseudo-class-selector>
<pseudo-class-selector> = ':' <ident-token> | ':' <function-token> <any-value> ')'

where
<wq-name> = <ns-prefix>? <ident-token>
<ns-prefix> = [ <ident-token> | '*' ]? |
<id-selector> = <hash-token>
<class-selector> = '.' <ident-token>
<attribute-selector> = '[' <wq-name> ']' | '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']'

where
<attr-matcher> = [ '~' | | | '^' | '$' | '*' ]? '='
<attr-modifier> = i | s

Examples

Cross-browser example

<header>
  <p>This is my header paragraph</p>
</header>

<main>
  <ul>
    <li><p>This is my first</p><p>list item</p></li>
    <li><p>This is my second</p><p>list item</p></li>
  </ul>
</main>

<footer>
  <p>This is my footer paragraph</p>
</footer>
:-webkit-any(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

:-moz-any(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

:matches(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

:is(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}
let matchedItems;

try {
  matchedItems = document.querySelectorAll(':is(header, main, footer) p');
} catch(e) {
  try {
    matchedItems = document.querySelectorAll(':matches(header, main, footer) p');
  } catch(e) {
    try {
      matchedItems = document.querySelectorAll(':-webkit-any(header, main, footer) p');
    } catch(e) {
      try {
        matchedItems = document.querySelectorAll(':-moz-any(header, main, footer) p');
      } catch(e) {
        console.log('Your browser doesn\'t support :is(), :matches(), or :any()');
      }
    }
  }
}

matchedItems.forEach(applyHandler);

function applyHandler(elem) {
  elem.addEventListener('click', function(e) {
    alert('This paragraph is inside a ' + e.target.parentNode.nodeName);
  });
}

Simplifying list selectors

The :is() pseudo-class can greatly simplify your CSS selectors. For example, the following CSS:

/* 3-deep (or more) unordered lists use a square */
ol ol ul,     ol ul ul,     ol menu ul,     ol dir ul,
ol ol menu,   ol ul menu,   ol menu menu,   ol dir menu,
ol ol dir,    ol ul dir,    ol menu dir,    ol dir dir,
ul ol ul,     ul ul ul,     ul menu ul,     ul dir ul,
ul ol menu,   ul ul menu,   ul menu menu,   ul dir menu,
ul ol dir,    ul ul dir,    ul menu dir,    ul dir dir,
menu ol ul,   menu ul ul,   menu menu ul,   menu dir ul,
menu ol menu, menu ul menu, menu menu menu, menu dir menu,
menu ol dir,  menu ul dir,  menu menu dir,  menu dir dir,
dir ol ul,    dir ul ul,    dir menu ul,    dir dir ul,
dir ol menu,  dir ul menu,  dir menu menu,  dir dir menu,
dir ol dir,   dir ul dir,   dir menu dir,   dir dir dir {
  list-style-type: square;
}

... can be replaced with:

/* 3-deep (or more) unordered lists use a square */
:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) ul,
:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) menu,
:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) dir {
  list-style-type: square;
}

Simplifying section selectors

The :is() pseudo-class is particularly useful when dealing with HTML5 sections and headings. Since <section>, <article>, <aside>, and <nav> are commonly nested together, without :is(), styling them to match one another can be tricky.

For example, without :is(), styling all the <h1> elements at different depths could be very complicated:

/* Level 0 */
h1 {
  font-size: 30px;
}
/* Level 1 */
section h1, article h1, aside h1, nav h1 {
  font-size: 25px;
}
/* Level 2 */
section section h1, section article h1, section aside h1, section nav h1,
article section h1, article article h1, article aside h1, article nav h1,
aside section h1, aside article h1, aside aside h1, aside nav h1,
nav section h1, nav article h1, nav aside h1, nav nav h1 {
  font-size: 20px;
}
/* Level 3 */
/* ... don't even think about it! */

Using :is(), though, it's much easier:

/* Level 0 */
h1 {
  font-size: 30px;
}
/* Level 1 */
:is(section, article, aside, nav) h1 {
  font-size: 25px;
}
/* Level 2 */
:is(section, article, aside, nav)
:is(section, article, aside, nav) h1 {
  font-size: 20px;
}
/* Level 3 */
:is(section, article, aside, nav)
:is(section, article, aside, nav)
:is(section, article, aside, nav) h1 {
  font-size: 15px;
}

Avoiding selector list invalidation

Unlike selector lists, the :is() pseudo-class doesn't get invalidated when one of the selectors passed to it isn't supported by the browser.

:is(:valid, :unsupported) {
  ...
}

Will still parse correctly and match :valid even in browsers which don't support :unsupported, whereas:

:valid, :unsupported {
  ...
}

Will be ignored in browsers which don't support :unsupported even if they support :valid.

Difference between :is() and :where()

The diffference between the two is that :is() counts towards the specificity of the overall selector (it takes the specificity of its most specific argument), whereas :where() has a specificity value of 0. This is demonstrated by the example on the :where() reference page.

Specifications

Specification Status Comment
Selectors Level 4
The definition of ':is()' in that specification.
Working Draft Initial definition

Browser compatibility

DesktopMobile
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung Internet
:is()Chrome Full support 68
Notes Disabled
Full support 68
Notes Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Disabled From version 68: this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled). To change preferences in Chrome, visit chrome://flags.
No support 66 — 71
Notes Alternate Name Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Alternate Name Uses the non-standard name: :matches()
Disabled From version 66 until version 71 (exclusive): this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled). To change preferences in Chrome, visit chrome://flags.
Full support 12
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
Edge Full support 79
Notes Disabled
Full support 79
Notes Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Disabled From version 79: this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled).
Full support 79
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
Firefox Full support 78
Full support 78
Full support 77
Notes Disabled
Notes Enabled by default in Firefox Nightly.
Disabled From version 77: this feature is behind the layout.css.is-where-selectors.enabled preference (needs to be set to enabled). To change preferences in Firefox, visit about:config.
Full support 4
Notes Alternate Name
Notes Doesn't support combinators.
Notes See bug 906353
Alternate Name Uses the non-standard name: :-moz-any()
IE No support NoOpera Full support 55
Notes Disabled
Full support 55
Notes Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Disabled From version 55: this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled).
No support 53 — 58
Notes Alternate Name Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Alternate Name Uses the non-standard name: :matches()
Disabled From version 53 until version 58 (exclusive): this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled).
Full support 15
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
Safari Full support 9
Alternate Name
Full support 9
Alternate Name
Alternate Name Uses the non-standard name: :matches()
Full support 5
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
WebView Android Full support ≤37
Notes Alternate Name
Full support ≤37
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
Chrome Android No support 66 — 71
Notes Alternate Name Disabled
No support 66 — 71
Notes Alternate Name Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Alternate Name Uses the non-standard name: :matches()
Disabled From version 66 until version 71 (exclusive): this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled). To change preferences in Chrome, visit chrome://flags.
Full support 18
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
Firefox Android Full support 4
Notes Alternate Name
Full support 4
Notes Alternate Name
Notes Doesn't support combinators.
Notes See bug 906353
Alternate Name Uses the non-standard name: :-moz-any()
Opera Android Full support 48
Notes Disabled
Full support 48
Notes Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Disabled From version 48: this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled).
No support 47 — 50
Notes Alternate Name Disabled
Notes Combinators in the selector list argument may not match correctly (see bug 842157).
Alternate Name Uses the non-standard name: :matches()
Disabled From version 47 until version 50 (exclusive): this feature is behind the #enable-experimental-web-platform-features preference (needs to be set to Enabled).
Full support 14
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
Safari iOS Full support 9
Alternate Name
Full support 9
Alternate Name
Alternate Name Uses the non-standard name: :matches()
Full support 5
Notes Alternate Name
Notes Doesn't support combinators.
Alternate Name Uses the non-standard name: :-webkit-any()
Samsung Internet Android No support 9.0 — 10.0
Alternate Name
No support 9.0 — 10.0
Alternate Name
Alternate Name Uses the non-standard name: :matches()
Full support 1.0
Alternate Name
Alternate Name Uses the non-standard name: :-webkit-any()

Legend

Full support
Full support
No support
No support
See implementation notes.
See implementation notes.
User must explicitly enable this feature.
User must explicitly enable this feature.
Uses a non-standard name.
Uses a non-standard name.

See also