Reference
Known limitations
A serious library doesn't pretend everything is universal. These are the boundaries we know about — documented so they're not a surprise.
1View Transitions API availability
Runtime transitions use the View Transitions API when available and fall back to a CSS-variable cross-fade otherwise. The View Transitions API is not available in every browser or context:
- Not available in Firefox as of writing — the CSS cross-fade fallback is used.
- Only applies to top-level same-origin navigations/updates; scoped updates inside
ThemeScopeuse the fallback path. - If
prefers-reduced-motion: reduceis set, transitions are disabled entirely rather than degraded.
2Custom scrollbar
ThemeScrollbar is an overlay scrollbar: it hides the native scrollbar and draws its own. This has consequences:
- The native scrollbar is hidden via the pre-paint CSS; content width does not change (no layout shift), but the scrollbar itself occupies no space — content can be obscured until the overlay thumb is over it.
- Browser-specific native scrollbar styling (Firefox
scrollbar-width, WebKit pseudo-elements) is replaced, not enhanced. - Scroll-linked effects that measure
window.innerWidthbefore the overlay mounts can observe the wider viewport.
3Touch and mobile
On touch devices the overlay scrollbar is intentionally inert: it does not fight native touch scrolling. The scrollbar engine observes pointer/touch input and does not attach drag handling to touch pointers. Consequences:
- Touch scrolling is fully native — the overlay thumb is informational, not draggable, on touch.
- On hybrid devices (touch laptops), a coarse-pointer touch event disables drag interaction until a fine pointer is detected again.
- iOS Safari's rubber-banding and scroll-to-refresh behave exactly as the native scrollbar would — the overlay does not interfere.
4SSR
SSR support differs slightly by framework. Next.js and Nuxt are the reference integrations with the full story (cookie resolution, fingerprint, blocking bootstrap, zero flash). The others hydrate client-side:
- Vue / Svelte / Solid / Angular providers are client components; SSR renders the provider but theme resolution happens on hydration. Use the core
createThemeBootstrapScriptfor zero-flash in those frameworks. - Angular ships an
esm+defaultexport map (ng-packagr layout) rather than the standardimport/requireconditions; Noderequire()of the Angular package is not supported. - The
ThemeScrollbarSSR output is pre-paint CSS only; the overlay engine mounts on hydration.
5System dark with JavaScript disabled
With JavaScript disabled and initialMode: "system", the first paint uses the light theme. The @media (prefers-color-scheme: dark) fallback block cannot override the inline light CSS variables that the SSR render applies to <html>. The blocking bootstrap script is the primary zero-flash mechanism; with JS enabled, system dark is applied before first paint in every browser that supports matchMedia.
6Adapter constraints
Adapters bridge tokens into a component library — they don't replicate every library behavior:
- MUI / Chakra / Ant Design / Mantine adapters are React-based and are available in React, Next.js, Astro, and Remix. They are not shipped for Vue, Svelte, or Solid — using a React-only component library in a non-React framework is not a supported combination.
- shadcn/ui, Bootstrap, DaisyUI, Open Props have framework-neutral factories and CSS output, so they work in every framework.
- UnoCSS is a preset: it maps tokens to utilities that reference the live
--theme-*variables, so it has no runtime install/update/cleanup lifecycle of its own. - Mantine is a theme builder + hook; apply it manually when the theme changes (it has no runtime adapter).
7Framework-specific gaps
- Svelte —
ThemeScopereadstheme/family/modeprops at mount only (the component is a legacy-style Svelte component). React, Vue, and Solid react to prop changes. Remount the scope to change an explicit theme in Svelte. - Angular — the docs app and adapters are verified against the standalone
provideThemeKitintegration; Angular Universal streaming is not verified. - Nuxt — the module targets Nuxt 3.10+; auto-imported composables work in the app directory, and server utilities are exported from the package root.
8Framework dependency advisories
@theme-kit/* packages ship zero external runtime dependencies — frameworks are peer dependencies. When you install a framework, its own dependency tree is yours. Run npm audit / pnpm audit in your application and upgrade framework versions to clear advisories in their transitive dependencies (for example, Next.js→postcss, Astro→sharp, Nuxt→brace-expansion, Remix→turbo-stream, Angular→fast-uri).
9Svelte ThemeScope reactivity
This limitation is called out above and repeated for visibility: ThemeScope in Svelte reads its props at mount. It still follows provider mode changes for family/boundary scopes via the store subscription, but an explicit theme prop change requires a remount. See the Svelte API reference.
These are the boundaries we know about. If you hit something that behaves differently across environments and isn't listed here, file an issue — it's either a bug or a limitation we should document.