Toggle
Vue actionOn/off button (different from Switch — this is shaped like a button and lives in toolbars). Three sizes, two variants. Use inside Toggle Group or standalone for "press to enable" buttons like bold / italic.
Also available for React ->Installation
$ pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/toggle.json$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/toggle.json$ yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/toggle.json$ bunx shadcn-vue@latest add https://uipkge.dev/r/vue/toggle.json
Or with the named registry:
npx shadcn-vue@latest add @uipkge/toggle
Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
class | HTMLAttributes['class'] | — | optional |
variant | 'default''outline' | 'default' | optional |
size | 'default''sm''lg' | 'default' | optional |
asChild | boolean | — | optional |
as | string | object | — | optional |
defaultValue | boolean | — | optional |
modelValue | boolean | — | optional |
pressed | boolean | — | optional |
disabled | boolean | false | optional |
name | string | — | optional |
required | boolean | — | optional |
Dependencies
Used by
Files (3)
-
app/components/ui/toggle/Toggle.vue 2 kB
<script setup lang="ts"> import type { ToggleEmits } from 'reka-ui' import type { HTMLAttributes } from 'vue' import { computed, getCurrentInstance } from 'vue' import { reactiveOmit } from '@vueuse/core' import { Toggle, useForwardPropsEmits } from 'reka-ui' import { cn } from '@/lib/utils' import { toggleVariants } from './toggle.variants' // Inlined unions for variant/size: Vue's SFC compiler can't extract // runtime props from indexed-access types like `ToggleVariants['variant']`, // so we spell the unions out. cva still validates at runtime. // Same goes for `ToggleProps` from reka-ui (no exports.types condition); // we inline only the props we actually expose. const props = withDefaults( defineProps<{ class?: HTMLAttributes['class'] variant?: 'default' | 'outline' size?: 'default' | 'sm' | 'lg' asChild?: boolean as?: string | object defaultValue?: boolean modelValue?: boolean pressed?: boolean disabled?: boolean name?: string required?: boolean }>(), { variant: 'default', size: 'default', disabled: false, }, ) const emits = defineEmits<ToggleEmits>() const delegatedProps = reactiveOmit(props, 'class', 'size', 'variant', 'modelValue') const forwarded = useForwardPropsEmits(delegatedProps, emits) const instance = getCurrentInstance() const isControlled = computed(() => Boolean(instance?.vnode?.props?.['onUpdate:modelValue'])) const userPassedModelValue = computed(() => { const raw = instance?.vnode?.props return Boolean(raw && ('modelValue' in raw || 'model-value' in raw)) }) const toggleStateBindings = computed(() => { if (isControlled.value) return { 'model-value': props.modelValue } if (userPassedModelValue.value) return { 'default-value': Boolean(props.modelValue) } return { 'default-value': false } }) </script> <template> <Toggle v-slot="slotProps" data-uipkge data-slot="toggle" v-bind="{ ...forwarded, ...toggleStateBindings }" :class="cn(toggleVariants({ variant, size }), props.class)" > <slot v-bind="slotProps" /> </Toggle> </template> -
app/components/ui/toggle/toggle.variants.ts 1.6 kB
import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' /** * Variant definitions live in their own file (rather than the package * `index.ts`) so consuming Vue SFCs can import without creating a circular * dependency through the index. See card.variants.ts for the canonical * example + the SSR symptom that motivated the split. */ export const toggleVariants = cva( // Focus ring intentionally subtle: 1px ring on :focus-visible only, no // border swap. Keyboard users still get a clear focused state; mouse // users don't see a loud 3px halo after click (which lingers because // the button retains focus). Matches the segmented-control density. "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:ring-ring/50 focus-visible:ring-1 outline-none transition-colors duration-200 whitespace-nowrap", { variants: { variant: { default: 'bg-transparent', outline: 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground', }, size: { default: 'h-9 px-2 min-w-9', sm: 'h-8 px-1.5 min-w-8', lg: 'h-10 px-2.5 min-w-10', }, }, defaultVariants: { variant: 'default', size: 'default', }, }, ) export type ToggleVariants = VariantProps<typeof toggleVariants> -
app/components/ui/toggle/index.ts 0.3 kB
export { default as Toggle } from './Toggle.vue' // Re-export variant API from the sibling file (kept separate to avoid the // Component.vue <-> index.ts circular import that broke dev SSR for Card). export { toggleVariants, type ToggleVariants } from './toggle.variants'
Raw manifest: https://uipkge.dev/r/vue/toggle.json