# Border Beam

> A beam of light that travels around any element's border.

Category: Effects. Docs: https://get-neonkit.vercel.app/components/border-beam

## Installation

```bash
npx shadcn@latest add https://get-neonkit.vercel.app/r/border-beam.json
```

With the `@neonkit` namespace configured: `npx shadcn@latest add @neonkit/border-beam`.

npm dependencies: none. Also needs `lib/utils.ts` (`cn`) and `lib/neon.ts` (`neonVars`), which the CLI adds.

## Usage

```tsx
import { BorderBeam } from "@/components/ui/border-beam"

<BorderBeam colorTo="magenta" className="rounded-2xl border border-white/10 p-6">
  Anything
</BorderBeam>
```

## Examples

### Card

```tsx
import { BorderBeam } from "@/components/ui/border-beam";

export default function BorderBeamDemo() {
  return (
    <BorderBeam colorTo="magenta" className="w-full max-w-sm rounded-2xl border border-white/10 bg-white/[0.02]">
      <div className="space-y-4 p-6">
        <div className="flex items-center justify-between">
          <p className="text-sm text-white/55">Build #2841</p>
          <span className="font-mono text-xs text-white/40">main · 4f2a91c</span>
        </div>
        <p className="text-lg font-medium text-white/90">Compiling 214 modules</p>
        <div className="h-1 overflow-hidden rounded-full bg-white/10">
          <div className="h-full w-2/3 rounded-full bg-[rgb(var(--neon-accent,34_211_238))]" />
        </div>
        <p className="text-xs text-white/40">Started 38s ago by @aadi</p>
      </div>
    </BorderBeam>
  );
}
```

### Around a button

The beam takes its radius from the wrapper, so round it to match.

```tsx
import { ArrowRight } from "lucide-react";
import { BorderBeam } from "@/components/ui/border-beam";

export default function BorderBeamButton() {
  return (
    <BorderBeam color="lime" duration={3} size={120} className="rounded-full">
      <a
        href="#"
        className="inline-flex h-11 items-center gap-2 rounded-full border border-white/10 bg-[#0a0b11] px-5 text-sm font-medium text-white/90 transition-colors hover:text-white"
      >
        Claim your handle <ArrowRight className="size-4" />
      </a>
    </BorderBeam>
  );
}
```

### Speed, length, direction

```tsx
import { BorderBeam } from "@/components/ui/border-beam";

export default function BorderBeamSettings() {
  return (
    <div className="grid w-full max-w-lg gap-4 sm:grid-cols-2">
      <BorderBeam color="cyan" duration={8} className="rounded-xl border border-white/10 p-5">
        <p className="text-xs uppercase tracking-[0.2em] text-white/40">Slow</p>
        <p className="mt-2 text-sm text-white/80">duration 8 · size 90</p>
      </BorderBeam>
      <BorderBeam color="pink" colorTo="violet" duration={2.5} size={160} reverse borderWidth={2} className="rounded-xl border border-white/10 p-5">
        <p className="text-xs uppercase tracking-[0.2em] text-white/40">Fast, reversed</p>
        <p className="mt-2 text-sm text-white/80">duration 2.5 · size 160 · 2px</p>
      </BorderBeam>
    </div>
  );
}
```

## API reference

### BorderBeam

A div wrapper. Give it the same rounding as your content.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `NeonColor` | `inherited` | Head of the beam. |
| `colorTo` | `NeonColor` | `color` | Tail of the beam. |
| `duration` | `number` | `6` | Seconds per lap. |
| `size` | `number` | `90` | Beam length in degrees. |
| `borderWidth` | `number` | `1` | Thickness in pixels. |
| `reverse` | `boolean` | `false` | Run counter-clockwise. |

## Accessibility

- The beam is decorative and hidden from assistive tech.
- Uses CSS @property, supported in every current browser. With reduced motion the beam holds still.

## Source

`components/ui/border-beam.tsx`

```tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";
import { neonVars, toNeonRgb, type NeonColor } from "@/lib/neon";

// @property lets the browser interpolate the angle, so the conic gradient
// can rotate without rotating (and clipping) a real element.
const css = `
@property --nk-beam-angle { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
@keyframes nk-beam { to { --nk-beam-angle: 360deg; } }
.nk-beam {
  background: conic-gradient(from var(--nk-beam-angle), transparent 0deg, transparent calc(360deg - var(--nk-beam-size)), rgb(var(--nk-beam-to) / 0.9), rgb(var(--neon)) 360deg);
  animation: nk-beam var(--nk-beam-duration) linear infinite;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
@media (prefers-reduced-motion: reduce) { .nk-beam { animation: none; --nk-beam-angle: 135deg; } }
`;

export interface BorderBeamProps
  extends Omit<React.HTMLAttributes<HTMLDivElement>, "color"> {
  /** Head of the beam. Inherits `--neon-accent` when omitted. */
  color?: NeonColor;
  /** Tail of the beam. Defaults to the head color. */
  colorTo?: NeonColor;
  /** Seconds per lap. */
  duration?: number;
  /** Beam length, in degrees of the lap. */
  size?: number;
  /** Border thickness in pixels. */
  borderWidth?: number;
  /** Run counter-clockwise. */
  reverse?: boolean;
}

const BorderBeam = React.forwardRef<HTMLDivElement, BorderBeamProps>(
  (
    {
      className,
      style,
      color,
      colorTo,
      duration = 6,
      size = 90,
      borderWidth = 1,
      reverse = false,
      children,
      ...props
    },
    ref,
  ) => {
    const beamStyle = {
      "--nk-beam-to": colorTo ? toNeonRgb(colorTo) : "var(--neon)",
      "--nk-beam-size": `${size}deg`,
      "--nk-beam-duration": `${duration}s`,
      padding: borderWidth,
      animationDirection: reverse ? "reverse" : undefined,
    } as React.CSSProperties;

    return (
      <div
        ref={ref}
        style={neonVars(color, style)}
        className={cn(
          "[--neon:var(--neon-accent,34_211_238)]",
          "relative rounded-2xl",
          className,
        )}
        {...props}
      >
        <style>{css}</style>
        {children}
        <span
          aria-hidden
          style={beamStyle}
          className="nk-beam pointer-events-none absolute inset-0 rounded-[inherit]"
        />
        <span
          aria-hidden
          style={{ ...beamStyle, padding: borderWidth + 2 }}
          className="nk-beam pointer-events-none absolute -inset-0.5 rounded-[inherit] opacity-60 blur-[6px]"
        />
      </div>
    );
  },
);
BorderBeam.displayName = "BorderBeam";

export { BorderBeam };
```
