
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
toggleuiOn/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 ->$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.jsonnpx shadcn-vue@latest add @uipkge/toggleInstalls to:app/components/ui/toggle/| 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 |
<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>
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>
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