Skip to content

fix(ui): scope tailwind preflight to [data-dg-agent] via plugin#27

Merged
lukeocodes merged 1 commit intomainfrom
fix/scope-preflight
May 6, 2026
Merged

fix(ui): scope tailwind preflight to [data-dg-agent] via plugin#27
lukeocodes merged 1 commit intomainfrom
fix/scope-preflight

Conversation

@lukeocodes
Copy link
Copy Markdown
Member

Why

The full @import "tailwindcss"; shortcut ships preflight at the global scope. The preflight uses universal selectors (*, ::before, ::after, html, body) that override the host page's box model, list styling, heading margins, button appearance, and more.

Symptoms reported in deepgram/deepgram-docs#777: content width collapsed on every non-demo page after the widget bundle finished loading. Same regression hits any customer embedding @deepgram/agents-widget on their own site, not just the docs.

What changed

  • Granular Tailwind imports: theme + utilities only. Preflight is no longer pulled in at the global scope.
  • New: tailwindcss-scoped-preflight plugin with isolation-strategy: inside and selector: [data-dg-agent]. The plugin re-introduces preflight, but every rule is rewritten as :where([data-dg-agent], [data-dg-agent] *)…. The reset only applies to widget descendants.
  • Moved tailwindcss-scoped-preflight from devDependencies to dependencies. The plugin runs at the consumer's Tailwind build time, so it must be installed transitively when consumers depend on @deepgram/ui.
  • Scoped :root-level extended tokens (--primary-hover, --msg-user-bg, --dg-va-primary, --dg-va-border, etc.) to [data-dg-agent]. These are widget-internal tokens; they should never have been on :root in the first place.

Verification

End-to-end through @deepgram/agents-widget@0.1.3 UMD build (locally patched to consume this @deepgram/ui):

before after
Unscoped *,:after,:before,::backdrop rules in widget bundle yes 0
Scoped :where([data-dg-agent], [data-dg-agent] *) rules in widget bundle 0 76
Total [data-dg-agent] occurrences in widget bundle 9 171

Bundle size grew ~3 KB gzipped from the added scope selectors. Acceptable trade for stopping the host-page regression.

Follow-up

@deepgram/agents-widget needs to bump its @deepgram/ui dependency once this lands and a new @deepgram/ui version publishes. The widget's UMD build will then ship correctly-scoped preflight automatically.

There's a separate (smaller) widget-side issue where the widget's Vite build appears to also process Tailwind directly, producing its own copy of unscoped output. That trail leads to widget vite config and is out of scope for this PR; will file separately.

The full `@import "tailwindcss";` shortcut shipped global preflight
rules with universal selectors (`*, ::before, ::after`, `html`, `body`)
that bled into every host page that loaded `@deepgram/ui` or
`@deepgram/agents-widget`. Symptoms reported in deepgram/deepgram-docs
included content width collapse on every non-demo page after the widget
bundle finished loading.

Fix: split the Tailwind import into theme + utilities (granular, no
preflight) and use `tailwindcss-scoped-preflight` to re-introduce
preflight scoped to `[data-dg-agent]`. The plugin's `inside` strategy
rewrites every preflight rule with `:where([data-dg-agent], [data-dg-agent] *)`
so the reset only applies to widget descendants.

Verified end-to-end through @deepgram/agents-widget UMD build:

  before: 0 `:where([data-dg-agent]` matches, full unscoped preflight
  after:  76 `:where([data-dg-agent]` matches, 0 unscoped preflight

Bundle size grew ~3KB after gzip from the added scope selectors, which
is acceptable to stop the host-page regression.

Also moved `tailwindcss-scoped-preflight` from devDependencies to
dependencies so consumers get the plugin transitively when they install
@deepgram/ui. The plugin only runs at the consumer's Tailwind build
time, so it must be resolvable from the consumer's node_modules.

Bonus cleanup: extended tokens that were previously on `:root` are now
scoped to `[data-dg-agent]` so they do not leak to the host page
either. `--primary-hover`, `--msg-user-bg`, `--dg-va-primary`, and
`--dg-va-border` are widget-internal and should never have been on
`:root`.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ui-web Ready Ready Preview, Comment May 6, 2026 0:41am

Request Review

@lukeocodes lukeocodes merged commit 8742c18 into main May 6, 2026
4 checks passed
lukeocodes added a commit to deepgram/agent that referenced this pull request May 6, 2026
#48)

## Why

`@deepgram/ui@0.1.1` (just published from
[deepgram/ui#27](deepgram/ui#27) +
[deepgram/ui#30](deepgram/ui#30)) ships
preflight scoped to `[data-dg-agent]` via
[`tailwindcss-scoped-preflight`](https://github.com/Roman86/tailwindcss-scoped-preflight).
Until the widget bumps its dependency, the published UMD on
`cdn.deepgram.com/widgets/` still ships unscoped Tailwind preflight that
bleeds into host pages.

Symptoms reported in
[deepgram/deepgram-docs#777](deepgram/deepgram-docs#777):
content width collapsed on every non-demo page on the docs site after
the widget bundle finished loading. Same regression hits any customer
embedding the widget on their own site.

## What changed

- `packages/widget/package.json`: `@deepgram/ui` `^0.1.0` → `^0.1.1`
- `bun.lock` regenerated

## Verification

Rebuilt the widget locally against `@deepgram/ui@0.1.1`:

| | before | after |
|---|---|---|
| Unscoped `@layer base{*,:after,…}` rules in widget UMD | many | **0**
|
| `:where([data-dg-agent], …)` scoped rules | 0 | **76** |
| Total `[data-dg-agent]` occurrences | 9 | **171** |

Bundle size: 384.30 KB → 387.53 KB (+3.23 KB) from the added scope
selectors. Acceptable trade.

## Expected after merge

1. release-please opens `chore(main): release agents-widget 0.1.4`.
2. Merging that PR bumps `packages/widget/package.json` to 0.1.4 and
tags `agents-widget-v0.1.4`.
3. The npm publish workflow pushes `@deepgram/agents-widget@0.1.4` to
npm.
4. The CDN publish job uploads the new UMD bundle to:
- `https://cdn.deepgram.com/widgets/v0.1.4/widget.umd.js` (immutable,
365-day TTL)
- `https://cdn.deepgram.com/widgets/latest/widget.umd.js` (5-min TTL
plus invalidation)
5. Anyone loading either URL gets the scoped preflight.

## Refs

- [deepgram/ui#27](deepgram/ui#27) — the source
fix
- [deepgram/ui#30](deepgram/ui#30) — unpin
release-please to actually publish 0.1.1
- [deepgram/dx-stack#4](deepgram/dx-stack#4) —
recipe + gotchas doc
-
[deepgram/deepgram-docs#777](deepgram/deepgram-docs#777)
— where the bleed was first surfaced
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant