template-kit/unlayered-fence
The template-kit/unlayered-fence authoring rule — what it enforces, why, and how to fix it.
This is not “you forgot to layer your CSS.” Everything is layered by default — the build does it for you. This rule governs the escape hatch from layering: an
/* unlayered: … */fence must give a reason and must be closed.
Your hand-written CSS is wrapped in a cascade layer by the build. You do not write
@layer yourself, and there is nothing to forget.
The fence opts a run of rules out of that wrapping, hoisting them above every layer:
/* unlayered: <reason> */…rules that must beat every layer…/* endunlayered */Two things are checked, both under this id:
- Every opener carries a non-empty reason. A bare
/* unlayered: */is a violation. - Openers and closers balance. An unterminated fence is a violation — it would swallow the whole rest of the file out of the layer, which is never what anyone meant.
Reason
Section titled “Reason”Cascade layers have one rule that decides everything here: an unlayered rule beats a
layered one, regardless of specificity. Not “usually” — always. That is what layers are
for, and it is why the platform layers your CSS: inside the layer order (theme, base, components, sections, template, utilities) a Tailwind utility can always override a
section’s stylesheet, and a section’s stylesheet can never leak past its layer to beat
someone else’s utility.
So the hatch exists for exactly one situation: a third-party library injects an unlayered
rule into the document at runtime — a map SDK writing a <style> element when it mounts,
say — and that rule beats your layered override no matter how specific you make it. The
only thing that can beat it is a rule that is also unlayered. Nothing else qualifies. If
your rule merely needs to beat your own CSS, or a Tailwind utility, it belongs in a layer
and you are reaching for the wrong tool.
Which is why the reason is mandatory. A fence with no reason is a rule that outranks every layer on the page for reasons the next author cannot reconstruct — they cannot tell whether it is load-bearing or whether someone was fighting a specificity problem with a hammer. And an unbalanced fence silently promotes every rule below it, so a stylesheet that behaved yesterday starts beating the utilities that were supposed to override it.
Close every fence with /* endunlayered */, and give every opener a reason that names the
runtime-injected rule it has to beat. A short phrase is enough — it only has to be
non-empty and specific about which rule it’s beating, not a full sentence. If it does not
have to beat one, delete the fence: layered is the default, and the default is right.
The Before/After below both open with a /* css-reason: … */ comment. That line has
nothing to do with fencing — it satisfies a separate rule, css-reason,
which every hand-written stylesheet must open with. It’s shown here because real section
CSS carries both; this rule’s own grammar starts at /* unlayered: */.
Before
Section titled “Before”/* css-reason: overrides the map SDK's injected control chrome. */
/* unlayered: */.maplibregl-ctrl-group button { background: var(--tr-color-surface); border-radius: var(--tr-radius-sm);}
.tr-map-legend { padding: 0.5rem;}Two violations at once: the reason is empty, and the fence is never closed — so
.tr-map-legend is hoisted out of the layer too, where it now outranks every utility a
future author might try to override it with.
/* css-reason: overrides the map SDK's control chrome — the SDK builds those nodes after hydration, so Tailwind's scanner never sees a class to compile. */
/* unlayered: the map SDK injects its control styles as an unlayered <style> element at runtime; a layered rule cannot beat it at any specificity. */.maplibregl-ctrl-group button { background: var(--tr-color-surface); border-radius: var(--tr-radius-sm);}/* endunlayered */
/* Our own DOM — layered, like everything else. */.tr-map-legend { padding: 0.5rem;}The fence now wraps exactly the rules that need to escape, and .tr-map-legend is back in
the layer where a utility can still override it.
See also
Section titled “See also”- Theme and CSS — the layer order, and why utilities come last.
css-reason— the comment every hand-written stylesheet opens with; the fence reason is the same discipline, one level in.