ham7a311

Nib Trail Cursor

Component 3

About

A custom pointer trail that draws like a broad-nib calligraphy pen, leaving thick-and-thin ink strokes that fade a second after drawing or linger for several seconds while the mouse button is held, with a double-click to wipe the page clean.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Nib Trail Cursor to your project and fits it to your theme.

Code

How Nib Trail Cursor is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import NibTrail from "@/components/ui/nib-trail";
import { useState } from "react";

export default function NibTrailDemo() {
  const [angle, setAngle] = useState(38);
  const c = {
    page: "bg-[#f5f0e4] text-[#1d2b4f]",
    ink: "#1d2b4f",
    muted: "text-[#7c7462]",
    rule: "rgb(29 43 79 / 0.14)",
    guide: "rgb(178 58 46 / 0.12)",
    trace: "text-[#1d2b4f]/[0.07]",
    chip: "border-black/10 aria-checked:bg-[#1d2b4f] aria-checked:text-[#f5f0e4] aria-checked:border-transparent",
    ring: "focus-visible:outline-[#1d2b4f]",
  };

  const NIBS = [
    { a: 38, label: "Italic · 38°" },
    { a: 50, label: "Copperplate · 50°" },
    { a: 70, label: "Naskh · 70°" },
  ];

  return (
    <NibTrail
      ink={c.ink}
      angle={angle}
      theme="paper"
      className={`min-h-screen w-full ${c.page}`}
      style={{ fontFamily: "Geist, ui-sans-serif, system-ui" }}
    >
      <div className="relative mx-auto flex min-h-screen w-full max-w-4xl flex-col px-5 py-10 sm:px-10">
        <div className="flex flex-wrap items-baseline justify-between gap-3">
          <p
            className={`font-mono text-[11px] uppercase tracking-[0.16em] ${c.muted}`}
          >
            Practice sheet · No. 4
          </p>
          <div
            role="radiogroup"
            aria-label="Nib angle"
            className="flex flex-wrap gap-1.5"
          >
            {NIBS.map((n) => (
              <button
                key={n.a}
                type="button"
                role="radio"
                aria-checked={angle === n.a}
                onClick={() => setAngle(n.a)}
                className={`rounded-full border px-3 py-1 font-mono text-[11px] tracking-wide transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 ${c.chip} ${c.ring}`}
              >
                {n.label}
              </button>
            ))}
          </div>
        </div>

        {/* A lettering pad: ascender, x-height, baseline and descender rules, with slant guides. */}
        <div className="relative mt-10 flex-1 [--row:150px] sm:[--row:170px]">
          {[0, 1, 2].map((row) => (
            <div
              key={row}
              className="relative h-[150px] sm:h-[170px]"
              aria-hidden="true"
            >
              <div
                className="absolute inset-x-0 top-[18%] border-t"
                style={{ borderColor: c.rule }}
              />
              <div
                className="absolute inset-x-0 top-[42%] border-t border-dashed"
                style={{ borderColor: c.rule }}
              />
              <div
                className="absolute inset-x-0 top-[70%] border-t"
                style={{ borderColor: c.guide, borderTopWidth: 1.5 }}
              />
              <div
                className="absolute inset-x-0 top-[90%] border-t"
                style={{ borderColor: c.rule }}
              />
              <div
                className="absolute inset-x-0 top-[18%] h-[72%]"
                style={{
                  backgroundImage: `repeating-linear-gradient(${90 - 12}deg, transparent 0 46px, ${c.guide} 46px 47px)`,
                }}
              />
            </div>
          ))}
          <p
            className={`pointer-events-none absolute left-0 select-none text-[clamp(5.5rem,17vw,10.5rem)] italic leading-none tracking-[-0.01em] ${c.trace}`}
            style={{
              fontFamily: '"Instrument Serif", Newsreader, Georgia, serif',
              top: 0,
              transform: "translateY(calc(var(--row) * 0.7 - 0.86em))",
            }}
            aria-hidden="true"
          >
            Muscat
          </p>
        </div>

        <div className="mt-6 flex flex-wrap items-baseline justify-between gap-2">
          <h2
            className="text-[clamp(1.4rem,3vw,1.9rem)] leading-tight tracking-[-0.01em]"
            style={{
              fontFamily: '"Instrument Serif", Newsreader, Georgia, serif',
            }}
          >
            Trace the word, or write your own.
          </h2>
          <p
            className={`text-[12.5px] [@media(pointer:coarse)]:hidden ${c.muted}`}
          >
            Move to sketch · hold to write in ink that stays · double-click to
            wipe
          </p>
          <p
            className={`hidden text-[12.5px] [@media(pointer:coarse)]:block ${c.muted}`}
          >
            Writes with a mouse or a pen — fingers keep scrolling the page.
          </p>
        </div>
      </div>
    </NibTrail>
  );
}

More by ham7a311

View all
  • Route Globe

    Route Globe

    18
  • Wave Mesh

    Wave Mesh

    11
  • Glass Tiles

    Glass Tiles

    10
  • Glass Carousel

    Glass Carousel

    8
  • Uptime Ribbon

    Uptime Ribbon

    7
  • Wind Map

    Wind Map

    7
  • Time Window

    Time Window

    6
  • Streak Field Stats

    Streak Field Stats

    5