Skip to content

Animation & Transition

@theme-kit/core — transition pipeline

Theme Kit owns the entire visual update for a theme change — diff what actually changed, plan which properties may animate, and run one coordinated, lag-free transition. Enable it with a one-line transition prop and pick a preset; nothing else is required.

Pick your framework

1Enable it

One prop on the provider — pick a preset, done.
tsx
1import { ThemeProvider } from "@theme-kit/react";
2
3<ThemeProvider
4  themes={themes}
5  defaultTheme="theme-kit-default-light"
6  transition={{ enabled: true, preset: "smooth" }}
7>
8  <YourApp />
9</ThemeProvider>

Same prop on every framework

React / Next / Svelte / Solid / Remix / Astrotransition={{ enabled: true, preset: "smooth" }}
Vue / Nuxt:transition="{ enabled: true, preset: 'smooth' }"
AngularprovideThemeKit({ transition: { enabled: true, preset: "smooth" } })
Web Components<theme-kit-provider transition='{"enabled":true,"preset":"smooth"}'></theme-kit-provider>
Vanilla JScreateThemeRuntime({ transition: { enabled: true, preset: "smooth" } })
Tailwind@import "@theme-kit/tailwind"

2Enable transitions in your framework

Step-by-step examples for every supported framework.

Pass the same transition object to your provider (or runtime). Every framework exposes it with the same shape — only the binding syntax changes.

tsx
1import { ThemeProvider, ThemeScope } from "@theme-kit/react";
2import { themes } from "./themes";
3
4export function App() {
5  return (
6    <ThemeProvider
7      themes={themes}
8      defaultTheme="theme-kit-default-light"
9      transition={{ enabled: true, preset: "smooth" }}
10    >
11      <YourApp />
12
13      {/* Nested region with its own theme + transition */}
14      <ThemeScope
15        theme="plum-dark"
16        transition={{ enabled: true, duration: 300, easing: "ease" }}
17      >
18        <Widget />
19      </ThemeScope>
20    </ThemeProvider>
21  );
22}
Step-by-step:
  1. Install the framework package (@theme-kit/react, @theme-kit/vue, etc.).
  2. Register your themes with the provider or runtime.
  3. Enable transitions by adding the transition prop (:transition in Vue/Svelte/Nuxt, attribute JSON in Web Components).
  4. Choose a preset — "smooth" works for most sites.
  5. Scope a nested region with ThemeScope (themeKitScope in Angular,theme-kit-scope in Web Components) for independent subtrees.

3View Transitions API

The cleanest cross-fade — no white intermediates, ever.
ts
1import { createThemeRuntime } from "@theme-kit/core";
2
3const runtime = createThemeRuntime({
4  themes,
5  transition: {
6    enabled: true,
7    useViewTransition: true,
8    duration: 300,
9  },
10});
With useViewTransition: true the new theme is painted beneath a fading snapshot of the old one — the opposite of interpolating directly from near-white colors — so light→dark switches never wash through white. Browsers without the API (Firefox/Safari) fall back to the inherited --theme-color-* interpolation automatically.

4Things to avoid

Theme colors animate through the provider — adding your own color transition on top re-eases the same value and lags. Everything else (hover, transform, opacity, shadow) is yours to animate freely.

Theme transitions are owned by theme-kit. The provider's transition engine (configured via the transition prop on ThemeProvider or createThemeRuntime) interpolates the inherited --theme-color-* variables every time the active theme changes — automatically, with no extra CSS from you. This is how every theme switch on this docs site animates smoothly, and it works the same way in your app. You don't need to (and should not) add your own color transition.

If you do add one — a transition-colors utility class in Tailwind, or a plain transition: background-color 0.3s in native CSS — that element re-eases the same value again, on top of theme-kit's own interpolation. The result is a double-eased, laggy switch. Leave color transitions to the library, in both Tailwind and hand-written CSS.

ts
1/* ── Tailwind ─────────────────────────────────────────────────────── */
2/* ✗ DON'T — animating theme tokens via Tailwind utility classes     */
3.btn {
4  @apply transition-colors;            /* ← re-eases theme colors → lag */
5}
6.card {
7  @apply transition-all;               /* ← catches managed properties too */
8}
9
10/* ✓ DO — animate non-theme properties with Tailwind                  */
11.btn {
12  @apply transition-transform duration-200;
13}
14.btn:hover {
15  @apply -translate-y-0.5;             /* hover lift — fine */
16}
17
18/* ── Native CSS ───────────────────────────────────────────────────── */
19/* ✗ DON'T — color transition on themed elements                      */
20button {
21  transition: background-color 0.3s;   /* ← re-eases inherited var → lag */
22}
23* {
24  transition: color 0.2s, background-color 0.2s; /* ← catches everything */
25}
26
27/* ✓ DO — animate properties the theme doesn't manage                 */
28.toast {
29  transition: opacity 0.2s ease, transform 0.2s ease;
30}
31.card {
32  transition: box-shadow 0.2s ease, transform 0.2s ease;
33}
DO
  • Tailwind: use transition-transform / transition-opacity with hover:scale, hover:translate, hover:shadow.
  • Native CSS: write transition: transform 0.2s ease, opacity 0.2s, box-shadow 0.2s on toasts, overlays, and cards — none of these are theme-managed.
  • Both: animate outline / outline-offset for focus rings.
DON'T
  • Tailwind: don't add transition-colors to buttons, cards, or links whose colors are theme tokens — the provider already animates those colors on theme change. Avoid transition-all too, which catches the managed properties.
  • Native CSS: don't write a transition for color, background-color, border-color on :root, *, or any themed element — theme-kit handles that interpolation for you.

5API Reference

Exports from the transition and animation modules.
ExportModuleDescription
ThemeTransitionOptionstransitionInterface for configuring transitions — enabled, duration, easing, useViewTransition, preset, properties.
TransitionPresettransition"smooth" | "subtle" | "instant" | "custom" | string[] — how the transition is filtered.
TRANSITION_PRESETStransitionThe curated property lists behind the smooth / subtle / instant presets.
DEFAULT_TRANSITION_PRESETtransitionThe default preset — "smooth".
DEFAULT_THEME_TRANSITIONtransitionBuilt-in defaults: enabled, 300ms, cubic-bezier(0.4, 0, 0.2, 1), smooth property list.
createThemeDiffanimationCompare two variable maps and report which token groups changed.
createTransitionPlananimationTurn a ThemeDiff into a TransitionPlan; null when nothing can or should animate.
scanForTransitionanimationTreeWalker scan for elements whose computed style uses a planned property.
runThemeAnimationanimationRun the attach → flush → swap → cleanup sequence for a theme change.
cancelThemeAnimationanimationAbort an in-flight theme animation and remove its temporary styles.
ThemeDiff / TransitionPlananimationTypes describing what changed and how to animate it.
createAnimationsPlugin@theme-kit/coreOfficial plugin — attaches a CSS transition to <html> (options.transition) via onBeforeThemeChange. A lightweight, manual alternative to the built-in runtime pipeline.
runtime.store.set(theme, { suppressTransition: true })@theme-kit/coreThe runtime-level toggle. Transitions are configured once at runtime creation; this per-update option forces an instant, non-animated apply for a single switch.
Animation & Transition — Theme Kit