CSS Collision Visualized
Interactive demo of bare element selectors colliding with library HTML — three defects from one rule, shown against common library producers.
Bare element selectors in a global stylesheet cascade into HTML emitted by third-party libraries. Box-model properties on different boxes stack rather than override, so a single well-meaning pre {} or a {} rule can produce visibly doubled padding, doubled borders, mismatched backgrounds, or structural elements that inherit prose styling. See Bare Element Selectors vs Library HTML for the full reference table and diagnostic.
The same syntax-highlighter and tabbed-code wrappers appear on Interactive Features Showcase, which makes this collision easier to reproduce in a real article context.
Worked example: pre vs a syntax-highlighter frame
Syntax highlighters (Expressive Code, Shiki frames, Prism plugins, etc.) wrap code in an outer <pre> and an inner container with its own padding, border, and background. A bare pre { padding, border, background } rule in the global stylesheet lands on the outer element — and produces three defects at once:
- Padding stacks. The outer
<pre>gets the global padding; the inner code line already had the highlighter’s padding. - Border doubles. The frame titlebar already draws a bottom border; the global rule draws a top border right beneath it.
- Background mismatches. The frame chrome uses the highlighter’s theme background; the global rule paints the outer
<pre>with a slightly different token.
Click a tag for the rule that caused it.
Fix. If no hand-authored <pre> appears anywhere on the site (i.e. every <pre> is highlighter output), delete the bare rule. The library owns inner styling via its own config. Keep one declaration for outer block rhythm:
.expressive-code { margin: 1.5rem 0;}If hand-authored <pre> does appear, scope the rule to a content wrapper instead:
.content :global(pre:not([class])) { /* your prose styling */}Same cascade, other common producers
The pre case is visible because three box-model properties stack at once. The same cascade shape applies elsewhere — one property at a time, so the collision is quieter but just as real. Each card below toggles between the broken state (global rule applied) and the fixed state (rule scoped to prose or removed).
p { max-width: 68ch } A paragraph that should fill its container. The 68ch cap from the global rule quietly clips it even though the card itself is wider, leaving awkward whitespace on the right of every result.
ul { padding-left: 1.5rem } a { underline + link color } blockquote { border-left, padding } A neutral pull quote. No callout directive was used.
:::note) — meant to look different.Toggle each card to see the cascade applied vs scoped away. The pre case demoed above stacks three box-model defects at once
and so reads as a single visible bug; these four apply one property at a
time, so each collision is quieter but the cascade shape is identical.
Fix ladder
- Delete when no legitimate prose consumer exists.
- Scope to a content wrapper class — move the rule into a layout-scoped
<style>with:global()targeting your rendered-markdown region. Astro-native. :not()exclusion — per-library, doesn’t scale.
See also: Bare Element Selectors vs Library HTML for the full inventory and diagnostic.
Sources
- MDN, Specificity
- MDN, The box model
- Astro Docs, Styles and CSS