CSS Doesn't Throw: One Mistyped Comment Closer Silently Ate 15 Lines of My Stylesheet

2026年8月5日6 次浏览来源:Dev.to阅读原文

Originally published on hexisteme notes.

Every test passed.

The page was in pieces.

I was rebuilding a small internal dashboard — FastAPI, Jinja2 templates, hand-written CSS, no build step — and the layout had come apart.

Timeline rows unstacked into a vertical column.

Status dots floated free of their rows.

Log group labels overlapped.

It looked exactly like a page whose stylesheet had failed to load.

The stylesheet had loaded.

All 481 tests in the suite were green.

And when I grepped the CSS file for the rules that were obviously not being applied, they were sitting right there on disk, correctly written.

The cause was a comment closer.

Somewhere in the middle of the file, a had been closed with — Jinja's comment terminator — instead of .

Muscle memory, from switching back and forth between templates and .

CSS then did precisely what the specification tells it to do: it kept reading.

The comment ran on and swallowed the next 15 lines of rules — the timeline-row grid, the feed, the bucket layout, the dot alignment — until it hit the next real , seventeen lines down.

No error.

No console warning.

No failing test.

The rules were present in the file and absent from the page at the same time.

The typo is the least interesting part.

What's worth keeping is why CSS is designed to fail without symptoms, why source-level review cannot see it, and why the fix is a two-line assertion rather than more care.

Why nothing complained: CSS has no fatal errors The CSS Syntax specification defines comment consumption like this: on seeing , consume everything "up to and including the first , or up to an EOF code point." No notion of a comment being too long, no heuristic about blank lines or braces, no upper bound.

First wins.

A comment closed seventeen lines later than intended is not a malformed comment — it is a well-formed comment that happens to be seventeen lines long.

The parser has no way to know you meant something else.

Even if the comment had run to the end of the file you would not get an exception.

Reaching EOF inside a comment is flagged as a parse error in the spec — but a parse error in CSS is not a failure.

Error handling is defined for every one of them, and CSS 2.1 goes further: user agents "must close all open constructs (for example: blocks, parentheses, brackets, rules, strings, and comments) at the end of the style sheet." This is deliberate, and the rationale in the spec explains the entire design: 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 UAs.

That is a good trade — it is why a stylesheet using a new property still renders in an older browser instead of blanking the page.

The price is that CSS cannot tell you it failed, because from the parser's point of view nothing failed. "Ignore," in the spec's own words, means the user agent "parses the illegal part (in order to find its beginning and end), but otherwise acts as if it had not been there" — which is exactly the behavior that leaves no trace.

Put languages on a spectrum.

JSON, YAML, and JavaScript throw: bad input produces a loud, located error.

CSS and HTML recover: bad input produces a silently different document.

You have to go look at the output.

The same failure class has other members, and they are all quiet: used as a comment in plain CSS.

It is not a comment.

The parser reads it as the start of a selector, keeps consuming until it finds a block, decides the whole qualified rule is invalid, and drops it — so the next rule silently disappears.

A stray or missing .

Everything until the braces rebalance is either nested into the wrong rule or thrown away.

Any comment terminator borrowed from a neighboring languag

分享