CSS Error Handling

May 03, 2021#css#guides

Have you ever wondered why nobody talks about CSS error handling? You write CSS rules, check what applied, keep adding more rules. So what will happen if you’re missing semicolons, using wrong properties, importing broken stylesheets, running in old browsers? Your sites still show up but with unexpected layouts. CSS is forgivable!

When errors occur in CSS, the parser attempts to recover gracefully, throwing away only the minimum amount of content before returning to parsing as normal.

This is because errors aren’t always mistakes—new syntax looks like an error to an old parser, and it’s useful to be able to add new syntax to the language without worrying about stylesheets that include it being completely broken in older User Agents (UAs).

The precise error-recovery behavior is detailed in the parser itself, but it’s algorithm can be summarized as follow:

  • At the top level of a stylesheet, an at token (@) starts an at-rule. Anything else starts a qualified rule, and is included in the rule’s prelude. This may produce an invalid selector, but that’s not the concern of the CSS parser—at worst, it means the selector will match nothing.

  • Once an at-rule starts, nothing is invalid from the parser’s standpoint; it’s all part of the at-rule’s prelude. Encountering a semicolon token (;) ends the at-rule immediately, while encountering an opening curly-brace token ({) starts the at-rule’s body. The at-rule seeks forward, matching blocks (content surrounded by (), {}, or []) until it finds a closing curly-brace token (}) that isn’t matched by anything else or inside of another block. The contents of the at-rule are then interpreted according to the at-rule’s own grammar.

@import 'my-styles.css';

@media print {
  body {
    font-size: 10pt;
  • Qualified rules work similarly, except that semicolons don’t end them; instead, they are just taken in as part of the rule’s prelude. When the first {} block is found, the contents are always interpreted as a list of declarations.
pre[class*='language-'] {
  overflow: auto;
  padding: 1em;
  • 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.

  • If the stylesheet ends while any rule, declaration, function, string, etc. are still open, everything is automatically closed. This doesn’t make them invalid, though they may be incomplete and thus thrown away when they are verified against their grammar.

  • After each construct (declaration, style rule, at-rule) is parsed, the user agent checks it against its expected grammar. If it does not match the grammar, it’s invalid, and gets ignored by the UA, which treats it as if it wasn’t there at all.

It’s helpful to understand how CSS parser dealing with errors. If you use a CSS property or value that a browser does not understand, the browser will ignore it. Start using new CSS features today either with graceful degradation or progressive enhancement strategy.