Stitch Path

A running stitch sewn along an SVG path as the page scrolls, following the holes it is laid over.

Loading preview

Installation

npx shadcn@latest add @loomui/stitch-path

Usage

import { StitchPath } from "@/components/ui/stitch-path"
<div className="h-32">
  <StitchPath />
</div>

It fills its parent and stretches to whatever shape that parent is, so give it a height. Colour comes from currentColor, so text-primary on the component or anywhere above it is enough.

How it is sewn

The visible path carries a dash pattern, because that is what makes it read as stitches rather than a line. A dashed path cannot be wiped with its own dash offset, because both want the same property. So the reveal is a second copy of the path, stroked solid and wiped, used as a mask.

pathLength="1" normalises the path, so the offset is simply the fraction left to sew and no geometry is ever measured.

<StitchPath stitch={5} gap={4} thickness={2} />

stitch and gap set the pattern. thickness is in pixels and does not stretch with the box, so a wide seam stays as fine as a narrow one.

guide draws the holes the thread runs through: zero-length dashes with a round cap, one per stitch. Turn it off for bare thread.

What drives it

By default it follows its own box through the viewport. Nothing when its top edge sits on the bottom of the screen, fully sewn once its bottom edge clears the top.

<StitchPath target={sectionRef} />

Pass target to follow a different element, a whole section usually, so a short seam is paced by the copy beside it rather than by its own height.

<StitchPath progress={0.4} />

Pass progress to drive it yourself, and the scroll listener is never attached.

Your own path

<StitchPath d="M 0 50 L 100 50" viewBox="0 0 100 100" />

Path data is in a 0 0 100 100 box. Because the box stretches, a straight line stays straight and a curve keeps its shape only in proportion. Draw the shape you want at the aspect ratio you plan to use.

Under prefers-reduced-motion the seam is drawn complete and no listener runs.

Props

PropTypeDefaultDescription
dstringan S-bendPath data, in the 0 0 100 100 box.
stitchnumber5Length of one stitch.
gapnumber4Bare thread between two stitches.
thicknessnumber2Thread thickness in pixels.
progressnumbernoneSew it yourself, 0 to 1.
targetRefObjectown boxElement to follow through the viewport.
guidebooleantrueDraw the holes the thread is sewn through.
classNamestringnoneMerged onto the svg. Colour goes here.

Any other <svg> prop is forwarded.

Accessibility

The whole svg is aria-hidden. It is decoration, and a seam has nothing a reader would want announced. Reduced motion draws it complete rather than leaving it half sewn.