๐ข Switch
- Full keyboard navigation.
- Can be controlled or uncontrolled.
Basic
NSwitch
is used to create a switch input. For more information about the APIs, please refer to the Radix-UI Switch documentation.
Props | Type | Default | Description |
---|---|---|---|
checked | boolean | - | The controlled state of the switch. Can be bind as v-model:checked . |
defaultChecked | boolean | - | The uncontrolled state of the switch. |
value | string | on | The value of the switch. |
Label
You can use the
NFormGroup
component to create a label for the switch.
NFormGroup
component here.Color
switch="{color}"
- change the color of the switch.
primary
. You can also add your own colors to the palette through the Configuration section.Size
size="{size}"
- change the size of the switch.
๐ You can freely adjust the size of the switch using any size imaginable. No limits exist, and you can use
breakpoints
such assm:sm, xs:lg
to change size based on screen size orstates
such ashover:lg, focus:3xl
to change size based on input state and more.
height
and width
of the switch scale depends on the switch-size
. If you want to change the height
and width
simultaneously, you can always customize it using utility classes.Icon
icon
- add an icon to the switch.
checkedIcon
- add an icon for the on state.
uncheckedIcon
- add an icon for the off state.
Disabled
disabled
- disable the switch.
Loading
loading
- set the switch to loading state.
Customization
You can customize the switch using the
una
prop and utility classes.
Slots
You can use the following slots to customize the switch.
Name | Description | Props |
---|---|---|
icon | Customizable icons for the switch in both checked and unchecked states. | checked |
loading-icon | The loading icon slot. | checked |
Props
import type { SwitchRootProps } from 'radix-vue'
import type { HTMLAttributes } from 'vue'
export interface NSwitchProps extends SwitchRootProps {
/**
* Add a loading indicator to the switch.
* This will also disable the switch.
*
* @default false
*/
loading?: boolean
/**
* Allows you to display an icon. Equivalent of setting both `onIcon` and `offIcon`.
*
* @example
* icon="i-heroicons-question-20-solid"
*/
icon?: HTMLAttributes['class']
/**
* Allows you to display an icon when the switch is on.
* Accepts icon name and utility classes
*
* @example
* on-icon="i-heroicons-check-20-solid text-white"
*/
checkedIcon?: HTMLAttributes['class']
/**
* Allows you to display an icon when the switch is off.
* Accepts icon name and utility classes
*
* @example
* off-icon="i-heroicons-x-mark-20-solid text-white"
*/
uncheckedIcon?: HTMLAttributes['class']
/**
* Allows you to change the size of the input.
*
* @default md
*
* @example
* size="sm" | size="2cm" | size="2rem" | size="2px"
*/
size?: HTMLAttributes['class']
/**
* Allows you to add `UnaUI` switch preset properties,
* Think of it as a shortcut for adding options or variants to the preset if available.
*
* @example
* switch-checked="green"
*/
switchChecked?: HTMLAttributes['class']
/**
* Allows you to add `UnaUI` switch preset properties,
* Think of it as a shortcut for adding options or variants to the preset if available.
*
* @example
* switch-unchecked="gray"
*/
switchUnchecked?: HTMLAttributes['class']
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/switch.ts
*/
una?: {
switch?: HTMLAttributes['class']
switchChecked?: HTMLAttributes['class']
switchUnchecked?: HTMLAttributes['class']
switchThumb?: HTMLAttributes['class']
switchThumbChecked?: HTMLAttributes['class']
switchThumbUnchecked?: HTMLAttributes['class']
switchIcon?: HTMLAttributes['class']
switchIconChecked?: HTMLAttributes['class']
switchIconUnchecked?: HTMLAttributes['class']
switchLoading?: HTMLAttributes['class']
switchLoadingIconName?: HTMLAttributes['class']
}
}
Presets
import type { RuleContext } from 'unocss'
import { parseColor, type Theme } from '@unocss/preset-mini'
type SwitchPrefix = 'switch'
export const staticSwitch: Record<`${SwitchPrefix}-${string}` | SwitchPrefix, string> = {
// base
'switch': 'bg-brand switch-focus h-1.25em w-2.25em switch-disabled inline-flex shrink-0 cursor-pointer items-center border-2 border-transparent rounded-full shadow-sm',
'switch-disabled': 'data-[disabled]:n-disabled',
'switch-focus': 'focus-visible:ring-brand focus-visible:ring-offset-base transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
// thumb
'switch-thumb': 'square-1em flex items-center justify-center absolute pointer-events-none block border border-base rounded-full bg-base shadow-lg ring-0 transition-transform',
'switch-thumb-checked': 'translate-x-1.01em',
'switch-thumb-unchecked': 'translate-x-0.03em',
// icon
'switch-icon': 'text-0.7em',
'switch-icon-unchecked': 'text-muted',
'switch-icon-checked': 'text-muted',
// loading
'switch-loading-icon': 'text-gray animate-spin text-0.8em',
'switch-loading-icon-name': 'i-loading',
}
export const dynamicSwitch = [
[
/^switch-checked(?:-([^-]+))?(?:-([^-]+))?$/,
([, color = 'primary'], { theme }: RuleContext<Theme>) => {
const parsedColor = parseColor(color, theme)
if ((parsedColor?.cssColor?.type === 'rgb' || parsedColor?.cssColor?.type === 'rgba') && parsedColor.cssColor.components)
return `data-[state=checked]:n-${color}-600 dark:data-[state=checked]:n-${color}-500`
},
],
[
/^switch-unchecked(?:-([^-]+))?(?:-([^-]+))?$/,
([, color = 'primary'], { theme }: RuleContext<Theme>) => {
const parsedColor = parseColor(color, theme)
if ((parsedColor?.cssColor?.type === 'rgb' || parsedColor?.cssColor?.type === 'rgba') && parsedColor.cssColor.components)
return `data-[state=unchecked]:n-${color}-200 dark:data-[state=unchecked]:n-${color}-700/58`
},
],
]
export const _switch = [
...dynamicSwitch,
staticSwitch,
]
Component
<script setup lang="ts">
import type { NSwitchProps } from '../../types'
import {
SwitchRoot,
type SwitchRootEmits,
SwitchThumb,
useForwardPropsEmits,
} from 'radix-vue'
import { cn } from '../../utils'
import NIcon from '../elements/Icon.vue'
const props = withDefaults(defineProps<NSwitchProps>(), {
size: 'md',
switchChecked: 'primary',
switchUnchecked: 'gray',
})
const emit = defineEmits<SwitchRootEmits>()
const forwarded = useForwardPropsEmits(props, emit)
</script>
<template>
<SwitchRoot
v-slot="{ checked }"
v-bind="forwarded"
:class="cn(
'peer switch',
una?.switch,
checked
? una?.switchChecked
: una?.switchUnchecked,
)"
:disabled="disabled || loading"
:switch-unchecked
:switch-checked
:size
>
<SwitchThumb
:class="cn(
'switch-thumb',
una?.switchThumb,
checked
? 'switch-thumb-checked'
: 'switch-thumb-unchecked',
checked
? una?.switchThumbChecked
: una?.switchThumbUnchecked,
)"
>
<slot v-if="!loading" name="icon" :checked>
<NIcon
:name="(checked ? checkedIcon : uncheckedIcon) || icon"
:class="cn(
'switch-icon',
una?.switchIcon,
checked
? ['switch-icon-checked', una?.switchIconChecked]
: ['switch-icon-unchecked', una?.switchIconUnchecked],
)"
/>
</slot>
<slot v-else name="loading-icon" :checked>
<NIcon
:class="cn(
'switch-icon switch-loading-icon',
una?.switchLoading,
)"
:name="una?.switchLoadingIconName || 'switch-loading-icon-name'"
/>
</slot>
</SwitchThumb>
</SwitchRoot>
</template>