Astro's scoped styles vs. runtime-created DOM

(Sample post — placeholder content for the new posts collection.)

Astro scopes every <style> block to the elements present in the component template by stamping them with a generated attribute. That’s great until your page builds DOM at runtime: document.createElement produces elements without the scoping attribute, so your carefully written rules silently never match. No error, no warning — the element just renders unstyled.

This bit three separate variants on this site before the pattern was written down:

  • TerminalBoot’s terminal lines lost their phosphor colours.
  • TypeSpecimen’s per-letter transforms did nothing because display: inline-block never applied.
  • PaperFold’s facets collapsed to width: 0.

The fix is the :global() wrapper:

/* Created in JS, so it must be :global() */
:global(.line) { white-space: pre-wrap; }

The better fix, where possible, is to keep the elements in the template in the first place and toggle them with classes or attributes — then the scoping just works, and you keep the isolation benefits you chose Astro for.