Icon Box
icon-boxuiIcon container primitive with calibrated sizes (2xs to xl), surface variants (solid, outline, subtle, primary, muted), and 2.5D layered IconStack for empty states and feature hero cards.
Installation
$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/icon-box.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/icon-box.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/icon-box.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/icon-box.jsonNamed registry:
npx shadcn-vue@latest add @uipkge/icon-boxInstalls to:app/components/ui/icon-box/Examples
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
icon | Component | — | optional |
variant | 'primary''muted''outline''solid''subtle''destructive''success''warning''ghost''custom' | 'primary' | optional |
shape | 'rounded''circle''square' | 'rounded' | optional |
size | '2xs''xs''sm''md''lg''xl' | 'md' | optional |
class | string | — | optional |
iconClass | string | — | optional |
Used by
Files installed (3)
app/components/ui/icon-box/IconBox.vue2 kB
<script setup lang="ts"> import type { Component } from 'vue' import { cn } from '@/lib/utils' type Variant = 'primary' | 'muted' | 'outline' | 'solid' | 'subtle' | 'destructive' | 'success' | 'warning' | 'ghost' | 'custom' type Size = '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' type Shape = 'rounded' | 'circle' | 'square' const props = withDefaults( defineProps<{ icon?: Component variant?: Variant shape?: Shape size?: Size class?: string iconClass?: string }>(), { variant: 'primary', shape: 'rounded', size: 'md', }, ) const variantClasses: Record<Variant, string> = { primary: 'bg-primary/10 text-primary', muted: 'bg-muted text-muted-foreground', outline: 'border border-border bg-background text-foreground shadow-xs', solid: 'bg-foreground text-background shadow-xs', subtle: 'bg-accent text-accent-foreground', destructive: 'bg-destructive/10 text-destructive', success: 'bg-success/15 text-success', warning: 'bg-warning/15 text-warning', ghost: 'text-muted-foreground hover:bg-accent hover:text-foreground', custom: '', } const shapeClasses: Record<Shape, string> = { rounded: 'rounded-lg', circle: 'rounded-full', square: 'rounded-none', } const sizeClasses: Record<Size, string> = { '2xs': 'size-6 p-1', xs: 'size-7 p-1.5', sm: 'size-8 p-1.5', md: 'size-9 p-2', lg: 'size-11 p-2.5', xl: 'size-14 p-3.5', } const iconSizes: Record<Size, string> = { '2xs': 'size-3', xs: 'size-3.5', sm: 'size-4', md: 'size-4.5', lg: 'size-6', xl: 'size-7', } </script> <template> <div data-uipkge data-slot="icon-box" :data-variant="variant" :data-size="size" :data-shape="shape" :class=" cn( 'inline-flex shrink-0 items-center justify-center transition-colors', variantClasses[variant], shapeClasses[shape], sizeClasses[size], props.class, ) " > <slot> <component :is="icon" v-if="icon" :class="cn(iconSizes[size], props.iconClass)" aria-hidden="true" /> </slot> </div> </template>app/components/ui/icon-box/IconStack.vue2.6 kB
<script setup lang="ts"> import type { Component, HTMLAttributes } from 'vue' import { cn } from '@/lib/utils' type Variant = 'primary' | 'muted' | 'destructive' | 'success' | 'warning' type Size = 'sm' | 'md' | 'lg' | 'xl' interface Props { icon?: Component variant?: Variant size?: Size class?: HTMLAttributes['class'] iconClass?: HTMLAttributes['class'] } const props = withDefaults(defineProps<Props>(), { variant: 'primary', size: 'md', }) const sizeMap: Record<Size, { root: string; base: string; icon: string }> = { sm: { root: 'size-10', base: 'size-8 rounded-lg', icon: 'size-4' }, md: { root: 'size-14', base: 'size-11 rounded-xl', icon: 'size-5' }, lg: { root: 'size-18', base: 'size-14 rounded-2xl', icon: 'size-7' }, xl: { root: 'size-24', base: 'size-18 rounded-3xl', icon: 'size-9' }, } const variantMap: Record<Variant, { back: string; front: string; text: string }> = { primary: { back: 'bg-primary/20 border-primary/30', front: 'bg-background border-primary/20 shadow-primary/10', text: 'text-primary', }, muted: { back: 'bg-muted border-border', front: 'bg-card border-border shadow-black/5', text: 'text-muted-foreground', }, destructive: { back: 'bg-destructive/20 border-destructive/30', front: 'bg-background border-destructive/20 shadow-destructive/10', text: 'text-destructive', }, success: { back: 'bg-success/20 border-success/30', front: 'bg-background border-success/20 shadow-success/10', text: 'text-success', }, warning: { back: 'bg-warning/20 border-warning/30', front: 'bg-background border-warning/20 shadow-warning/10', text: 'text-warning', }, } </script> <template> <div data-uipkge data-slot="icon-stack" :data-variant="variant" :data-size="size" :class="cn('relative inline-flex items-center justify-center select-none', sizeMap[size].root, props.class)" > <!-- Background offset sheet --> <div aria-hidden="true" :class=" cn( 'absolute inset-0 m-auto rotate-6 border transition-transform duration-300', sizeMap[size].base, variantMap[variant].back, ) " /> <!-- Top elevated card --> <div :class=" cn( 'relative z-10 flex items-center justify-center border shadow-md transition-transform duration-300 group-hover:-translate-y-0.5', sizeMap[size].base, variantMap[variant].front, variantMap[variant].text, ) " > <slot> <component :is="icon" v-if="icon" :class="cn(sizeMap[size].icon, props.iconClass)" aria-hidden="true" /> </slot> </div> </div> </template>app/components/ui/icon-box/index.ts0.1 kB
export { default as IconBox } from './IconBox.vue' export { default as IconStack } from './IconStack.vue'
Raw manifest:https://uipkge.dev/r/vue/icon-box.json