CSS4 Selectors: What Can We Expect?
Important: Due to the work-in-progress nature of the Selectors Level 4 Draft, there’s a chance that things included in this post will change or disappear. This post was written following the guidelines of the January 20th, 2017 draft.
CSS just turned 20 years old and some people still write it as if it was 2002. I’ve always felt this powerful language was often left behind in the project development process and seen as something that just needed to be done, without worrying about quality, optimization or simplicity. Thankfully, this mindset has begun to change in recent years with the growth in interest in front end stack technologies.
In my opinion, a well written/coded app falls short without a good CSS implementation. The “snappiness” of a website depends a lot on the quality of the CSS that it has. Sometimes it’s hard to achieve a specific layout or styling and this causes us to create clunky or hacky stylesheets that, in the end, may have an impact on the user experience. The good news is that CSS4 allows us to use some nice new selectors that will simplify the way we code our stylesheets, keeping them straightforward and organized.
In this post, I want to share with you some of the ideas that are being discussed on the W3C right now.
Let’s take a look at these:
Logical Combinations
Negation :not(selector1, selector2)
This pseudo-class was already present in CSS3, but it just allowed one single selector as an argument. Now, in my opinion, its use is pretty explicit as it can receive as many selectors as you want. It simply takes a list of selectors as an argument and represents one or many elements that are not represented by it’s argument.
Examples:
input:not([required][type="text"]) { … } *:not(.hidden) { … }
Matches-any :matches(selector1, selector2)
This pseudo-class is the opposite of :not. It takes a list of selectors as argument and represents one or many elements that are represented by the given argument.
Examples:
li:matches(:first-child, .highlighted) { … } a:matches(*:hover, *:focus) { … }
Relational :has(selector1, selector2)
It’s a pseudo-class that takes a relative selector list as an argument and represents one or many elements if any of the relative selectors match at least one element.
Examples:
li:has(> ul) { … } dt:has(+ dt) { … }
Attribute selectors
Case-sensitivity
The default behavior for case-sensitivity of attribute’s values and names depends on the document language. With this new selector, it is possible to match any element where the specified attribute is equal to a value of any case combination. We just need to add an i before the closing bracket (]) to indicate case-insensitivity.
Examples:
/* All input elements with a value of name, Name, NAME, etc, will be matched */ input[value="name" i] { … }
Linguistic Pseudo-classes
Directionality Pseudo-class :dir()
This pseudo-class allows us to write selectors that represent an element based on its directionality (left-to-right, right-to-left), determined by the document language.
Example:
blockquote:dir(ltr) { border-left: 5px solid #555; } blockquote:dir(rtl) { border-right: 5px solid #555; }
Language Pseudo-class :lang()
This pseudo-class represents an element that is in a language of the listed as arguments. It was introduced first in CSS2, but in CSS4 we have the possibility to add wildcard matching.
Examples:
a:lang(es-CR) { background-color: blue; } /* The following example will match all anchor elements with a language ending in CA like en-CA or fr-CA */ a:lang(*-CA) { background-color: red; }
Location Pseudo-classes
Hyperlink Pseudo-class :any-link
It represents an element that acts as the source anchor of a hyperlink (elements with an href attribute). In other words, any element that would match :link
and :visited
. It’s like a shorthand of :matches(:link, :visited)
.
Example:
#navbar a:any-link { text-decoration: none; }
Reference Element Pseudo-class :scope
This selector is useful when we want to give a scope to the CSS rules that we are writing. It allow us to represent a (potentially empty) set of elements that provide a reference point for selectors to match against, just like querySelector()
call in DOM, or the parent element of a scoped <style>
element in HTML5.
Example:
<div> <p>Paragraph outside the scope.</p> <div> <style scoped> :scope { background-color: black; } p { color: white; } </style> <p>Paragraph is inside the scope.</p> </div> </div>
Time-dimensional Pseudo-classes
These pseudo-classes help to classify relative to the currently displayed or active position in a timeline, like a speech rendering of a document or when displaying subtitles for a video using WebVTT.
Current-element Pseudo-class :current
This represents the element, or an ancestor of the element, that is currently being displayed. Also, it’s possible to provide a list of selectors. This will represent the :current
element that at the same time matches the arguments, and if there aren’t matches, the innermost ancestor of the :current
element that does.
Example:
:current(p) { background-color: lightgreen; }
Past-element Pseudo-class :past
This pseudo-class represents any element that is defined to occur entirely prior to a :current
element.
Example:
:past(p) { background-color: yellow; opacity: .5; }
Future-element Pseudo-class :future
This selector represents any element that is defined to occur entirely after a :current
element.
Example:
:future(p) { background-color: lightblue; opacity: .9; }
User Action Pseudo-classes
Input Focus-Ring Pseudo-class :focus-ring
This pseudo-class represents an element that matches the :focus
pseudo-class, but at the same time, the UA determines the focus should be specially indicated on the element (via a “focus ring”). This is just like what happens with buttons or links when tabbing through a document.
Example:
/* This will match any focused element */ .content > :focus { outline-width: 2px; outline-style: solid; outline-color: lightblue; } /* This will match just elements that display a focus ring by default, like buttons or input fields */ .content > :focus-ring { outline-width: 2px; outline-style: solid; outline-color: lightblue; }
Drag-and-Drop Pseudo-classes :drop
and :drop()
The :drop
pseudo-class represents all elements that are drop targets while the user is “dragging” an item to be “dropped”. The :drop()
functional pseudo-class is the same as :drop
, but allows extra filters to be specified that can exclude some drop targets. The filters can be:
active:
The drop target is the current drop target for the drag interaction.valid:
This matches if the drop target is valid (according to the document language) for the item being dragged. Otherwise, it matches all drop targets. For example, it is useful when we want to style drop targets when they just accept files.invalid:
This matches if the drop target is invalid (according to the document language) for the object currently being dragged. Otherwise, it matches nothing.
Multiple filters can be combined in the argument.
Example:
.drop-zone:drop(valid active) { background-color: lightgreen; }
Input Pseudo-classes
Input Control States
Mutability Pseudo-classes :read-only
and :read-write
An element matches :read-write
if it is alterable by the user, as defined by the document language. Otherwise, it is :read-only
.
Example:
input:read-write { border-color: green; } input:read-only { border-color: gray; }
Placeholder-shown Pseudo-class :placeholder-shown
This pseudo-class matches an input element that is showing placeholder text.
Example:
input:placeholder-shown + label { display: none; }
Default-option Pseudo-class :default
This applies to the one or more UI elements that are the default among a set of similar elements. This typically applies to context menu items, buttons and select lists/menus. For example, this will match the default submit button in a set of buttons.
Example:
.btn { background-color: lightgray; } .btn:default { background-color: lightgreen; }
Input Value States
Indeterminate-value Pseudo-class :indeterminate
This pseudo-class applies to UI elements with a value in an indeterminate state. For example, radio and checkbox elements can be in an indeterminate state, neither checked nor unchecked.
Example:
:indeterminate, :indeterminate + label { background: yellow; }
Input Value-checking
Validity Pseudo-classes :valid
and :invalid
An element is :valid
or :invalid
when its content or value is valid or invalid respecting the data validity semantics defined by the document language. An element without data validity semantics is neither :valid
nor :invalid
.
Example:
<input type="text" required />
input:invalid { border-color: red; }
Range Pseudo-classes :in-range
and :out-of-range
These pseudo-classes apply only to elements that have range limitations. An element is :in-range
or :out-of-range
when its value is in range or out of range with respect to its range limits as defined by the document language. If an element doesn’t have data range limits or is not a form control it’s neither :in-range
nor :out-of-range
.
Example:
<input type="number" required max="5" />
input:out-of-range { border-color: red; }
Optionality Pseudo-classes :required
and :optional
A form element is :required
or :optional
if a value for it is, respectively, required or optional before the form can be submitted. Elements that are not form elements are neither :required
nor :optional
.
Example:
input:optional { color: gray; }
User-interaction Pseudo-class :user-invalid
This represents an element with incorrect input, but only after the user has interacted with it.
Example:
input:user-invalid { border-color: red; }
Tree-Structural pseudo-classes
:blank
pseudo-class
This pseudo-class is like the :empty
pseudo-class, but additionally, matches elements that only contain code affected by whitespace processing.
Example:
<p> </p>
p:blank { display: none; }
Combinators
Descendant combinator ( )
or (>>)
This selector describes an element that is the descendant of another element in the document tree. It’s essentially the same as separating compound selectors with whitespace, but it’s just more explicit.
Example:
h2 >> span { … }
Grid-Structural Selectors
Column combinator ||
This matches any cell belonging to a column in a grid or table.
Example:
<table> <col>Title</col> <col class="selected">Name</col> <tr> <td>Architect</td> <td>John Doe</td> </tr> <tr> <td>Software Developer</td> <td>Donkey Kong</td> </tr> </table>
col.selected || td { background: lightblue; font-weight: bold; }
:nth-column()
pseudo-class
This matches any cell, in a grid or table, belonging to the nth column in a grid or table.
Example:
:nth-column(2n) { background: gray; }
:nth-last-column()
pseudo-class
This pseudo-class represents any cell belonging to the nth column in a grid or table, counting from the last one.
Example:
:nth-last-column(3n+1) { background: purple; }
Conclusion
As you can see, there are lots of new and powerful selectors that will make our lives easier. Keep them in mind, as the future is near! Again, I want to clarify this information is based on the January 20th, 2017 editor’s draft of “Selectors Level 4”, so things may change at any moment. — Want to read more from Arturo in the future? Subscribe to our blog and follow us on Twitter.