{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "avatar", "type": "registry:block", "title": "Avatar", "description": "Identity avatars for people, teams, presence, and profile edits.", "author": "Wensity ", "dependencies": [ "@base-ui/react", "@tabler/icons-react", "clsx", "framer-motion", "tailwind-merge" ], "registryDependencies": [], "files": [ { "path": "registry/wensity/lib/utils.ts", "type": "registry:lib", "target": "@lib/utils.ts", "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n" }, { "path": "registry/wensity/avatar.tsx", "type": "registry:component", "target": "@components/wensity/avatar.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Avatar as BaseAvatar } from \"@base-ui/react/avatar\";\nimport {\n AnimatePresence,\n motion,\n useReducedMotion,\n} from \"framer-motion\";\nimport { IconCamera, IconUser } from \"@tabler/icons-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type AvatarSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type AvatarStatus = \"online\" | \"offline\" | \"away\" | \"busy\";\ntype AvatarImageStatus = \"idle\" | \"loading\" | \"loaded\" | \"error\";\ntype AvatarImageLoadingStatus = Exclude;\nexport type AvatarFallbackTone =\n | \"neutral\"\n | \"blue\"\n | \"red\"\n | \"violet\"\n | \"emerald\"\n | \"amber\"\n | \"rose\"\n | \"sky\";\n\nexport interface AvatarProps extends React.HTMLAttributes {\n size?: AvatarSize;\n status?: AvatarStatus | null;\n /** When true, shows a status ring even without an explicit status value. */\n showStatus?: boolean;\n /** Hover tooltip label anchored above the avatar. */\n tooltip?: React.ReactNode;\n /** Subtle hover affordance on the avatar ring. Defaults to true when `tooltip` is set. */\n interactive?: boolean;\n}\n\nexport interface AvatarImageProps\n extends Omit, \"size\"> {\n onLoadingStatusChange?: (status: AvatarImageLoadingStatus) => void;\n}\n\nexport interface AvatarFallbackProps extends React.HTMLAttributes {\n delayMs?: number;\n tone?: AvatarFallbackTone;\n}\n\nexport interface AvatarGroupItemProps {\n name: string;\n children: React.ReactElement;\n}\n\nexport interface AvatarGroupProps extends React.HTMLAttributes {\n max?: number;\n size?: AvatarSize;\n /** Reveal names on hover without moving the avatar stack. */\n interactive?: boolean;\n children: React.ReactNode;\n}\n\nexport interface AvatarUploadProps\n extends Omit, \"onChange\"> {\n size?: AvatarSize;\n src?: string | null;\n alt?: string;\n initials?: string;\n accept?: string;\n disabled?: boolean;\n onChange?: (file: File | null) => void;\n uploadLabel?: string;\n}\n\nconst EASE_OUT: [number, number, number, number] = [0.16, 1, 0.3, 1];\nconst TOOLTIP_SPRING = { type: \"spring\" as const, stiffness: 420, damping: 32, mass: 0.45 };\nconst TOOLTIP_EASE: [number, number, number, number] = [0.23, 1, 0.32, 1];\nconst HOVER_SPRING = { type: \"spring\" as const, stiffness: 380, damping: 28, mass: 0.42 };\n\nconst sizeClasses: Record = {\n xs: \"size-6 text-[10px] [&_[data-slot=avatar-status]]:size-1.5 [&_[data-slot=avatar-status]]:ring-[1.5px]\",\n sm: \"size-8 text-[11px] [&_[data-slot=avatar-status]]:size-2 [&_[data-slot=avatar-status]]:ring-2\",\n md: \"size-10 text-xs [&_[data-slot=avatar-status]]:size-2.5 [&_[data-slot=avatar-status]]:ring-2\",\n lg: \"size-12 text-sm [&_[data-slot=avatar-status]]:size-3 [&_[data-slot=avatar-status]]:ring-2\",\n xl: \"size-16 text-base [&_[data-slot=avatar-status]]:size-3.5 [&_[data-slot=avatar-status]]:ring-[2.5px]\",\n};\n\nconst iconSizes: Record = {\n xs: \"size-3\",\n sm: \"size-3.5\",\n md: \"size-4\",\n lg: \"size-5\",\n xl: \"size-6\",\n};\n\nconst groupOverlap: Record = {\n xs: \"-ml-2\",\n sm: \"-ml-2.5\",\n md: \"-ml-3\",\n lg: \"-ml-3.5\",\n xl: \"-ml-4\",\n};\n\nconst tooltipOffsets: Record = {\n xs: \"bottom-[calc(100%+6px)]\",\n sm: \"bottom-[calc(100%+7px)]\",\n md: \"bottom-[calc(100%+8px)]\",\n lg: \"bottom-[calc(100%+9px)]\",\n xl: \"bottom-[calc(100%+10px)]\",\n};\n\nconst statusColors: Record = {\n online: \"bg-[var(--primitive-success)]\",\n offline: \"bg-[color-mix(in_srgb,var(--muted-foreground)_55%,transparent)]\",\n away: \"bg-[var(--primitive-warning)]\",\n busy: \"bg-[var(--primitive-destructive)]\",\n};\n\nconst fallbackTones: Record = {\n neutral:\n \"bg-[color-mix(in_srgb,var(--foreground)_12%,var(--background))] text-[color-mix(in_srgb,var(--foreground)_82%,var(--background))]\",\n blue: \"bg-[var(--primitive-chart-1)] text-white\",\n emerald: \"bg-[var(--primitive-chart-2)] text-white\",\n amber: \"bg-[var(--primitive-chart-3)] text-white\",\n rose: \"bg-[var(--primitive-chart-4)] text-white\",\n violet: \"bg-[var(--primitive-chart-5)] text-white\",\n red: \"bg-[var(--primitive-chart-6)] text-white\",\n /** Manual alias of `blue` — excluded from AUTO_TONES / toneFromSeed. */\n sky: \"bg-[var(--primitive-chart-1)] text-white\",\n};\n\n/** Seed-hashed tones only — one bucket per chart stop, no aliases. */\nconst AUTO_TONES: AvatarFallbackTone[] = [\n \"blue\",\n \"emerald\",\n \"amber\",\n \"rose\",\n \"violet\",\n \"red\",\n];\n\nconst AVATAR_IMAGE_DISPLAY_NAME = \"AvatarImage\";\n\nconst AvatarContext = React.createContext<{\n size: AvatarSize;\n hasImage: boolean;\n imageStatus: AvatarImageStatus;\n setImageStatus: React.Dispatch>;\n}>({\n size: \"md\",\n hasImage: false,\n imageStatus: \"idle\",\n setImageStatus: () => {},\n});\n\nfunction useAvatarContext() {\n return React.useContext(AvatarContext);\n}\n\nfunction getInitials(value: string) {\n const parts = value.trim().split(/[\\s@._-]+/).filter(Boolean);\n if (parts.length === 0) return \"?\";\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n\nfunction toneFromSeed(seed: string): AvatarFallbackTone {\n let hash = 0;\n for (let index = 0; index < seed.length; index += 1) {\n hash = (hash + seed.charCodeAt(index) * (index + 1)) % AUTO_TONES.length;\n }\n return AUTO_TONES[hash] ?? \"blue\";\n}\n\nfunction isAvatarImageElement(child: React.ReactNode) {\n if (!React.isValidElement(child)) return false;\n const type = child.type as { displayName?: string };\n return type.displayName === AVATAR_IMAGE_DISPLAY_NAME;\n}\n\nfunction AvatarTooltip({\n label,\n size,\n visible,\n reduceMotion,\n}: {\n label: React.ReactNode;\n size: AvatarSize;\n visible: boolean;\n reduceMotion: boolean;\n}) {\n return (\n \n {visible ? (\n \n \n {label}\n \n \n ) : null}\n \n );\n}\n\nfunction AvatarStatusDot({\n status,\n}: {\n status: AvatarStatus;\n}) {\n return (\n \n {status === \"online\" ? (\n \n ) : null}\n \n );\n}\n\nexport const Avatar = React.forwardRef(\n (\n {\n className,\n size = \"md\",\n status = null,\n showStatus = false,\n tooltip,\n interactive,\n children,\n onFocus,\n onBlur,\n ...rest\n },\n ref,\n ) => {\n const reduceMotion = useReducedMotion() ?? false;\n const [revealed, setRevealed] = React.useState(false);\n const hasImage = React.Children.toArray(children).some(isAvatarImageElement);\n const [imageStatus, setImageStatus] = React.useState(() =>\n hasImage ? \"loading\" : \"idle\",\n );\n const resolvedStatus = showStatus && !status ? \"online\" : status;\n const isInteractive = interactive ?? Boolean(tooltip);\n const contextValue = React.useMemo(\n () => ({\n size,\n hasImage,\n imageStatus,\n setImageStatus,\n }),\n [hasImage, imageStatus, size],\n );\n\n React.useEffect(() => {\n setImageStatus((current) => {\n if (!hasImage) return \"idle\";\n return current === \"idle\" ? \"loading\" : current;\n });\n }, [hasImage]);\n\n return (\n \n setRevealed(true)}\n onMouseLeave={() => setRevealed(false)}\n onFocus={(event) => {\n setRevealed(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setRevealed(false);\n onBlur?.(event);\n }}\n {...rest}\n >\n \n \n {children}\n \n {resolvedStatus ? (\n \n ) : null}\n \n {tooltip ? (\n \n ) : null}\n \n \n );\n },\n);\nAvatar.displayName = \"Avatar\";\n\nexport const AvatarImage = React.forwardRef(\n ({ className, alt = \"\", onLoadingStatusChange, src, ...props }, ref) => {\n const { setImageStatus } = useAvatarContext();\n const reduceMotion = useReducedMotion() ?? false;\n\n return (\n {\n setImageStatus(status);\n if (status !== \"idle\") onLoadingStatusChange?.(status);\n }}\n {...props}\n />\n );\n },\n);\nAvatarImage.displayName = AVATAR_IMAGE_DISPLAY_NAME;\n\nexport const AvatarFallback = React.forwardRef(\n ({ className, delayMs = 0, tone, children }, ref) => {\n const { size } = useAvatarContext();\n const seed = typeof children === \"string\" ? children : \"\";\n const resolvedTone = tone ?? (seed ? toneFromSeed(seed) : \"neutral\");\n const content = seed ? getInitials(seed) : children;\n\n return (\n \n {content ?? }\n \n );\n },\n);\nAvatarFallback.displayName = \"AvatarFallback\";\n\nexport function AvatarGroupItem({ children }: AvatarGroupItemProps) {\n return children;\n}\nAvatarGroupItem.displayName = \"AvatarGroupItem\";\n\nfunction isAvatarGroupItem(\n child: React.ReactNode,\n): child is React.ReactElement {\n if (!React.isValidElement(child)) return false;\n const type = child.type as { displayName?: string };\n return type.displayName === \"AvatarGroupItem\";\n}\n\nfunction resolveGroupAvatar(child: React.ReactNode, interactive: boolean) {\n if (isAvatarGroupItem(child)) {\n const avatar = child.props.children;\n if (!interactive) {\n return React.cloneElement(avatar, {\n tooltip: undefined,\n interactive: false,\n });\n }\n return React.cloneElement(avatar, {\n tooltip: avatar.props.tooltip ?? child.props.name,\n interactive: avatar.props.interactive ?? true,\n });\n }\n return child;\n}\n\nexport const AvatarGroup = React.forwardRef(\n ({ className, max = 4, size = \"md\", interactive = true, children, ...props }, ref) => {\n const childArray = React.Children.toArray(children);\n const avatars = childArray.map((child) => resolveGroupAvatar(child, interactive));\n const visible = avatars.slice(0, max);\n const overflow = avatars.length - max;\n\n return (\n \n {visible.map((child, index) => (\n 0 && groupOverlap[size])}\n style={{ zIndex: visible.length - index }}\n >\n {React.isValidElement<{ size?: AvatarSize }>(child)\n ? React.cloneElement(child, { size: child.props.size ?? size })\n : child}\n \n ))}\n {overflow > 0 ? (\n
\n \n \n +{overflow}\n \n \n
\n ) : null}\n \n );\n },\n);\nAvatarGroup.displayName = \"AvatarGroup\";\n\nexport const AvatarUpload = React.forwardRef(\n (\n {\n className,\n size = \"lg\",\n src,\n alt = \"Profile photo\",\n initials,\n accept = \"image/*\",\n disabled = false,\n onChange,\n uploadLabel = \"Upload profile photo\",\n children,\n ...props\n },\n ref,\n ) => {\n const reduceMotion = useReducedMotion() ?? false;\n const inputRef = React.useRef(null);\n const localPreviewRef = React.useRef(null);\n const [preview, setPreview] = React.useState(src ?? null);\n const [hovered, setHovered] = React.useState(false);\n const revokeLocalPreview = React.useCallback(() => {\n if (!localPreviewRef.current) return;\n URL.revokeObjectURL(localPreviewRef.current);\n localPreviewRef.current = null;\n }, []);\n\n React.useEffect(() => {\n revokeLocalPreview();\n setPreview(src ?? null);\n }, [revokeLocalPreview, src]);\n\n React.useEffect(() => revokeLocalPreview, [revokeLocalPreview]);\n\n return (\n setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocus={() => setHovered(true)}\n onBlur={() => setHovered(false)}\n {...props}\n >\n \n {preview ? (\n \n ) : (\n \n {initials ?? }\n \n )}\n \n \n \n \n \n \n {\n const file = event.target.files?.[0] ?? null;\n if (file) {\n const objectUrl = URL.createObjectURL(file);\n revokeLocalPreview();\n localPreviewRef.current = objectUrl;\n setPreview(objectUrl);\n } else {\n revokeLocalPreview();\n setPreview(src ?? null);\n }\n onChange?.(file);\n }}\n />\n {children}\n \n );\n },\n);\nAvatarUpload.displayName = \"AvatarUpload\";\n\nconst AVATAR_STYLE_ID = \"wensity-avatar-styles-v2\";\n\nfunction ensureAvatarStyles() {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(AVATAR_STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = AVATAR_STYLE_ID;\n style.textContent = `\n @keyframes wensity-avatar-online-pulse {\n 0%, 100% { transform: scale(1); opacity: 0.35; }\n 50% { transform: scale(1.85); opacity: 0; }\n }\n @media (prefers-reduced-motion: reduce) {\n [data-slot=\"avatar-status\"] .motion-safe\\\\:animate-\\\\[wensity-avatar-online-pulse_2\\\\.4s_ease-in-out_infinite\\\\] {\n animation: none !important;\n }\n }\n `;\n document.head.appendChild(style);\n}\n\nif (typeof document !== \"undefined\") {\n ensureAvatarStyles();\n}\n" } ], "cssVars": { "theme": { "font-sans": "var(--font-satoshi, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif)", "font-display": "var(--font-cabinet, ui-sans-serif, system-ui, sans-serif)", "color-background": "var(--background)", "color-foreground": "var(--foreground)", "color-surface": "var(--surface)", "color-surface-muted": "var(--surface-muted)", "color-border": "var(--border)", "color-border-strong": "var(--border-strong)", "color-muted": "var(--muted)", "color-muted-foreground": "var(--muted-foreground)", "color-ring": "var(--ring)", "color-chili-50": "#fff1ee", "color-chili-100": "#ffe2db", "color-chili-200": "#ffa896", "color-chili-300": "#ff8a73", "color-chili-400": "#f15a45", "color-chili-500": "#cd1c18", "color-chili-600": "#b31614", "color-chili-700": "#9b1313", "color-chili-800": "#6a0d0e", "color-chili-900": "#38000a", "color-chili-950": "#1f0006", "color-primitive-surface-elevated": "var(--primitive-surface-elevated)", "color-primitive-surface-overlay": "var(--primitive-surface-overlay)", "color-primitive-surface-hover": "var(--primitive-surface-hover)", "color-primitive-surface-active": "var(--primitive-surface-active)", "color-primitive-surface-selected": "var(--primitive-surface-selected)", "color-primitive-border-subtle": "var(--primitive-border-subtle)", "color-primitive-ring": "var(--primitive-ring)", "color-primitive-text-secondary": "var(--primitive-text-secondary)", "color-primitive-text-placeholder": "var(--primitive-text-placeholder)", "color-primitive-text-inverse": "var(--primitive-text-inverse)", "color-primitive-destructive": "var(--primitive-destructive)", "color-primitive-destructive-hover": "var(--primitive-destructive-hover)", "color-primitive-destructive-active": "var(--primitive-destructive-active)", "color-primitive-destructive-foreground": "var(--primitive-destructive-foreground)", "color-primitive-destructive-surface": "var(--primitive-destructive-surface)", "color-primitive-destructive-border": "var(--primitive-destructive-border)", "color-primitive-success": "var(--primitive-success)", "color-primitive-warning": "var(--primitive-warning)", "color-primitive-info": "var(--primitive-info)", "color-primitive-control-solid": "var(--primitive-control-solid)", "color-primitive-control-solid-hover": "var(--primitive-control-solid-hover)", "color-primitive-control-solid-active": "var(--primitive-control-solid-active)", "color-primitive-control-solid-foreground": "var(--primitive-control-solid-foreground)", "color-primitive-chart-1": "var(--primitive-chart-1)", "color-primitive-chart-2": "var(--primitive-chart-2)", "color-primitive-chart-3": "var(--primitive-chart-3)", "color-primitive-chart-4": "var(--primitive-chart-4)", "color-primitive-chart-5": "var(--primitive-chart-5)", "color-primitive-chart-6": "var(--primitive-chart-6)", "radius-primitive": "var(--primitive-radius)", "radius-primitive-control": "var(--primitive-radius-control)", "radius-primitive-control-sm": "var(--primitive-radius-control-sm)", "radius-primitive-surface": "var(--primitive-radius-surface)", "radius-primitive-item": "var(--primitive-radius-item)", "font-primitive-sans": "var(--primitive-font-sans)", "font-primitive-display": "var(--primitive-font-display)", "font-primitive-mono": "var(--primitive-font-mono)", "spacing-primitive-control-height-sm": "var(--primitive-control-height-sm)", "spacing-primitive-control-height-md": "var(--primitive-control-height-md)", "spacing-primitive-control-height-lg": "var(--primitive-control-height-lg)" }, "light": { "background": "#fafafa", "foreground": "#0a0a0a", "surface": "#ffffff", "surface-muted": "#f4f4f5", "border": "rgba(10, 10, 10, 0.08)", "border-strong": "rgba(10, 10, 10, 0.16)", "muted": "#f4f4f5", "muted-foreground": "#52525b", "ring": "#cd1c18", "pattern-fg": "rgba(10, 10, 10, 0.07)", "llb-primary": "#18181b", "llb-primary-fg": "#ffffff", "llb-success": "#1f883d", "llb-error": "#cf222e", "primitive-surface-elevated": "#ffffff", "primitive-surface-overlay": "#ffffff", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 4%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 7%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 6%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#24292d", "primitive-control-solid-hover": "#2c3237", "primitive-control-solid-active": "#1e2326", "primitive-control-solid-foreground": "#ffffff", "primitive-chart-1": "#2a78d6", "primitive-chart-2": "#1baf7a", "primitive-chart-3": "#eda100", "primitive-chart-4": "#008300", "primitive-chart-5": "#4a3aa7", "primitive-chart-6": "#e34948", "primitive-text-inverse": "#ffffff", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#dc2626", "primitive-destructive-hover": "#b91c1c", "primitive-destructive-active": "#991b1b", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(220, 38, 38, 0.10)", "primitive-destructive-border": "rgba(220, 38, 38, 0.45)", "primitive-success": "#047857", "primitive-success-surface": "rgba(4, 120, 87, 0.10)", "primitive-success-border": "rgba(4, 120, 87, 0.45)", "primitive-warning": "#b45309", "primitive-warning-surface": "rgba(180, 83, 9, 0.10)", "primitive-warning-border": "rgba(180, 83, 9, 0.45)", "primitive-info": "#0369a1", "primitive-info-surface": "rgba(3, 105, 161, 0.10)", "primitive-info-border": "rgba(3, 105, 161, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.06), 0 8px 24px -12px rgba(0, 0, 0, 0.08)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.06), 0 18px 48px -24px rgba(0, 0, 0, 0.35)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.08), 0 24px 64px -28px rgba(0, 0, 0, 0.42)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" }, "dark": { "background": "#0a0a0b", "foreground": "#f5f5f6", "surface": "#111113", "surface-muted": "#18181b", "border": "rgba(255, 255, 255, 0.08)", "border-strong": "rgba(255, 255, 255, 0.14)", "muted": "#1c1c1f", "muted-foreground": "#a1a1aa", "ring": "#cd1c18", "pattern-fg": "rgba(255, 255, 255, 0.06)", "llb-primary": "#f5f5f6", "llb-primary-fg": "#0a0a0b", "llb-success": "#2da44e", "llb-error": "#f85149", "primitive-surface-elevated": "#0f0f10", "primitive-surface-overlay": "#141415", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 5%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 10%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 8%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#f5f3ee", "primitive-control-solid-hover": "#ffffff", "primitive-control-solid-active": "#e8e4da", "primitive-control-solid-foreground": "#151719", "primitive-chart-1": "#3987e5", "primitive-chart-2": "#199e70", "primitive-chart-3": "#c98500", "primitive-chart-4": "#008300", "primitive-chart-5": "#9085e9", "primitive-chart-6": "#e66767", "primitive-text-inverse": "#151719", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#e11d2e", "primitive-destructive-hover": "#c1121f", "primitive-destructive-active": "#a4161a", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(225, 29, 46, 0.12)", "primitive-destructive-border": "rgba(225, 29, 46, 0.52)", "primitive-success": "#34d399", "primitive-success-surface": "rgba(52, 211, 153, 0.12)", "primitive-success-border": "rgba(52, 211, 153, 0.45)", "primitive-warning": "#fbbf24", "primitive-warning-surface": "rgba(251, 191, 36, 0.12)", "primitive-warning-border": "rgba(251, 191, 36, 0.45)", "primitive-info": "#38bdf8", "primitive-info-surface": "rgba(56, 189, 248, 0.12)", "primitive-info-border": "rgba(56, 189, 248, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px -12px rgba(0, 0, 0, 0.6)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 56px -28px rgba(0, 0, 0, 0.72)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.45), 0 28px 72px -32px rgba(0, 0, 0, 0.8)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" } }, "css": { "@layer base": { ":where([data-wensity-primitive])": { "font-family": "var(--primitive-font-sans)" } }, "@utility scrollbar-hidden": { "scrollbar-width": "none", "-ms-overflow-style": "none", "&::-webkit-scrollbar": { "display": "none" } }, "@keyframes wensity-marquee-x": { "from": { "transform": "translate3d(0, 0, 0)" }, "to": { "transform": "translate3d(-50%, 0, 0)" } }, "@keyframes wensity-marquee-x-reverse": { "from": { "transform": "translate3d(-50%, 0, 0)" }, "to": { "transform": "translate3d(0, 0, 0)" } }, "@keyframes wensity-morph-rot-cw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(360deg)" } }, "@keyframes wensity-morph-rot-ccw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(-360deg)" } } }, "docs": "Free Wensity component. Installs to @components/wensity/avatar.tsx. For Pro components and updates, use pnpm dlx wensity@latest add avatar.", "categories": [ "UI Primitives", "wensity", "free" ], "meta": { "wensity": { "slug": "avatar", "access": "free", "category": "UI Primitives", "kind": "component", "componentUrl": "https://ui.wensity.com/primitives/avatar", "shadcnUrl": "https://ui.wensity.com/r/avatar", "cliInstall": "pnpm dlx wensity@latest add avatar" } } }