Skip to content
WANCRAFT

E.171Backgrounds & Effects

Ribbon Trail

Silky spring-chain ribbons that chase the pointer, tapering and twisting, and drift on a Lissajous path when idle.

NewReactCanvasTailwindadvanced

Overview

Ribbon Trail draws a handful of silk ribbons on one canvas. Each ribbon is a chain of springs: the head springs toward the pointer and every following point springs toward a rest spot one link behind the point ahead, so ribbons keep their length, trace the head's path and overshoot a little; each ribbon gets its own stiffness, so they fan out and overtake each other. Ribbons are rendered as smooth tapered outlines — offset along the curve's normal and joined with quadratic curves — with a slow twist that narrows them edge-on and a head-to-tail gradient that blends from the accent to the secondary colour across the set. With no pointer, they drift along a slow Lissajous figure. Physics runs at a fixed 60Hz step (identical on 120/144Hz displays), the loop sleeps once everything settles, and it stops offscreen and in hidden tabs.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/ribbon-trail.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/ribbon-trail.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/ribbon-trail.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { RibbonTrail } from "@/components/ui/ribbon-trail";

export function RibbonTrailExample() {
  return (
    <div className="relative h-96 w-full overflow-hidden rounded-xl">
      <RibbonTrail />
    </div>
  );
}

03Props

PropTypeDefaultDescription
ribbonCountnumber5Number of ribbons (1–12); each is a little looser than the last.
thicknessnumber18Ribbon width at the head in px.
lengthnumber34Points per ribbon (6–120); more points make longer tails.
stiffnessnumber0.5How tightly the ribbons follow, 0–1.
accentstring"#FF7448"Colour of the first ribbon (any CSS colour).
secondarystring"#B6F3D1"Colour of the last ribbon; those between blend.
idlebooleantrueDrift along a slow Lissajous path while no pointer is present.
childrenReactNode—Optional content layered above the ribbons.
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the root, which is absolute inset-0 — place it inside a positioned parent.

04Features

  • 01Follow-the-leader spring chains with per-ribbon stiffness and a rope limit, so ribbons keep their length, fan out and overtake each other
  • 02Tapered, twisting outlines smoothed with quadratic curves and a head-to-tail gradient
  • 03Idle Lissajous drift when no pointer is present; touch and pen steer while pressed
  • 04Fixed 60Hz physics: identical on 60, 120 and 144Hz displays, and never 'settles' on a frame without a step
  • 05Loop sleeps once settled, stops offscreen and in hidden tabs; DPR-aware canvas with ResizeObserver
motion · springmotion · continuousinput · pointerinput · viewport

05Accessibility

  • The canvas is aria-hidden and ignores pointer events; content layered on top stays fully interactive.
  • Touch input uses passive listeners and only steers while pressed, so page scrolling is never blocked.
  • With prefers-reduced-motion the ribbons are drawn once as a still composition along the idle path, and pointer input is ignored.