Effects & Borders

Glow Border

A static glowing border ring with a strength prop, confined to a 1px ring via the double-mask idiom.

Install

npx shadcn@latest add @paragon/glow-border

glow-border.tsx

import * as React from "react";
import { cn } from "@/lib/utils";

export interface GlowBorderProps extends React.ComponentProps<"div"> {
  /** Glow intensity, 0–1. Scales stroke opacity and bloom spread. */
  strength?: number;
  /** Border ring thickness in px. */
  borderWidth?: number;
  /** Corner radius of the ring in px. Match the parent's radius. */
  borderRadius?: number;
  /** Gradient stops painted along the ring. A single color also works. */
  colors?: string[];
}

/** Confines a painted layer to the border ring: a full-bleed mask minus a
 *  content-box mask leaves only the padding — the ring itself. */
const ringMask: React.CSSProperties = {
  mask: "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
  WebkitMaskComposite: "xor",
  maskComposite: "exclude",
};

/**
 * A static glowing border ring. Pure surface treatment — no animation.
 *
 * Anatomy: a crisp gradient stroke confined to a ring via the double-mask
 * idiom (padding + content-box/exclude composite), plus a separate blurred
 * bloom sibling underneath, so the blur never touches the crisp stroke.
 * Absolutely positioned — parent needs position: relative.
 */
export function GlowBorder({
  strength = 1,
  borderWidth = 1,
  borderRadius = 12,
  colors = ["#38bdf8", "#6366f1", "#a855f7"],
  className,
  style,
  ...props
}: GlowBorderProps) {
  const level = Math.min(Math.max(strength, 0), 1);
  const paint =
    colors.length > 1
      ? `linear-gradient(130deg, ${colors.join(", ")})`
      : (colors[0] ?? "#6366f1");

  return (
    <div
      aria-hidden
      className={cn("pointer-events-none absolute inset-0", className)}
      style={{ borderRadius, ...style }}
      {...props}
    >
      {/* Bloom — a slightly thicker ring, blurred. Kept as its own child so
          the halo can bleed softly without softening the stroke above it. */}
      <div
        className="absolute inset-0"
        style={{
          borderRadius,
          padding: borderWidth + 2,
          background: paint,
          filter: `blur(${4 + 4 * level}px)`,
          opacity: 0.5 * level,
          ...ringMask,
        }}
      />
      {/* Crisp stroke, exactly borderWidth thick. */}
      <div
        className="absolute inset-0"
        style={{
          borderRadius,
          padding: borderWidth,
          background: paint,
          opacity: 0.9 * level,
          ...ringMask,
        }}
      />
    </div>
  );
}