It’s about time we started learning some new CSS, isn’t it?
We’re all used to new features being introduced to frameworks left and right, but we often forget that new features are constantly being developed in the core web languages we use every day.
W3C Editor’s Draft
The CSS Selectors Level 4 draft from the 15th of January outlines a major jump in features for CSS.
Before we get started, you should know: the CSS you find in this article, today, will largely fail to work in any browser. Even the newest of the new. The reason for this is that the draft is exactly that–a draft. This is the fourth version of the level 4 selectors draft, which started back in 2011. The selectors suggested in the draft are a specification for new selectors that are being suggested by the editors of the document itself, through a lot of deliberation and collaboration with people in the community who participate in the W3C mailing list.
So what does this mean? It means that in the relatively near future, we will likely see a lot of these features implemented, and many will be delivered in development versions of browsers like Chrome and Firefox.
Let’s look at some of the new selectors!
1. :not(.warning, .alert)
We’ve seen the :not
selector before in level 3 selectors. However, the current draft of the level 4 :not
selector allows for multiple arguments with more complex matching.
Previously, :not
could only be used with a single selector and couldn’t be combined. For example, level 3 :not
might look like this:
a:not([href*="somesite.com"]) { }
This would choose all anchor tags that didn’t have an href
including the string "somesite.com"
. But with the new draft, we can do more interesting things.
We’ll cover :has
shortly, but for now, here is a selector that selects all links that are not direct descendants of elements with a class of col
or a class that begins with col
(like Bootstrap’s grid classes col-md-4
). It will also ignore anchors that have img elements as descendants.
a:not([class|="col"]>a, :has(img)) { }
This selector also allows us to do things like:
div:not(div+div) { }
This selects all divs that are not next-in-line siblings of other divs.
Taking it Even Further
With the addition of the :nth-last-child
pseudo-class we can also do things like this:
div:not(.container>div:nth-last-child(-n+2)) { }
This would select all divs except those that are direct descendants of .container
elements and are the last two siblings.
As you can see, these rules can get very powerful and complex; but let’s add even more flexibility into the mix by introducing the :has
pseudo selector.
2. :has(div, p, > a)
The :has
pseudo-selector allows you to select elements that have the passed in arguments as children. For example, to select all anchor links that have image elements as children, you would use the following syntax:
a:has(img) { }
However, :has
is not limited to simple selectors. You can combine :has
with :not
and :nth-*
selectors to create quite complex relational selectors.
For example, let’s select table
elements that have more than ten rows:
table:has(tr:nth-of-type(11)) { }
Combining of :has() and :not()
How about selecting a body
element whose last child element isn’t a footer?
body:not(:has(footer:last-child)) { }
3. :any-link
Currently we can target hyperlinks using :link
and :visited
. This goes further than simply using the a
selector, as it checks for the presence of href
, and checks the user’s browsing history to determine if a given link has been visited.
:link, :visited { color: blue; }
With proposed level 4 selectors we could style all links, visited or not, using :any-link
. The above style is relatively equivalent to:
:any-link { color: blue; }
4. :scoped
Until level 4 selectors, CSS has been given a global scope. In other words, if you add the following CSS:
div { color: #444; }
all divs will receive the color: #444
style rule. (This assumes that no other competing styles are applied.) Level 4 selectors allow for stylesheets and style tags to be scoped to an element:
<section> <h2>This is outside the scope.</h2> <aside> <style scoped> h2 { font-size: 2rem; } </style> <h2>This is within the scope</h2> </aside> </section>
In this example, we have applied a scope to a style tag inside of the aside
element. The rules in this style tag will only apply to descendants of the style
’s parent element.
5. :matches(selector1, selector2)
The :matches
pseudo selector allows us to check if an element matches a list of elements. For example, if you wanted to target all anchor, paragraph, and h2 tags within an article, you could accomplish that with the following:
article :matches(h2, a, p) { }
This replaces the previously much more verbose syntax:
article a, article h2, article p { }
Note: As of this version of the draft, :matches()
may not be used with :not
, :has
, or another nested :matches
.
6. Explicit Descender Selector >>
You’re probably familiar with the space character allowing us to write descendant rules, such as all anchors inside a div:
div a { }
But until now we haven’t seen an explicit descendant selector. With level 4 selectors we have one: >>
.
However, note that this results in an unnecessary extra character in your CSS, as you can accomplish the same effect with a single character space, so be careful how you use it. Presumably it acts as a bridge between the immediate child selector, >
, and the shadow-dom access selector, >>>
.
7. Table In-Column Selector ||
This selector is a welcome addition for table styling. Let’s look at some markup for a basic table:
<table>
<colgroup>
<col class="id">
<col class="personnel-info" colspan="2">
</colgroup>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<!-- etc -->
</tr>
</tbody>
</table>
Styling horizontally grouped elements (ie: within the same row) has historically been relatively straightforward to accomplish. Styling elements grouped vertically is a whole other ball game.
In order to style all elements that are a part of the column with the class personnel-info
, we could add more classes to the markup and style them directly, or, looking towards level 4 selectors, we could use the ||
selector. The ||
selector allows you to cleanly target those td
elements as follows:
.personnel-info || td { }
Conclusion
That wraps up our overview of some of the new features coming in level 4 selectors! These powerful, byte-saving new set of tools aren’t locked into place entirely, but browsers are beginning to implement them in their early, experimental forms, so be on the lookout for updates to the spec. Of course when they are formalized, we will cover them here on Tuts+!
Relevant Links
Check out our course on The CSS of the Future, where Craig Campbell covers, amongst other things:
Comments