Account

Chrome

Free

Border Beam

Marks the one surface that matters on a page: a plan, a prompt box, a live session. The beam follows the parent's own radius, so it fits cards, inputs and full panels without extra markup.

Install

No key needed.

Terminal
npx @galactrex/ui add border-beam

Usage

Example
import { BorderBeam } from "@/components/galactrex/border-beam";

<div className="relative rounded-xl border border-gxui-line p-6">
  <BorderBeam />
  Pro plan
</div>

Source

Installs to components/galactrex/border-beam.tsx, with the shared motion primitives beside it.

components/galactrex/border-beam.tsx
"use client";
import { useRef, type CSSProperties } from "react";
import { motion } from "motion/react";
import { cx } from "@/components/galactrex/motion/cx";
import { useElementSize } from "@/components/galactrex/motion/use-element-size";
import { useReducedMotion } from "@/components/galactrex/motion/use-reduced-motion";
import { useVisible } from "@/components/galactrex/motion/use-visible";

export type BorderBeamProps = {
  /** seconds for one lap of the border */
  duration?: number;
  /** length of the beam as a share of the lap, 0.05 to 0.5 */
  length?: number;
  /** border width in px */
  width?: number;
  /** any CSS colour for a single-colour beam; by default the tail runs through the theme spectrum */
  color?: string;
  /** run counter-clockwise */
  reverse?: boolean;
  /** seconds before the first lap */
  delay?: number;
  className?: string;
};

/** A light that travels the border of its parent. Place it inside any element with position: relative and a
    border radius; it inherits the radius and never takes pointer events. The beam is a rotating conic gradient
    seen through a ring mask, so each frame is a compositor transform, not a repaint of the border. */
export function BorderBeam({ duration = 6, length = 0.18, width = 1, color, reverse = false, delay = 0, className }: BorderBeamProps) {
  const ref = useRef<HTMLSpanElement>(null);
  const { width: w, height: h } = useElementSize(ref);
  const reduced = useReducedMotion();
  const visible = useVisible(ref);
  const side = Math.ceil(Math.hypot(w, h)) + 2;
  const share = Math.min(0.5, Math.max(0.05, length)) * 360;
  const ring: CSSProperties = {
    padding: width,
    WebkitMask: "linear-gradient(black, black) content-box exclude, linear-gradient(black, black)",
    mask: "linear-gradient(black, black) content-box exclude, linear-gradient(black, black)",
  };
  const beam: CSSProperties = {
    width: side,
    height: side,
    marginLeft: -side / 2,
    marginTop: -side / 2,
    // the head is the brightest point; without a colour the tail cools through the spectrum behind it
    background: color
      ? `conic-gradient(from 0deg, transparent 0deg, transparent ${360 - share}deg, ${color} 360deg)`
      : `conic-gradient(from 0deg, transparent 0deg, transparent ${360 - share}deg, var(--gxui-spectrum-1) ${360 - share * 0.66}deg, var(--gxui-spectrum-2) ${360 - share * 0.33}deg, var(--gxui-spectrum-3) ${360 - share * 0.08}deg, var(--gxui-glow) 360deg)`,
  };
  return (
    <span ref={ref} aria-hidden className={cx("pointer-events-none absolute inset-0 overflow-hidden rounded-[inherit]", className)} style={ring}>
      {w > 0 && (
        <motion.span
          className="absolute left-1/2 top-1/2 block will-change-transform"
          style={beam}
          initial={{ rotate: reverse ? 360 : 0 }}
          animate={reduced || !visible ? undefined : { rotate: reverse ? 0 : 360 }}
          transition={{ duration, delay, ease: "linear", repeat: Infinity }}
        />
      )}
    </span>
  );
}

Props

PropTypeDefaultDescription
durationnumber6Seconds for one lap.
lengthnumber0.18Beam length as a share of the lap, 0.05 to 0.5.
widthnumber1Border width in px.
colorstringvar(--gxui-glow)Any CSS colour.
reversebooleanfalseRun counter-clockwise.
delaynumber0Seconds before the first lap.
classNamestring—Extra classes for the outer element.

Variants

  • Single beam. The default: one light, one lap every six seconds.
  • Paired. Two beams with opposite `reverse` and half-lap `delay` for a balanced trace.

Accessibility

  • Decorative only: aria-hidden and pointer-events: none, so it never intercepts clicks or reaches assistive tech.

Motion and reduced motion

  • Rotation by transform on a single layer; no repaint of the border per frame.
  • Pauses while offscreen.
  • Reduced motion: the beam holds still at its starting point.