DEV Community

Bastien Calou
Bastien Calou

Posted on

What happens when you write an invalid CSS selector? (bad things)

I'd like to talk about a very specific CSS behavior that might cause some headaches if you don't know (or forget!) about it.

Let's create a button:

<button>Click me</button>
Enter fullscreen mode Exit fullscreen mode

I want this button to change its colors when it is pressed. With the :active pseudo-class, that's easy.

button:active {
  background-color: black;
  color: white;
}
Enter fullscreen mode Exit fullscreen mode

So far so good.

To improve accessibility, I want to apply the same effect when the button is focused with the keyboard or an assistive technology (and do nothing special if the focus comes from the mouse).

This is possible thanks to the new :focus-visible pseudo-class. At the time of writing, it is supported without prefix by Chrome and Edge.

button:active,
button:focus-visible {
  background-color: black;
  color: white;
}
Enter fullscreen mode Exit fullscreen mode

If you are on Chrome or Edge, you can try to focus the button with the tab key and you should see the :focus-visible styles.

Focus within styles

This is a great visual help if you don't use a mouse

Here's the catch

Can you guess what will happen on other browsers? Obviously the :focus-visible styles won't show, but any side effect, maybe?

We also lost the :active styles!

There are no styles on Firefox

The :active styles don't show on Firefox, despite the browser perfect understanding of the :active selector. So sad.

The problem might seem obvious if you already know why this happens, but when I had to debug this in a much more complex CSS file, it boggled me for a good few minutes. And I don't think it is a well-known fact among beginners.

So how to put it?

If any part of a selector fails, the whole selector fails

In other words, if a browser doesn't know about the :focus-within selector, it will throw out your entire CSS rule, including the :active part.

You could say that CSS is a bit susceptible. If it doesn't like the type of olive you put on the pizza, it won't even consider eating it.

Garbage truck

CSS has no time to waste with your crazy selectors

Here's the W3C specification:

When interpreting a list of declarations, unknown syntax at any point causes the parser to throw away whatever declaration it’s currently building, and seek forward until it finds a semicolon (or the end of the block). It then starts fresh, trying to parse a declaration again.

And so here's the fix you'll need to use: separate the "safe styles" and the crazy ones.

button:active {
  background-color: black;
  color: white;
}

button:focus-visible {
  background-color: black;
  color: white;
}
Enter fullscreen mode Exit fullscreen mode

Now, :focus-visible might fail, but it won't bother the :active selector.

But why this behavior?

Not really resilient for a language that should adapt itself to many environments, is it?
CSS is broken! JavaScript is our only savior!

Well, there is actually a good reason for this, which is explained in an old document, the CSS 2.1 specification:

CSS 2.1 gives a special meaning to the comma (,) in selectors. However, since it is not known if the comma may acquire other meanings in future updates of CSS, the whole statement should be ignored if there is an error anywhere in the selector, even though the rest of the selector may look reasonable in CSS 2.1.

CSS designers displayed a great level of anticipation here. Indeed, here are some CSS selector that we'll be able to use sooner or later (in some cases, we already are):

.title:is(h1, h2, h3) {} /* h1.title, h2.title, h3.title */
.title:not(h1, h2) {} /* multiple exclusions */
section:has(img, figure) {} /* parent selector */
Enter fullscreen mode Exit fullscreen mode

These selector contain commas, but these commas don't separate the first-level selectors.

If a browser doesn't understand :is and tries to separate our first example, it could result in something like this:

.title:is(h1 /* What is this? Ignore */
h2 /* Style all the h2, even without the .title class! */
h3) /* Wut?*/
Enter fullscreen mode Exit fullscreen mode

A man with a chainsaw

The CSS chainsaw massacre

Instead, the browser is cautious. This has been discussed again in 2013, and kept the same way "due to Web-compat concerns".

Hacks

Some developers even used this as a hack to discriminate a specific browser.

Wanna apply a specific style to all browsers except IE? Just add a modern selector for the sole purpose of making IE ignore the rule.

.covfefe:not(div), h1 {
  color: red;
}
Enter fullscreen mode Exit fullscreen mode

Tadaa, the h1 will be red only if the browser also understands :not.

With the correct combination of selectors, you could target or exclude any specific browser version. Which is of course very, very dirty, and hopefully not needed anymore 🙏

Top comments (0)