All components
Effects
Border & Counters
BorderBeam, AnimatedGradientText, and NumberTicker in one panel.
components/ui/effects
New components
0 designs
Headers, motion, and map primitives — rendered live, not screenshots.
Source
Copy the files below into your project.
packages/ui/src/components/border-beam.tsx
import { cn } from '../lib/utils'
interface BorderBeamProps {
className?: string
size?: number
duration?: number
delay?: number
colorFrom?: string
colorTo?: string
borderWidth?: number
}
/**
* Animated conic-gradient border beam. Mount inside a `relative overflow-hidden rounded-*` container.
*/
export function BorderBeam({
className,
size = 200,
duration = 10,
delay = 0,
colorFrom = 'rgba(255,255,255,0.6)',
colorTo = 'transparent',
borderWidth = 1,
}: BorderBeamProps) {
return (
<div
aria-hidden="true"
className={cn('pointer-events-none absolute inset-0 rounded-[inherit]', className)}
style={
{
'--size': `${size}px`,
'--duration': `${duration}s`,
'--delay': `${delay}s`,
'--color-from': colorFrom,
'--color-to': colorTo,
'--border-width': `${borderWidth}px`,
} as React.CSSProperties
}
>
{/* The beam travels around the border via a mask + background trick */}
<div
className="absolute inset-0 rounded-[inherit]"
style={{
padding: borderWidth,
background: `conic-gradient(from calc(var(--angle, 0deg)), ${colorTo}, ${colorTo} 40%, ${colorFrom} 60%, ${colorTo})`,
WebkitMask: `linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)`,
WebkitMaskComposite: 'xor',
maskComposite: 'exclude',
animation: `border-beam-rotate var(--duration) linear calc(var(--delay) * -1) infinite`,
}}
/>
<style>{`
@keyframes border-beam-rotate {
from { --angle: 0deg; }
to { --angle: 360deg; }
}
@property --angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
`}</style>
</div>
)
}packages/ui/src/components/animated-gradient-text.tsx
import { cn } from '../lib/utils'
import type { ReactNode } from 'react'
interface AnimatedGradientTextProps {
children: ReactNode
className?: string
}
/**
* Pill badge with an animated conic-gradient border and shimmer fill.
*/
export function AnimatedGradientText({ children, className }: AnimatedGradientTextProps) {
return (
<div
className={cn(
'group relative mx-auto flex max-w-fit cursor-default items-center justify-center',
'rounded-full px-4 py-1.5 text-sm',
'bg-background/80 backdrop-blur-sm',
'shadow-[inset_0_-6px_10px_#ffffff1f]',
'transition-shadow duration-500 ease-out',
'hover:shadow-[inset_0_-6px_10px_#ffffff3f]',
// animated border via background-clip trick
'before:absolute before:inset-0 before:-z-10 before:rounded-full',
'before:bg-[conic-gradient(from_var(--border-angle),transparent_25%,rgba(255,255,255,0.18)_50%,transparent_75%)]',
'[--border-angle:0turn]',
'before:animate-[border-beam_4s_linear_infinite]',
className
)}
>
<span
className={cn(
'inline-flex items-center gap-1.5',
'bg-gradient-to-r from-white/90 via-white to-white/60 bg-clip-text text-transparent'
)}
>
{children}
</span>
</div>
)
}packages/ui/src/components/number-ticker.tsx
'use client'
import { useEffect, useRef } from 'react'
import { useInView, useMotionValue, useSpring, animate } from 'motion/react'
import { cn } from '../lib/utils'
interface NumberTickerProps {
value: number
/** Prefix string, e.g. "$" */
prefix?: string
/** Suffix string, e.g. "+" or "k+" */
suffix?: string
className?: string
delay?: number
decimalPlaces?: number
}
export function NumberTicker({
value,
prefix = '',
suffix = '',
className,
delay = 0,
decimalPlaces = 0,
}: NumberTickerProps) {
const ref = useRef<HTMLSpanElement>(null)
const motionValue = useMotionValue(0)
const springValue = useSpring(motionValue, { damping: 60, stiffness: 100 })
const isInView = useInView(ref, { once: true, margin: '0px' })
useEffect(() => {
if (!isInView) return
const timer = setTimeout(() => {
animate(motionValue, value, { duration: 1.2, ease: 'easeOut' })
}, delay * 1000)
return () => clearTimeout(timer)
}, [isInView, value, delay, motionValue])
useEffect(() => {
return springValue.on('change', (latest) => {
if (ref.current) {
ref.current.textContent =
prefix +
Intl.NumberFormat('en-US', {
minimumFractionDigits: decimalPlaces,
maximumFractionDigits: decimalPlaces,
}).format(Number(latest.toFixed(decimalPlaces))) +
suffix
}
})
}, [springValue, prefix, suffix, decimalPlaces])
return (
<span
ref={ref}
className={cn('inline-block tabular-nums tracking-tight', className)}
>
{prefix}0{suffix}
</span>
)
}