"Undefined SCSS variable silently kills panels at runtime"
▸ SYMPTOM
Panels render as fully invisible on a published build: the opener UI fades for a modal that never appears, making the game look dead-ended. The build is GREEN (both headless and in-editor compile_status), no Code Error toast fires, and no red compile-time diagnostic appears anywhere.
▸ CAUSE
An undefined SCSS variable ($var used but never declared in that .razor.scss sheet) is caught only at runtime. The SCSS parser emits a per-render console warning:
Skipping malformed rule in /ui/x.razor.scss: Unknown variable '$var'But that warning lives only in the console log: no Code Error toast, no compile error. The sheet silently drops all rules referencing the undefined variable, which can make the entire panel invisible (not just one rule's effect).
This typically happens when a sweep or refactor copies a token name from another sheet. SCSS sheets in s&box share nothing: a variable defined in one sheet does not exist in another.
▸ FIX
All three defenses, used together:
-
Define every
$varin the sheet that uses it. Never assume a variable exists because another sheet declares it; each.razor.scssis self-contained. -
Static pre-push lint. Diff used-vs-declared variables per sheet. A simple grep that flags
$varreferences with no matching$var:declaration in the same file catches this in seconds with no editor. -
In-editor smoke pass. Open each overlay/modal and assert a non-trivial rendered rect. This catches the entire invisible-panel failure class regardless of cause.
▸ SAME FAILURE FAMILY: A TRAILING `//` COMMENT ON A VALUE LINE
A second trigger in the same family (confirmed live, engine 26.07.22): a // line comment placed after a property value on the same physical line also aborts the rule. The parser doesn't stop at the comment marker; it swallows the rest of the physical line, including the rule terminator and the closing }:
/* BROKEN - the // eats the rest of the line, including the } */
.panel {
background-image: url( "x.png" ) // note
}The console message shares the same prefix, with a different suffix:
Skipping malformed rule in /ui/<file>.razor.scss: Invalid Rule "<property>:"Blast radius is the same: the whole stylesheet for that panel drops, not just the one rule, so the panel renders with no styles (typically invisible or zero-size) while compile_status and dotnet build both stay green. Because it's baked into the source file, it survives editor restarts. Law: SCSS comments go on their own line above a rule, never trailing a value.
▸ RECOVERY TRAP: A TRANSIENT BROKEN HOT-COMPILE IS NOT HEALED BY A LATER GREEN HOTLOAD
Related trap worth flagging: a panel killed by a transient broken hot-compile window (the assembly briefly broken mid multi-file save) is not resurrected by a later green hotload. The component/GameObject must be rebuilt (disable/enable) or the session restarted. When saving multiple files into a live-compiling tree, land definitions first to avoid opening that broken window at all.
▸ WHY IT WORKS
The SCSS parser in s&box is a runtime text processor, not a compile-time analyzer. Undefined variables produce a console warning and silently skip the affected rules; the sheet partially evaluates rather than failing hard. This is the same silent-parser family as the border-style: dashed and radial-gradient(... at ...) traps, but worse: those at least raise a red Code Error toast. This one only whispers to the console, where nobody looks during normal development.
- Added the trailing // line-comment trigger (same 'malformed rule' family) and the transient-broken-hotload recovery trap
- Published