Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)
Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)

🔧 Programmierung 🕛 vor 1 Monat 11 Min Lesezeit
0

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

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Originally published on ; this is the same problem one layer down.



The conclusion is not "review harder" — review is the wrong instrument for a failure that is invisible in the artifact being reviewed.






Guard the failure class, not the typo



The mistake I nearly made was to fix the line, commit, and move on. That fixes one instance of an unbounded class. The next #} in a stylesheet — or the next //, or the next stray } — lands exactly the same way, and the next debugging session starts from zero.



What went into the test suite instead was two assertions:




CODE
css = (STATIC_DIR / "style.css").read_text()

# 1. Every comment that opens must close.
assert css.count("/*") == css.count("*/")

# 2. No template-language comment syntax inside a stylesheet.
assert "#}" not in css and "{#" not in css






Two lines, no new dependencies, runs with everything else. It knows nothing about the line that broke; it knows the shape of the failure. That is the general move — write the assertion for the class, not the instance. "Is that one line correct?" is a test with a lifespan of one commit. "Do comment delimiters balance, and is foreign comment syntax absent?" still works after the file has been rewritten twice.



In this project the regression lives in test_aesthetic.py; the narrow run is:




CODE
pytest test_aesthetic.py -q -k css_comment_delimiters_balanced






That command proves only the text-level guard. The diagnosis has its own falsifier: if a real CSS parser still returns .timeline-row from the broken fixture above, then the rule was not swallowed by that comment and the investigation has to move to cascade, specificity, or asset selection. A rendered assertion such as “the timeline row computes to display: grid” is the stronger final gate because it observes the surface the user sees.



If you want more than two lines, the guards stack in cost order:




























Tier Catches Cost
Delimiter balance + foreign-token ban unclosed comments, template syntax bleed 2 lines, no dependencies
Parse the stylesheet, assert critical selectors survive anything that removes a rule from the parse one CSS parser dependency
Visual regression on key screens everything, including cascade and specificity bugs a browser in CI, plus baselines


The middle tier is the one most projects skip and probably shouldn't. Parse the file with a real CSS parser and assert that the selectors your layout depends on are present in the parsed result. That would have failed loudly here: the swallowed rules were absent from the parse while present in the text — precisely the discrepancy that defines this bug.



Worth noting what would not have caught it: a linter. Stylelint and PostCSS parse this file without complaint, because the comment is closed, just later than intended. There is no rule for "this comment is longer than you meant it to be." Stylelint does ship no-invalid-double-slash-comments, which flags JS-style // comments in CSS — one member of this class, enumerated by hand. Tooling guards instances; you have to guard the class yourself.






Where these guards break



The two-line check is cheap, not perfect, and being clear about its limits is part of shipping it.





  • Delimiter counting has false positives, and balanced counts are not sufficient. A stylesheet containing content: "/*", or a url() with those characters in it, fails a naive count. Two typos that cancel out still balance, and order is not checked: a */ before its /* counts the same.


  • The foreign-token ban is a blacklist. It knows about {# and #} because those are the languages in this project. A different stack brings different terminators, and the assertion won't know about them until someone adds them.


  • Neither assertion catches the sibling failure. A stale stylesheet in the browser cache produces an identical symptom — correct rules on disk, broken page — and no assertion about file contents can see it. That needs a version query on the asset URL, so the URL changes whenever the file does.



Those last two are why "tests green, screen broken" needs both suspects ruled out in order: is the browser reading this file, and is the browser seeing these rules as rules?






The rule I run on now



Absence of an error is not evidence of correctness in a recovery-by-design language. CSS, HTML, and most template languages swallow bad input and keep going, because that is what lets them survive version skew. There, the only ground truth is rendered output. A green suite tells you the markup exists and the routes return 200; it says nothing about whether the cascade produced the layout you asked for. If your verification surface never renders the thing, .

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
Use custom web fonts in Google Sheets charts
2 Quellen
Introducing the new 1Password App for Google Chat
1 Quelle
Context-aware access controls are available for Gemini Enterprise in the Admin console