Border Glide
scrollxui/border-glideFreeComponent
Modern UI cards with a moving border and smooth transitions.
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/border-glide.jsonSource
1'use client';2import React, { useRef, createContext, useContext, useCallback } from 'react';3import {4 motion,5 AnimatePresence,6 PanInfo,7 useSpring,8 useMotionTemplate,9 useTransform,10} from 'motion/react';11import {12 Card,13 CardContent,14 CardHeader,15 CardFooter,16 CardTitle,17 CardDescription,18} from '@/components/ui/card';19import { cn } from '@/lib/utils';2021interface BorderGlideContextType {22 currentIndex: number;23 direction: number;24 handleDragEnd: (25 e: MouseEvent | TouchEvent | PointerEvent,26 info: PanInfo,27 ) => void;28 totalItems: number;29}3031const BorderGlideContext = createContext<BorderGlideContextType | undefined>(32 undefined,33);3435const useBorderGlideContext = () => {36 const context = useContext(BorderGlideContext);37 if (!context) {38 throw new Error('BorderGlide components must be used within BorderGlide');39 }40 return context;41};4243const MovingBorder: React.FC<{44 children: React.ReactNode;45 duration?: number;46 rx?: string;47 ry?: string;48 color?: string;49 width?: string;50 height?: string;51 opacity?: number;52}> = ({53 children,54 duration = 3000,55 rx = '1.5rem',56 ry = '1.5rem',57 color = '#3b82f6',58 width = '12rem',59 height = '0.5rem',60 opacity = 0.8,61}) => {62 const pathRef = useRef<SVGRectElement>(null);63 const animationRef = useRef<number | null>(null);64 const startTimeRef = useRef<number>(Date.now());6566 const time = useSpring(0, {67 stiffness: 100,68 damping: 20,69 mass: 0.5,70 });7172 const animate = useCallback(() => {73 const elapsed = Date.now() - startTimeRef.current;74 const speed = 1000 / duration;75 time.set(elapsed * speed);76 animationRef.current = requestAnimationFrame(animate);77 }, [time, duration]);7879 React.useLayoutEffect(() => {80 startTimeRef.current = Date.now();81 animate();82 return () => {83 if (animationRef.current) {84 cancelAnimationFrame(animationRef.current);85 }86 };87 }, [animate]);8889 const progress = useTransform(time, (val) => {90 if (!pathRef.current) return 0;91 const length = pathRef.current.getTotalLength();92 return val % length;93 });9495 const x = useTransform(progress, (val) => {96 if (!pathRef.current) return 0;97 return pathRef.current.getPointAtLength(val).x;98 });99100 const y = useTransform(progress, (val) => {101 if (!pathRef.current) return 0;102 return pathRef.current.getPointAtLength(val).y;103 });104105 const angle = useTransform(progress, (val) => {106 if (!pathRef.current) return 0;107// … 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 |
