All components

Tracing Beam

Gradient line that draws itself down long-form content.

0.8 KB gzippedDeps: motion/reactReduced-motion safeBase UI & Radix compatible

The beam draws as you read.

Installation

npx shadcn@latest add https://innsite.com.mx/r/tracing-beam.json

Installs with: motion

Source

"use client";

import { useEffect, useRef, useState } from "react";
import { motion, useScroll, useSpring, useTransform } from "motion/react";

import { cn } from "@/lib/utils";

interface TracingBeamProps {
  children: React.ReactNode;
  className?: string;
}

/**
 * A gradient line that draws itself down the side of long-form content as you
 * read. Pairs well with blog posts and changelogs.
 */
export function TracingBeam({ children, className }: TracingBeamProps) {
  const ref = useRef<HTMLDivElement>(null);
  const [height, setHeight] = useState(0);

  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ["start 10%", "end 85%"],
  });

  useEffect(() => {
    const element = ref.current;
    if (!element) return;
    // Track height so the beam stays accurate as images and fonts settle.
    const observer = new ResizeObserver(() =>
      setHeight(element.getBoundingClientRect().height)
    );
    observer.observe(element);
    return () => observer.disconnect();
  }, []);

  const drawn = useSpring(useTransform(scrollYProgress, [0, 1], [0, height]), {
    stiffness: 480,
    damping: 90,
  });

  return (
    <div
      ref={ref}
      data-slot="tracing-beam"
      className={cn("relative w-full", className)}
    >
      <div
        aria-hidden
        className="absolute -left-4 top-0 hidden h-full w-px bg-border md:block"
      >
        <motion.div
          style={{ height: drawn }}
          className="w-px bg-gradient-to-b from-brand-from via-brand-via to-brand-to"
        />
      </div>
      {children}
    </div>
  );
}