Advertisement
  1. Web Design
  2. HTML/CSS
  3. CSS

Intriguing CSS Level 4 Selectors

Scroll to top

CSS selectors have progressed massively over the years, giving developers far more power for targeting specific bits on their pages. The examples in this article are part of the CSS Selectors Level 4 specification. Let’s dive in and investigate seven of these intriguing selectors, some of which I guarantee you’ve yet to use in practice!

Selectors Level 4

The Level 4 spec certainly contains some well-known selectors such as nth-child, but it also contains some very unique characters. Much of the Level 4 spec outlines many new and wonderfully useful pseudo classes and pseudo selectors, so on that subject, let’s briefly review what the difference is between single and double colons when it comes to pseudos in CSS.

Single vs. Double Pseudo Colons

As a general rule these days, double colons :: should be used instead of a single colon : to distinguish pseudo-classes from pseudo-elements and content as the specification states.

“This [double-colon] notation is introduced … in order to establish a discrimination between pseudo-classes and pseudo-elements. For compatibility with existing style sheets, user agents must also accept the previous one-colon notation for pseudo-elements introduced in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and :after). This compatibility is not allowed for the new pseudo-elements introduced in CSS level 3.” –W3C Specification

Okay, so that’s clear, but what’s the difference between classes and elements?

Pseudo-classes vs Pseudo-elements

A pseudo-class always consists of a “colon” : followed by the name of the pseudo-class (the keyword) and, for functional pseudo-classes, by one or more arguments between parentheses (similar to CSS functions). It’s a method for authors to specify and target a special state of the selected element(s).

“Pseudo-classes let you apply a style to an element not only in relation to the content of the document tree, but also in relation to external factors like the history of the navigator (:visited, for example), the status of its content (like :checked on certain form elements), or the position of the mouse (like :hover, which lets you know if the mouse is over an element or not).” – MDN Web Docs

Pseudo-elements create abstractions about the document tree beyond those specified by the document language. For example, document languages don’t offer mechanisms to access the “first letter” or “first line” of an element’s content. Pseudo-elements allow authors to refer to this otherwise inaccessible information. 

Pseudo-elements also provide authors a mechanism to reference content that doesn’t exist in the source document such as the well-known ::before and ::after pseudo-elements that allow access to generated content in CSS 2.

Now that we have the logistics out of the way let’s dive in and explore a few of these intriguing CSS level 4 selectors.

1. :matches()

The :matches() pseudo-class is certainly a strong selector to come from level 4, but browsers are still discussing its inclusion with the exception of Safari (WebKit). 

Generally speaking, it can be used to combine multiple selector rules into one succinct line. It accepts a selector list as its argument. These arguments can be compound selectors and complex selectors, however combinator selectors are not allowed.

1
/* Chrome */
2
:-webkit-any(:hover, :focus, :active) {
3
  color: #222;
4
}
5
6
/* Firefox */
7
:-moz-any(:hover, :focus, :active) {
8
  color: #222;
9
}
10
11
/* Safari */
12
:matches(:hover, :focus, :active) {
13
  color: #222;
14
}

While this snippet combines all our general states for :hover, :focus, and :active into one ruleset, it still doesn’t show the potential it possesses. We’ll review a more complex example coming up next.

If you’re familiar with “nesting” get ready to have your socks knocked off. Matches can be combined with other :matches selectors to mimic this very same feature that many pre-processor users adore, but from within CSS!

1
:matches(section, article) :matches(h1, h2, h3, h4, h5, h6) {
2
  color: goldenrod;
3
}
4
5
/* equivalent to: */
6
section h1,
7
section h2,
8
section h3,
9
section h4,
10
section h5,
11
section h6, 
12
article h1,
13
article h2,
14
article h3,
15
article h4,
16
article h5,
17
article h6 {
18
  color: goldenrod;
19
}

If you’re so inclined you can even string them together to open up another set of possibilities.

1
/* Firefox */
2
:-moz-any(a):-moz-any(:link, :visited) {
3
  color: blue;
4
}
5
:-moz-any(a):-moz-any(:hover, :focus, :active) {
6
  color: red;
7
  text-decoration: none;
8
}
9
10
/* Chrome */
11
:-webkit-any(a):-webkit-any(:link, :visited) {
12
  color: blue;
13
}
14
:-webkit-any(a):-webkit-any(:hover, :focus, :active) {
15
  color: red;
16
  text-decoration: none;
17
}
18
19
/* Safari */
20
:matches(a):matches(:link, :visited) {
21
  color: blue;
22
}
23
:matches(a):matches(:hover, :focus, :active) {
24
  color: red;
25
  text-decoration: none;
26
}

This example is the equivalent of writing:

1
a:link,
2
a:visited {}
3
4
a:hover,
5
a:focus,
6
a:active {}

Keep in mind that :matches() can’t be nested (:matches(:matches())) and doesn't work with :not() (:matches(:not()), :not(:matches())). 

