Summary
The useTheme browser adapter appends a CSSStyleSheet to document.adoptedStyleSheets but never removes it on dispose() — only Vue watchers are stopped. Repeated mount/unmount (HMR, test suites, micro-frontend / multi-app teardown) accumulates orphaned stylesheets.
Location
packages/0/src/composables/useTheme/adapters/v0.ts:122-130 — upsert() creates this.sheet and appends it to document.adoptedStyleSheets.
:64, :75, :89-92 — all three dispose assignments stop watchers only; none remove the sheet or clear this.sheet.
dispose is invoked on teardown via useTheme/index.ts:399 (app.onUnmount(() => adapter.dispose?.())).
- Precedent: the sibling
useTheme/adapters/unhead.ts:111-113 correctly disposes its injected style.
Impact
Single long-lived SPA: negligible (the sheet should live for the app lifetime). Repeated mount/unmount: orphaned stylesheets accumulate. Each sheet is tiny and the theme CSS is idempotent ([data-theme]-scoped vars), so this is memory/cleanliness growth, not visual or correctness breakage. SSR is unaffected (the branch is IN_BROWSER-gated). Low severity.
Suggested fix
In dispose, remove the sheet and clear the ref, across all three assignment sites:
document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== this.sheet)
this.sheet = undefined
Follow-up to #342 (leak-safe adapter lifecycle), which added dispose → app.onUnmount for watchers but did not cover the adopted stylesheet the adapter owns. Optional: a regression test asserting the sheet is gone from document.adoptedStyleSheets after app.unmount().
Summary
The
useThemebrowser adapter appends aCSSStyleSheettodocument.adoptedStyleSheetsbut never removes it ondispose()— only Vue watchers are stopped. Repeated mount/unmount (HMR, test suites, micro-frontend / multi-app teardown) accumulates orphaned stylesheets.Location
packages/0/src/composables/useTheme/adapters/v0.ts:122-130—upsert()createsthis.sheetand appends it todocument.adoptedStyleSheets.:64,:75,:89-92— all threedisposeassignments stop watchers only; none remove the sheet or clearthis.sheet.disposeis invoked on teardown viauseTheme/index.ts:399(app.onUnmount(() => adapter.dispose?.())).useTheme/adapters/unhead.ts:111-113correctly disposes its injected style.Impact
Single long-lived SPA: negligible (the sheet should live for the app lifetime). Repeated mount/unmount: orphaned stylesheets accumulate. Each sheet is tiny and the theme CSS is idempotent (
[data-theme]-scoped vars), so this is memory/cleanliness growth, not visual or correctness breakage. SSR is unaffected (the branch isIN_BROWSER-gated). Low severity.Suggested fix
In
dispose, remove the sheet and clear the ref, across all three assignment sites:Follow-up to #342 (leak-safe adapter lifecycle), which added
dispose → app.onUnmountfor watchers but did not cover the adopted stylesheet the adapter owns. Optional: a regression test asserting the sheet is gone fromdocument.adoptedStyleSheetsafterapp.unmount().