Skip to content
info

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 ThemeScope use the fallback path.
  • If prefers-reduced-motion: reduce is 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.innerWidth before 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 createThemeBootstrapScript for zero-flash in those frameworks.
  • Angular ships an esm + default export map (ng-packagr layout) rather than the standard import/require conditions; Node require() of the Angular package is not supported.
  • The ThemeScrollbar SSR 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

  • SvelteThemeScope reads theme / family / mode props 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 provideThemeKit integration; 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.

Anything else?

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.

Known Limitations — Theme Kit