Facescape
scrollxui/facescapeFreeComponent
Interactive, animated user avatars with hover effects and responsive layout.
Live preview
live · rendered by the registry
Rendered by scrollxui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://scrollxui.dev/registry/facescape.jsonSource
1'use client';2import React, { useState, useEffect, useRef } from 'react';3import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';4import {5 Tooltip,6 TooltipContent,7 TooltipTrigger,8 TooltipProvider,9} from '@/components/ui/tooltip';10import { cn } from '@/lib/utils';1112export interface AvatarData {13 src: string;14 alt: string;15 fallback: string;16 name: string;17}1819export interface FacescapeProps {20 avatars: AvatarData[];21 className?: string;22 colorDuration?: number;23 variant?: 'circle' | 'square' | 'squircle';24}2526const BREAKPOINTS = { sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1536 };2728const useBreakpoint = (breakpoint: 'sm' | 'md' | 'lg' | 'xl' | '2xl') => {29 const [isBelow, setIsBelow] = useState(false);30 useEffect(() => {31 const handleResize = () =>32 setIsBelow(window.innerWidth < BREAKPOINTS[breakpoint]);33 handleResize();34 window.addEventListener('resize', handleResize);35 return () => window.removeEventListener('resize', handleResize);36 }, [breakpoint]);37 return isBelow;38};3940const FacescapeItem = React.forwardRef<41 HTMLDivElement,42 {43 className?: string;44 src: string;45 alt: string;46 fallback: string;47 name: string;48 colorDuration?: number;49 autoAnimate?: boolean;50 variant?: 'circle' | 'square' | 'squircle';51 }52>(53 (54 {55 className,56 src,57 alt,58 fallback,59 name,60 colorDuration = 3000,61 autoAnimate = false,62 variant = 'squircle',63 ...props64 },65 ref,66 ) => {67 const [isHovered, setIsHovered] = useState(false);68 const [isColorful, setIsColorful] = useState(false);69 const [isLarge, setIsLarge] = useState(false);70 const [isVisible, setIsVisible] = useState(false);71 const itemRef = useRef<HTMLDivElement>(null);7273 useEffect(() => {74 if (ref) {75 if (typeof ref === 'function') {76 ref(itemRef.current);77 } else {78 ref.current = itemRef.current;79 }80 }81 }, [ref]);8283 useEffect(() => {84 if (!autoAnimate || !itemRef.current) return;85 const observer = new IntersectionObserver(86 ([entry]) => setIsVisible(entry.isIntersecting),87 { threshold: 0.3 },88 );89 observer.observe(itemRef.current);90 return () => observer.disconnect();91 }, [autoAnimate]);9293 const active = autoAnimate ? isVisible : isHovered;9495 useEffect(() => {96 let colorTimeout: NodeJS.Timeout | undefined;97 let sizeTimeout: NodeJS.Timeout | undefined;98// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from scrollxui
All 180 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | scrollxui | Components | Free | 4 deps | |
| Alert Dialogalert-dialog | scrollxui | Components | Free | 7 deps | |
| Animated Buttonanimated-button | scrollxui | Components | Free | 4 deps | |
| Animated Tabsanimated-tabs | scrollxui | Components | Free | 1 dep | |
| Animated Testimonialsanimated-testimonials | scrollxui | Components | Free | 2 deps | |
| Animated TextGenerateanimated-textgenerate | scrollxui | Components | Free | 3 deps | |
| Announcementannouncement | scrollxui | Components | Free | 5 deps | |
| Aspect Ratioaspect-ratio | scrollxui | Components | Free | 1 dep |
