UI Toolkit: Theme Switcher OFFICIAL SALE

Unity 6 ships TSS (Theme Style Sheets) and USS custom properties as primitives — but no turnkey runtime theme system. There's no C# swap API for driving themes from code; no OS dark-mode detection; every swap is a hard cut with no transition; and no way to override a single token at runtime.

Drop a ThemeManager onto your UIDocument GameObject, point it at a ThemeRegistry, and `themeManager.SetTheme("dark")` swaps the entire panel — or call `themeManager.SetToken("--color-accent", Color.red)` to override a single CSS variable at runtime without touching files. The full theme-swap pipeline is awaitable, the pre-change event is cancellable for guard logic, and the override layer persists through theme swaps so user preferences survive.


Async swap API that feels native.

`await themeManager.SetThemeAsync("dark")` completes when the swap finishes — so your code reads top-to-bottom. Cancellation tokens, `BeforeThemeChange` with a `Cancel` flag for guard logic, `ThemeChanged` for reactive UI, and `ThemeTransitionComplete` for post-swap hooks. Synchronous `SetTheme(id, skipTransition)` for explicit instant swaps. `NextTheme()` / `PreviousTheme()` cycle the registry. `PushTheme("preview")` / `PopTheme()` for settings screens where the user samples a theme then commits or backs out.


Three swap strategies, picked per theme.

`StyleSheetSwap` (default) adds and removes USS files on the root. `ThemeStyleSheetSwap` assigns `panelSettings.themeStyleSheet` for global, cross-document themes (with a warning when the PanelSettings is shared across UIDocuments). `RootVariableOverride` keeps your single base USS and treats themes as pure token state. Set the strategy field on the ThemeDefinition asset and the right swap path runs automatically.


Token overrides with one-line element bindings.

`SetToken("--color-accent", Color.red)` stores a runtime override that outlives every subsequent `SetTheme` call. To reflect the value on a specific element, wire it once with `BindColorToken`:

```

themeManager.BindColorToken(myButton, "--color-accent",

(el, c) => el.style.backgroundColor = c);

```

The binding applies the current value immediately, re-applies on every override or theme change, returns an `IDisposable` for explicit cleanup, and auto-unsubscribes when the element detaches from its panel. `BindFloatToken` ships the same surface for numeric tokens. For multi-token or derived-value scenarios, drop down to the raw `TokenChanged` event.

`TryGetToken` reads with override → theme → parent-chain precedence. `ClearTokenOverride` / `ClearAllTokenOverrides` remove. Color and float types both supported.


Theme inheritance.

Declare a base theme with shared tokens and a dark variant that only overrides what changes. The resolver walks the parent chain (cycle-detected, max depth 16) for every token read. Stop repeating yourself across every theme variant.


Lifecycle hooks for every swap.

Three events fire in order on every theme swap: `BeforeThemeChange` (cancellable — set `ctx.Cancel = true` to abort), `ThemeChanged` (immediately after stylesheets and overrides apply), and `ThemeTransitionComplete` (after the registered animator finishes, or immediately when no animator is registered). Per-manager transition duration; skip-transition flag on every swap call when you need an explicit instant cut.


Persistence built in.

`PlayerPrefsThemeStorage` (configurable key) is the default — your user's choice persists across runs without any extra setup. `InMemoryThemeStorage` for no-persist mode. A `Custom` mode and the `IThemeStorage` interface let you wire Cloud Save, a JSON file, or an encrypted backend with a single property assignment.


OS dark-mode auto-follow.

Windows and macOS providers ship out of the box. `themeManager.SystemProvider = new WindowsThemeSystemProvider()` (or `MacOSThemeSystemProvider`) plus `themeManager.Mode = ThemeMode.Auto` and your panel automatically follows the user's OS-level light/dark preference. Both providers poll for changes (2s default), clean up cleanly when disposed, and degrade to manual mode on unsupported platforms. Implement `IThemeSystemProvider` for iOS, Android, Linux, or any other platform.


Animated crossfades via UI Toolkit: Tween Engine.

For smooth color crossfades between themes, install UI Toolkit: Tween Engine and enable the integration from Tools > KrookedLilly > Tween Engine Setup (tick the Theme Switcher row and click Apply Changes). The integration registers a token interpolator that runs every time you call `SetTheme` / `SetThemeAsync`, writing per-frame interpolated values into the override store for elements wired via `BindColorToken`. Mid-flight swaps cancel cleanly and redirect smoothly toward the new target — clicking Toggle three times in a row produces a continuous animation, not a stutter. Without Tween Engine, theme swaps complete instantly.


Locale-aware root classes.

`SetLocale("en", rtl: false)` writes `locale-en` and `locale-rtl` / `locale-ltr` classes to the document root. Author per-locale USS rules with zero code branches: `.locale-ja .demo-title { font-size: 18px; }`, `.locale-rtl .nav-row { flex-direction: row-reverse; }`. Subscribe to Unity Localization's `SelectedLocaleChanged` event and forward the new locale code to `SetLocale` — full recipe in the included docs.


Three demo scenes included.

Basic Toggle — `Toggle`, `Next`, `Previous` buttons walk three sample themes (light, dark, high-contrast) with a live "Active theme: X" label; install Tween Engine to see the visible crossfade between themes. Palette Token — three RGB sliders drive an override into `--color-accent`; watch the swatch and accent-styled buttons update live as you drag; "Clear Override" restores the theme's accent; "Toggle Theme" proves the override outlives the swap. System Theme — "Follow OS theme" toggle binds the Windows or macOS provider to the manager and flips between `Manual` and `Auto`; readout labels show the detected OS preference and the active theme. All three ship with controller scripts, UXML, USS, sample Light + Dark + High Contrast themes, and a configured DemoPanelSettings (Scale With Screen Size, 1920×1080, Match=0).


Full C# source, no DLLs.

XML documentation on every public API. Three custom inspectors (manager + registry + theme definition), an editor window for live theme preview against the active manager, and a quick-create that stamps out a Light + Dark + Registry triplet ready to fill in. Works with both Unity's Legacy Input Manager and the new Input System package. Zero external dependencies.