# Blueprint Compass Marks Trailing the Cursor

A shadcn/ui block from the [@weinc](https://we.inc/components?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/weinc/blueprint-compass-marks-trailing-the-cursor).

- Page, with a live preview: https://shadcn.exchange/r/weinc/blueprint-compass-marks-trailing-the-cursor
- Screenshot: https://shadcn.exchange/shot/weinc/blueprint-compass-marks-trailing-the-cursor/light.png
- Registry item: https://we.inc/r/blueprint-compass-marks-trailing-the-cursor.json
- Listed: 2026-10-28, source last changed 2026-10-28

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/weinc/blueprint-compass-marks-trailing-the-cursor.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `framer-motion`

## Files

### `components/we-inc/blueprint-compass-marks-trailing-the-cursor.tsx`

```tsx
import { useEffect, useRef } from "react";
import { useReducedMotion } from "framer-motion";

// Same markup, styles and script as the plain HTML version. The CSS comments name the values to change.
const STYLES = `
  /* Change these: --w7cmp-paper (blueprint blue), --w7cmp-line (mark and grid colour), --w7cmp-cell (grid size). Spacing and life of marks are in the script. */
  .w7cmp { --w7cmp-paper: #1d3a5f; --w7cmp-line: #dce8f5; --w7cmp-cell: 24px;
    position: relative; height: 560px; overflow: hidden; color: var(--w7cmp-line); touch-action: pan-y;
    background-color: var(--w7cmp-paper);
    background-image: linear-gradient(rgba(220,232,245,.07) 1px, transparent 1px), linear-gradient(90deg, rgba(220,232,245,.07) 1px, transparent 1px),
      linear-gradient(rgba(220,232,245,.12) 1px, transparent 1px), linear-gradient(90deg, rgba(220,232,245,.12) 1px, transparent 1px);
    background-size: var(--w7cmp-cell) var(--w7cmp-cell), var(--w7cmp-cell) var(--w7cmp-cell), calc(var(--w7cmp-cell) * 5) calc(var(--w7cmp-cell) * 5), calc(var(--w7cmp-cell) * 5) calc(var(--w7cmp-cell) * 5);
    font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; display: flex; align-items: center; }
  .w7cmp-marks { position: absolute; inset: 0; pointer-events: none; }
  .w7cmp-mark { position: absolute; left: -48px; top: -48px; width: 96px; height: 96px; opacity: 0; fill: none; stroke: var(--w7cmp-line); stroke-width: 1.3; stroke-linecap: round; will-change: transform, opacity; }
  .w7cmp-mark .w7cmp-dot, .w7cmp-mark .w7cmp-n { fill: var(--w7cmp-line); stroke: none; }
  .w7cmp-sheet { position: absolute; inset: 14px; border: 1px solid rgba(220,232,245,.35); pointer-events: none; }
  .w7cmp-body { position: relative; width: 100%; max-width: 64rem; margin: 0 auto; padding: 28px 32px 90px; }
  .w7cmp-eye { margin: 0; font-size: 12px; font-weight: 600; letter-spacing: .26em; text-transform: uppercase; color: #a9c3e0; }
  .w7cmp-h { margin: 14px 0 0; max-width: 14ch; font-family: Georgia, "Iowan Old Style", serif; font-weight: 400; font-size: clamp(2.2rem, 5.8vw, 4.2rem); line-height: 1.04; letter-spacing: -.02em; color: #f4f8fc; }
  .w7cmp-p { margin: 18px 0 0; max-width: 31rem; font-size: 16px; line-height: 1.65; color: #c5d5e7; }
  .w7cmp-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
  .w7cmp-btn, .w7cmp-ghost { display: inline-flex; align-items: center; min-height: 48px; padding: 0 22px; border-radius: 2px; font-weight: 600; font-size: 15px; text-decoration: none; }
  .w7cmp-btn { background: #f4f8fc; color: var(--w7cmp-paper); }
  .w7cmp-btn:hover { background: #fff; }
  .w7cmp-ghost { color: #f4f8fc; border: 1px solid rgba(220,232,245,.5); background: rgba(29,58,95,.6); }
  .w7cmp-ghost:hover { border-color: #f4f8fc; }
  .w7cmp-btn:focus-visible, .w7cmp-ghost:focus-visible { outline: 3px solid #f2c46b; outline-offset: 3px; }
  .w7cmp-block { position: absolute; right: 14px; bottom: 14px; display: flex; margin: 0; border-top: 1px solid rgba(220,232,245,.35); border-left: 1px solid rgba(220,232,245,.35); background: rgba(29,58,95,.85); }
  .w7cmp-block div { padding: 8px 14px; border-left: 1px solid rgba(220,232,245,.2); }
  .w7cmp-block div:first-child { border-left: 0; }
  .w7cmp-block dt { font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: #a9c3e0; }
  .w7cmp-block dd { margin: 2px 0 0; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
  @media (min-width: 720px) { .w7cmp-body { padding: 40px 64px 60px; } }
  @media (prefers-reduced-motion: reduce) { .w7cmp .w7cmp-mark:first-child { opacity: .6; left: auto; right: 10%; top: 16%; width: 120px; height: 120px; transform: rotate(18deg); } }
`;

export default function BlueprintCompassMarksTrailingTheCursor() {
  const rootRef = useRef<any>(null);
  const prefersReduced = useReducedMotion();

  useEffect(() => {
    const root: any = rootRef.current;
    if (!root) return;
    const off: Array<() => void> = [];
    
      var GAP = 90, LIFE = 2800, POOL = 12, IDLE_AFTER = 1800; // px between marks, ms each mark lasts, marks reused, ms before the demo resumes
      if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
      var layer = root.querySelector(".w7cmp-marks"), tpl = layer.firstElementChild, pool = [tpl], anims = [], k = 0;
      for (var i = 1; i < POOL; i++) { var c = tpl.cloneNode(true); layer.appendChild(c); pool.push(c); }
      off.push(function () { anims.forEach(function (a) { a.cancel(); }); while (layer.children.length > 1) layer.removeChild(layer.lastChild); });
      var lx = -999, ly = -999, real = 0, raf = 0, seen = true;
      function mark(x, y, age) {
        var n = k++ % POOL, a = Math.atan2(y - ly, x - lx) * 57.3 + 90, base = "translate(" + x.toFixed(1) + "px," + y.toFixed(1) + "px) rotate(" + a.toFixed(1) + "deg)";
        if (anims[n]) anims[n].cancel();
        anims[n] = pool[n].animate([
          { opacity: 0, transform: base + " scale(.5) rotate(-40deg)" },
          { opacity: .95, transform: base + " scale(1)", offset: .14 },
          { opacity: .7, transform: base + " scale(1)", offset: .5 },
          { opacity: 0, transform: base + " scale(1.08)" }
        ], { duration: LIFE, easing: "cubic-bezier(.2,.7,.2,1)", fill: "forwards" });
        if (age) anims[n].currentTime = age;
        lx = x; ly = y;
      }
      function near(x, y) { if (Math.hypot(x - lx, y - ly) > GAP) mark(x, y, 0); }
      function path(t) { return [root.clientWidth * (.62 + .3 * Math.sin(t * .0005)), root.clientHeight * (.48 + .32 * Math.sin(t * .00115 + .6))]; }
      var now = performance.now();
      for (var j = 6; j > 0; j--) { var q = path(now - j * 520); mark(q[0], q[1], j * 330); }
      function tick(t) {
        raf = requestAnimationFrame(tick);
        if (real && t - real < IDLE_AFTER) return;
        var p = path(t); near(p[0], p[1]);
      }
      function at(e) { var b = root.getBoundingClientRect(); return [e.clientX - b.left, e.clientY - b.top]; }
      function move(e) { if (e.pointerType === "touch") return; real = performance.now(); var p = at(e); near(p[0], p[1]); }
      function tap(e) { if (e.pointerType !== "touch") return; real = performance.now(); var p = at(e); mark(p[0], p[1], 0); }
      function leave() { real = 0; }
      root.addEventListener("pointermove", move);
      root.addEventListener("pointerdown", tap);
      root.addEventListener("pointerleave", leave);
      off.push(function () { root.removeEventListener("pointermove", move); root.removeEventListener("pointerdown", tap); root.removeEventListener("pointerleave", leave); });
      function play() { if (!raf && seen) raf = requestAnimationFrame(tick); }
      function stop() { cancelAnimationFrame(raf); raf = 0; }
      var io = new IntersectionObserver(function (e) { seen = e[0].isIntersecting; if (seen) play(); else stop(); });
      io.observe(root);
      off.push(stop, function () { io.disconnect(); });
      play();
    return () => off.forEach((f) => f());
  }, [prefersReduced]);

  return (
    <section className="w7cmp" data-w7cmp="" ref={rootRef} aria-label="Arden Vale Architects">
      <style>{STYLES}</style>
      <div className="w7cmp-marks" aria-hidden="true">
        <svg className="w7cmp-mark" viewBox="-30 -30 60 60"><circle r="17" /><circle r="2.2" className="w7cmp-dot" /><path d="M0 -26V-19M0 19V26M-26 0H-19M19 0H26M-12 -12L-9 -9M12 12L9 9M12 -12L9 -9M-12 12L-9 9" /><path d="M0 -15L4 -4L0 -6L-4 -4Z" className="w7cmp-n" /></svg>
      </div>
      <div className="w7cmp-sheet" aria-hidden="true"></div>
      <div className="w7cmp-body">
        <p className="w7cmp-eye">Arden Vale Architects</p>
        <h2 className="w7cmp-h">Houses drawn around the way you live</h2>
        <p className="w7cmp-p">We start with a week of your routines, not a style. Where the morning light lands, where the boots go, which door everyone really uses. The plan follows from that.</p>
        <div className="w7cmp-row">
          <a className="w7cmp-btn" href="#">Start with a site visit</a>
          <a className="w7cmp-ghost" href="#">View drawings</a>
        </div>
      </div>
      <dl className="w7cmp-block">
        <div><dt>Drawing</dt><dd>AV-014</dd></div>
        <div><dt>Sheet</dt><dd>Ground floor</dd></div>
        <div><dt>Scale</dt><dd>1:50</dd></div>
      </dl>
    </section>
  );
}
```
