SwThemeSwitch
SwThemeSwitch is the built-in theme switcher: driven by useTheme(), clicking cycles between "follow system / light / dark".
Self-gating: the component renders the toggle button only when swift.config.json's theme.mode is 'toggle'; in the system / dark / light modes (which have no manual-switch semantics) it renders nothing. So you just drop <SwThemeSwitch /> into your template — change the theme.mode config and the switcher appears / disappears automatically, no conditional needed.
The switching shape follows theme.toggleStates:
- 3-state (default): the icon follows
theme, and clicking cycles auto → light → dark → auto, so you can return to "follow system". - 2-state: the icon follows
resolvedTheme(the actual current light/dark), and clicking only toggles light ↔ dark; once switched you can't return to follow-system.
Three customization tiers (headless escape hatch)
From lightest to heaviest:
- Reskin — the
--sw-theme-switch-*CSS tokens (size / color / background / radius / hover). - Swap icons — the
#light/#dark/#autonamed slots take icons from your own icon library (only apply to the default single-button UI). - Replace the whole interaction — the default scoped slot: once provided, you self-render the entire UI, and the component hands you
{ theme, resolvedTheme, mode, toggleStates, toggleTheme, setTheme, canSwitch }while still reusing the self-gate (renders nothing whenmode !== 'toggle', so you don't have to check yourself).
If you'd rather build entirely without this component, that works too:
useTheme()is a public composable you can read/write the theme with directly.
Basic usage
Default switcher (this docs site is in toggle mode, clicking actually switches the theme)
<template>
<!-- Renders only when theme.mode: 'toggle'; empty in other modes -->
<SwThemeSwitch />
</template>
Swap icons (named slots)
#light / #dark / #auto override the default inline SVGs with icons from your icon library (scope { theme, resolved }):
Replace default icons with @phosphor-icons/vue
<script setup lang="ts">
import { PhSun, PhMoon, PhCircleHalf } from '@phosphor-icons/vue'
</script>
<template>
<SwThemeSwitch>
<template #light><PhSun :size="18" /></template>
<template #dark><PhMoon :size="18" /></template>
<template #auto><PhCircleHalf :size="18" /></template>
</SwThemeSwitch>
</template>
Self-render the whole UI (default scoped slot)
Providing the default scoped slot takes over the entire switcher UI while reusing the framework's data + actions + self-gate. Here three buttons form a "follow system / light / dark" single choice:
Self-render via the default scoped slot (setTheme single choice)
<template>
<SwThemeSwitch v-slot="{ theme, resolvedTheme, setTheme }">
<SwButton :class="{ 'is-active': theme === 'auto' }" @click="setTheme('auto')">Follow system</SwButton>
<SwButton :class="{ 'is-active': theme === 'light' }" @click="setTheme('light')">Light</SwButton>
<SwButton :class="{ 'is-active': theme === 'dark' }" @click="setTheme('dark')">Dark</SwButton>
<span>Resolved: {{ resolvedTheme }}</span>
</SwThemeSwitch>
</template>
Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
size | number | 18 | Default inline icon size (px). Has no effect when self-rendering via the default scoped slot. ⚠️ Unlike SwTable's size (a 'default' | 'compact' density enum), this one is a pixel number |
Slots
| Name | Scope | Description |
|---|---|---|
default | { theme, resolvedTheme, mode, toggleStates, toggleTheme, setTheme, canSwitch } | Self-render the entire switcher UI; takes over when provided, still gated by mode: 'toggle' |
light | { theme, resolved } | Light-state icon (overrides the default sun; default UI only) |
dark | { theme, resolved } | Dark-state icon (overrides the default moon; default UI only) |
auto | { theme, resolved } | Follow-system icon (overrides the default half-circle; 3-state + default UI only) |
Skin tokens
| Token | Purpose |
|---|---|
--sw-theme-switch-size | Button width/height |
--sw-theme-switch-color / --sw-theme-switch-hover-color | Icon color / hover icon color |
--sw-theme-switch-bg / --sw-theme-switch-hover-bg | Background / hover background |
--sw-theme-switch-radius | Corner radius |
Caveats
- Self-gate controls visibility: the component renders nothing when
theme.modeis nottoggle— to let users switch themes, settheme.mode: 'toggle'inswift.config.json(see Theme system). sizeonly affects the default inline icons: when using the icon slots or self-rendering via the default scoped slot, the size is up to your own content.- No emitted events: switching is driven by the internal
toggleTheme; to react to theme changes, watchuseTheme()'stheme/resolvedThemedirectly.