Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
117 lines
2.9 KiB
TypeScript
117 lines
2.9 KiB
TypeScript
"use client"
|
||
|
||
import { motion, useReducedMotion, type Transition } from "motion/react"
|
||
|
||
import { cn } from "@/lib/utils"
|
||
|
||
interface BorderBeamProps {
|
||
/**
|
||
* Approximate length of the visible beam. Mapped to conic spread.
|
||
*/
|
||
size?: number
|
||
/**
|
||
* The duration of one full loop (seconds).
|
||
*/
|
||
duration?: number
|
||
/**
|
||
* The delay of the border beam.
|
||
*/
|
||
delay?: number
|
||
/**
|
||
* The color of the border beam from.
|
||
*/
|
||
colorFrom?: string
|
||
/**
|
||
* The color of the border beam to.
|
||
*/
|
||
colorTo?: string
|
||
/**
|
||
* The motion transition of the border beam.
|
||
*/
|
||
transition?: Transition
|
||
/**
|
||
* The class name of the border beam.
|
||
*/
|
||
className?: string
|
||
/**
|
||
* The style of the border beam.
|
||
*/
|
||
style?: React.CSSProperties
|
||
/**
|
||
* Whether to reverse the animation direction.
|
||
*/
|
||
reverse?: boolean
|
||
/**
|
||
* The initial offset position (0-100).
|
||
*/
|
||
initialOffset?: number
|
||
/**
|
||
* The border width of the beam.
|
||
*/
|
||
borderWidth?: number
|
||
}
|
||
|
||
/**
|
||
* Soft beam riding the parent border via a masked rotating conic-gradient.
|
||
* Avoids offset-path corner breaks while staying flush with rounded frames.
|
||
*/
|
||
export const BorderBeam = ({
|
||
className,
|
||
size = 50,
|
||
delay = 0,
|
||
duration = 6,
|
||
colorFrom = "#29A19C",
|
||
colorTo = "#044343",
|
||
transition,
|
||
style,
|
||
reverse = false,
|
||
initialOffset = 0,
|
||
borderWidth = 1,
|
||
}: BorderBeamProps) => {
|
||
const reduceMotion = useReducedMotion()
|
||
// Map legacy px "size" → angular span (deg). Clamped so the beam stays a short arc.
|
||
const spread = Math.min(48, Math.max(12, size * 0.18))
|
||
const startAngle = initialOffset * 3.6
|
||
|
||
return (
|
||
<div
|
||
className="pointer-events-none absolute overflow-hidden rounded-[inherit] border-(length:--border-beam-width) border-transparent [mask-image:linear-gradient(transparent,transparent),linear-gradient(#000,#000)] [mask-composite:intersect] [mask-clip:padding-box,border-box]"
|
||
style={
|
||
{
|
||
// Overlay the parent’s border (e.g. .media-frame 1px).
|
||
inset: `-${borderWidth}px`,
|
||
"--border-beam-width": `${borderWidth}px`,
|
||
} as React.CSSProperties
|
||
}
|
||
>
|
||
<motion.div
|
||
className={cn(
|
||
"absolute top-1/2 left-1/2 aspect-square w-[max(200%,200vw)]",
|
||
className
|
||
)}
|
||
style={
|
||
{
|
||
x: "-50%",
|
||
y: "-50%",
|
||
background: `conic-gradient(from ${startAngle}deg, transparent 0deg, ${colorFrom} ${spread * 0.35}deg, ${colorTo} ${spread}deg, transparent ${spread * 1.65}deg)`,
|
||
...style,
|
||
} as React.CSSProperties
|
||
}
|
||
initial={false}
|
||
animate={
|
||
reduceMotion
|
||
? { rotate: 0 }
|
||
: { rotate: reverse ? -360 : 360 }
|
||
}
|
||
transition={{
|
||
repeat: Infinity,
|
||
ease: "linear",
|
||
duration,
|
||
delay: -delay,
|
||
...transition,
|
||
}}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|