Sadly the spec for this selector has only been solidified in Safari and still awaits other browser vendors to support it, so you’ll have to use separate declarations and vendor prefixes in the meantime. Currently, tooling such as Autoprefixer doesn’t support :matches(), but have no fear as I recently filed an issue on GitHub requesting this support.

If you have an example using this selector and would like to share, please post it in the comments below. We always love a tasty CodePen demo!

2. :placeholder-shown

Input elements have the option to show placeholder text hinting to a user what should be typed into the input. This occurs when the placeholder attribute is present on the input tag and thus matches an input element showing placeholder text.

1
/* affects entire input */
2
:placeholder-shown {
3
  opacity: 1; /* works */
4
  color: gold; /* sketchy */
5
  background: blue; /* works */
6
  font-weight: bold; /* works */
7
  border: 1px solid red; /* works */
8
}
9
10
/* only affects placeholder text */
11
::placeholder {
12
  opacity: 1; /* works */
13
  color: gold; /* works */
14
  background: goldenrod; /* works */
15
  font-weight: bold; /* works */
16
  border: 1px solid red; /* works */
17
}

Essentially :placeholder-shown is for selecting the input itself when its placeholder text is being shown vs. ::placeholder which styles the placeholder text. There is in fact documentation about the ::placeholder noted in the CSS Pseudo-Elements Module Level 4 Editors Draft. Keep in mind that :placeholder-shown is a pseudo class (it’s an element in a particular state) and ::placeholder is a pseudo element (a visible thing that isn’t really in the DOM).

During my research I discovered that color was a particular property (when used with :placeholder-shown to change placeholder text color) that was only honored by Firefox, whereas Chrome and Safari maintain the gray placeholder text color.

3. :any-link

The :any-link pseudo-class represents an element that acts as the source anchor of a hyperlink. 

1
:any-link {}

For example, in HTML5, any <a><area>, or <link> elements with an href attribute are hyperlinks that match :any-link and are equivalent to :matches(:link, :visited). In my opinion it’s a really peculiar selector and is still awaiting a better name apparently, however support is fairly wide these days with the exception of Edge. 

Autoprefixer currently supports this property and provides the proper vendor prefixes for browsers that require them.

4. :indeterminate

The pseudo-class :indeterminate is a selector intended for particular form elements and applies to all media. 

1
:indeterminate {
2
  outline: 2px solid red;
3
}

Typically this selector will match inputs such as radio and checkboxes when there is no checked attribute present. Inputs such as these can exist without without either checked or unchecked states. 

It also selects elements like the progress element with no value content attribute (i.e. when the percent completion is unknown). Support is very stable at the present time across many widely used browser vendors.

5. :user-error

The pseudo-class :user-error represents an input element with incorrect input, but only after the user has interacted with it.  For example, from the time the user has attempted to submit the form and before the user has interacted again with the form element.

1
:user-error {
2
  background: red;
3
  color: red;
4
  border: 2px solid red;
5
}

Support, however, just isn’t there and there’s no way to know if this will be implemented by major browser vendors. For the time being it’s better to use :invalid or :required if you need this kind of error styling for your web forms.

1
:invalid {
2
  background: red;
3
  color: red;
4
  border: 2px solid red;
5
}

6. :optional

The pseudo-class :optional can be used when the value is valid and before the form it belongs to is submitted.

1
:optional {}

It selects any input or textarea element that does not have the required attribute present. This allows forms to easily indicate optional fields, and to style them accordingly. 

This is one of the very lightly documented selectors included in the selectors level 4 specification draft.

7. :scope

The pseudo-class :scope  can limit style rules that only apply to a part of a page by setting the scoped attribute on a <style> element (a direct child of the root element of the subtree you want the styles to be applied to). This limits the styles to affect just the element that is the parent of the <style> element and all of its descendants, or in other words, the parent of the <style scoped> element and anything inside.

1
<ul>
2
  <style scoped>
3
    :scope {
4
       color: red;
5
    }
6
  </style>
7
  <li>item 1</li>
8
  <li>item 2</li>
9
  <li>item 3</li>
10
</ul>

Unfortunately, most browsers don’t support it, or did at one time and have since removed it entirely. Although Chrome and Safari register a match of the pseudo-class, and therefore apply the CSS rule, they don’t yet support style scoping itself; this means that the applied CSS rule floods beyond the intended portion of the document and is equal to :root

Based on current research Chrome 35 and FF 55 removed support however there was a time when Chrome and Firefox supported :scope. All this back and forth makes scope’s outlook appear pretty grim, and according to Can I Use most browsers currently have support placed behind a flag.

Conclusion

There are certainly some very promising selectors in level 4 and many more not mentioned in this article. As with anything in specification form, some selectors could change slightly over time or be removed entirely. It’s up to us as developers to cling to the ones we value the most and make sure the teams in charge are aware. If you have questions or comments or even examples of the selectors mentioned prior please post them below. Happy coding!

Specifications

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.