From 9ee8766e5ce5bea75eb223e7b14a4842649e10d2 Mon Sep 17 00:00:00 2001 From: lukeocodes Date: Thu, 7 May 2026 10:18:30 +0100 Subject: [PATCH] fix(ui): keep tailwind imports granular so preflight stays scoped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 0.1.2 change switched from granular tailwind imports to the full `@import "tailwindcss" prefix(dg)` shortcut to apply prefix(dg) cleanly. That shortcut also imports preflight at root scope. The scoped-preflight plugin still ran and added its own `:where([data-dg-agent], ...)` copy of every preflight rule, but it did NOT strip the upstream raw preflight that the full import had already injected. The 0.1.2 bundle therefore shipped TWO copies of preflight: one scoped, one raw. The raw copy bled the universal selector reset onto every host page, undoing the original 0.1.1 work. Restoring granular imports with prefix() applied per-import: @import "tailwindcss/theme.css" layer(theme) prefix(dg); @import "tailwindcss/utilities.css" layer(utilities) prefix(dg); is the configuration documented for "preflight-free" Tailwind v4 setups. Theme tokens and utility classes still get the dg- and dg: prefix from prefix(). Preflight is not imported at all, so the only preflight rules in the bundle are the scoped copies the plugin emits. Verified locally: before (0.1.2): unscoped @layer base{*,:after,:before{...}}, raw html{}, body{}, h1,h2,h3{} rules at root scope alongside the scoped versions after (0.1.3): 0 unscoped preflight indicators, 76 scoped :where([data-dg-agent], …) rules, 353 prefixed .dg\: utility refs, 13/13 tests pass Bundle size: 188.9 KB -> 185.2 KB (-3.7 KB) from removing the duplicate raw-preflight rules. Refs: - https://tailwindcss.com/docs/preflight#disabling-preflight (granular import pattern) - deepgram/ui#27 (the original scoped-preflight work) - deepgram/ui#32 (the prefix(dg) work that introduced this regression) - deepgram/dx-stack#4 (gotchas doc gains a "full import re-leaks preflight" section) --- packages/ui/src/styles.css | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/styles.css b/packages/ui/src/styles.css index 8cce157..375984a 100644 --- a/packages/ui/src/styles.css +++ b/packages/ui/src/styles.css @@ -12,12 +12,21 @@ * (`*, ::before, ::after`, `html`, `body`) only applies inside widget * descendants and never bleeds onto the host page. * + * Imports are intentionally granular (`tailwindcss/theme.css` and + * `tailwindcss/utilities.css`) so preflight is NOT pulled in directly. + * Preflight comes only from the scoped-preflight plugin, ensuring every + * preflight rule that ships in the bundle has a `[data-dg-agent]` ancestor + * selector. The full `@import "tailwindcss"` shortcut would inject the raw + * upstream preflight at root scope alongside the plugin's scoped copy, + * leaking the universal selector reset onto the host page. + * * Together, the bundle ships zero unscoped global selectors AND zero * unprefixed utility class names. Any consumer of this package writing * components must use the `dg:` prefix on Tailwind classes. */ @layer theme, base, components, utilities; -@import "tailwindcss" prefix(dg); +@import "tailwindcss/theme.css" layer(theme) prefix(dg); +@import "tailwindcss/utilities.css" layer(utilities) prefix(dg); @plugin "@tailwindcss/typography"; @plugin "tailwindcss-scoped-preflight" { isolation-strategy: inside;