Theming
Dark, light, system themes and accent color customization.
The full-featured desktop starter has two persisted appearance controls: a light/dark/system mode and an independent accent color. Both are applied inside the webview through a root class or data attribute; they do not cross a custom Rust command boundary.
What ships
| Surface | File | Role |
|---|---|---|
| Appearance UI | src/routes/settings/sections/AppearanceSection.tsx | Mode buttons and accent swatches |
| Theme helpers | src/utils/theme-settings.ts | Persists preferences and applies dark / data-accent |
| Settings schema | src/lib/settings/schema.ts | Valid theme modes and ACCENT_COLORS |
| Design tokens | src/styles/globals.css | Tailwind v4 mappings, shadcn/ui variables, dark tokens, and accent overrides |
| Native launch color | src-tauri/tauri.conf.json and tauri.macos.conf.json | Fixed window background shown around webview startup |
The defaults in src/lib/settings/store.ts are dark and default. The
versioned settings store restores both at startup.
Theme modes
type ThemePreference = 'light' | 'dark' | 'system'applyThemePreference() resolves system with
window.matchMedia('(prefers-color-scheme: dark)') and toggles the root
dark class. Tailwind's custom dark variant applies to that class:
@import 'tailwindcss';
@variant dark (&:where(.dark, .dark *));
@import 'tw-animate-css';The current helper reads the OS color scheme when the preference is applied,
but does not install a media-query change listener. If the app is set to
system while it is open, an OS theme switch does not update the webview
until the preference is applied again or the app restarts. Add that listener
if live system following matters to your product.
Accent colors
ACCENT_COLORS contains default, blue, violet, green, rose, and
amber. default removes the root attribute and keeps the brand primary;
another selection sets document.documentElement.dataset.accent.
:root[data-accent='violet'] {
--primary: hsl(262 83% 58%);
--primary-foreground: hsl(0 0% 100%);
--ring: hsl(262 83% 58%);
}Accent rules override only primary, primary foreground, and focus ring. Stable semantic tokens such as background, card, muted, border, and destructive still come from the light or dark palette.
To change the product palette:
- Update the default
--primary,--primary-foreground, and--ringvalues in:rootand.dark. - Keep
ACCENT_SWATCHESinAppearanceSection.tsxvisually aligned with the CSS selectors. - When adding or removing a choice, update
ACCENT_COLORSandaccentColorSchema, the CSS selector, settings translations, and tests. - Add a settings-store migration when a removed value may already exist on user devices.
Tailwind v4 and shadcn/ui
globals.css maps semantic CSS variables into Tailwind v4 through @theme.
shadcn/ui components consume the same tokens with classes such as
bg-background, text-foreground, border-border, and bg-primary.
tw-animate-css provides the animation utilities used by generated primitives.
components.json is configured for the new-york style, React without RSC,
TypeScript, CSS variables, the neutral base color, Lucide icons, and the
@/components/ui alias. Add components from the template root:
pnpm dlx shadcn@latest add dialog dropdown-menuReview generated files before committing; they should land in
src/components/ui/ and use the existing aliases and variables.
Native window chrome
Windows and Linux use decorations: false, so the visible titlebar and window
buttons are React components and naturally follow the webview tokens. macOS
uses titleBarStyle: "Overlay" with native traffic lights managed by the OS.
There is no programmatic native-chrome theme synchronization today. The
Conveyor compatibility method windowSetTitlebarDark() is a no-op, and
backgroundColor is statically #0e0e10 in both Tauri config files. Treat that
value as a dark startup/fallback color; adjust it if your product defaults to a
light first frame.
The tray is separate from the app theme: macOS uses an OS-tinted template icon, while Windows/Linux choose light or dark tray assets from the OS color scheme and listen for OS changes.
Verify
- Switch between light, dark, and system in Settings → Appearance.
- Try every accent and check buttons, links, focus rings, and contrast.
- Restart and confirm both preferences persist.
- Inspect startup frames and titlebar controls on every target OS.
- Confirm tray art remains readable in light and dark system themes.
pnpm lint:check && pnpm typecheck && pnpm test