Open demo

CSS code

HTML
<div class="hd-ui">
<div class="two">
<div><span class="lbl">Pointing at a card</span>
<div class="ann">
<p class="hd-note">Most teams of five pick this one</p>
<svg class="hd" viewBox="0 0 112 72" aria-hidden="true"><path pathLength="1" d="M6 12C38 2 84 8 104 56"/><path class="hd-head" pathLength="1" d="M93 47L104 56L106 42"/></svg>
<div class="plan"><h3>Studio</h3><p class="price">€24 <small>per month</small></p>
<ul><li>Five seats included</li><li>Shared brand kit</li><li>Priority support</li></ul>
<a href="#">Choose Studio</a></div>
</div></div>
<div><span class="lbl">Pointing at an interface</span>
<div class="tb" role="img" aria-label="Spreadsheet toolbar with Share and Export buttons, Export highlighted"><span class="file">Q3 revenue.xlsx</span><span class="chip">Share</span><span class="chip on">Export</span></div>
<div class="callout"><p class="hd-note">Export lives here now, next to Share</p>
<svg class="hd" viewBox="0 0 76 80" aria-hidden="true"><path pathLength="1" d="M4 72C26 74 38 44 28 38C18 32 12 50 30 52C50 54 62 32 66 8"/><path class="hd-head" pathLength="1" d="M58 18L66 8L70 20"/></svg></div>
</div>
</div>
<div><span class="lbl">Arrow library</span>
<div class="hd-set">
<figure><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M8 48Q50 4 90 30"/><path class="hd-head" pathLength="1" d="M84 18L90 30L77 29"/></svg><figcaption>Swoosh</figcaption></figure>
<figure><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M6 44C26 44 36 14 24 12C12 10 14 34 34 34C54 34 70 26 90 22"/><path class="hd-head" pathLength="1" d="M78 18L90 22L80 31"/></svg><figcaption>Loop</figcaption></figure>
<figure><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M8 14Q18 30 28 40Q38 26 46 16Q56 30 64 42Q76 30 88 22"/><path class="hd-head" pathLength="1" d="M75 24L88 22L83 34"/></svg><figcaption>Zigzag</figcaption></figure>
<figure><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M14 10C60 6 80 20 62 34C48 44 40 30 52 26C66 22 70 40 66 54"/><path class="hd-head" pathLength="1" d="M75 45L66 54L63 41"/></svg><figcaption>Curl</figcaption></figure>
</div></div>
</div>
CSS
/* Hand-drawn SVG arrows for annotations. Each path has pathLength="1", so one dash of length 1 can be
   slid away to draw it; the head draws after the line. The note beside an arrow is real text; the SVG is aria-hidden. */
.hd-ui{--ink:#17142b;--accent:#4232d8;--line:#e4e2ef;--muted:#4a4660;--pen:#c2410c;
  --hand:"Segoe Print","Bradley Hand","Chalkboard SE","Comic Sans MS",cursive;
  display:flex;flex-direction:column;gap:44px}
.hd{display:block;overflow:visible;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.hd path{stroke-dasharray:1;animation:hd-draw 1.1s cubic-bezier(.6,.1,.3,1) .2s both}
.hd .hd-head{animation-duration:.35s;animation-delay:1.25s}
@keyframes hd-draw{from{stroke-dashoffset:1}}
.hd-note{font-family:var(--hand);font-size:1.12rem;line-height:1.25;color:var(--pen)}
/* 1. Pointing at a pricing card */
.ann{display:flex;align-items:center;gap:4px}
.ann .hd-note{flex:none;width:112px;text-align:center;rotate:-5deg}
.ann .hd{flex:none;width:84px;height:58px;margin-top:40px;color:var(--pen)}
.plan{flex:1;min-width:0;padding:20px 22px;border:2px solid var(--accent);border-radius:16px;background:#fff}
.plan h3{font-size:1rem}
.plan .price{margin:6px 0 12px;font-size:1.8rem;font-weight:750;letter-spacing:-.02em}
.plan .price small{font-size:.84rem;font-weight:500;color:var(--muted);letter-spacing:0}
.plan ul{margin:0 0 16px;padding-left:18px;font-size:.86rem;line-height:1.7;color:var(--muted)}
.plan a{display:block;padding:10px;border-radius:10px;background:var(--accent);color:#fff;font-size:.9rem;font-weight:650;text-align:center;text-decoration:none}
.plan a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
/* 2. Pointing at part of an interface */
.tb{display:flex;align-items:center;gap:8px;padding:12px 14px;border:1px solid var(--line);border-radius:14px;background:#fff}
.tb .file{margin-right:auto;font-size:.9rem;font-weight:650;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tb .chip{padding:7px 12px;border:1px solid #d6d3e4;border-radius:8px;font-size:.82rem;font-weight:600}
.tb .chip.on{border-color:var(--accent);background:var(--accent);color:#fff}
.callout{display:flex;justify-content:flex-end;align-items:flex-end;gap:2px;padding-right:22px}
.callout .hd-note{max-width:170px;margin-bottom:6px;text-align:right;rotate:3deg}
.callout .hd{flex:none;width:76px;height:80px;color:var(--pen)}
/* 3. Arrow library */
.hd-set{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}
.hd-set figure{display:flex;flex-direction:column;align-items:center;gap:12px;margin:0;padding:22px 14px 16px;border:1px solid var(--line);border-radius:14px;background:#fff}
.hd-set .hd{width:100%;max-width:120px;height:72px;color:var(--ink)}
.hd-set figure:nth-child(2) path{animation-delay:.4s}.hd-set figure:nth-child(2) .hd-head{animation-delay:1.45s}
.hd-set figure:nth-child(3) path{animation-delay:.6s}.hd-set figure:nth-child(3) .hd-head{animation-delay:1.65s}
.hd-set figure:nth-child(4) path{animation-delay:.8s}.hd-set figure:nth-child(4) .hd-head{animation-delay:1.85s}
.hd-set figcaption{font-size:.8rem;color:var(--muted)}
@media (max-width:700px){.hd-set{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:420px){.ann{flex-direction:column;align-items:stretch}.ann .hd-note{width:auto}.ann .hd{align-self:center;margin:24px 0 20px;rotate:60deg}}
@media (prefers-reduced-motion:reduce){.hd path{animation:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="hd-ui d-flex flex-column gap-5">
<div class="row gx-4 gy-5">
<div class="col-md-7"><span class="lbl">Pointing at a card</span>
<div class="ann d-flex align-items-center gap-1">
<p class="hd-note flex-shrink-0 text-center mb-0">Most teams of five pick this one</p>
<svg class="hd" viewBox="0 0 112 72" aria-hidden="true"><path pathLength="1" d="M6 12C38 2 84 8 104 56"/><path class="hd-head" pathLength="1" d="M93 47L104 56L106 42"/></svg>
<div class="plan card flex-fill"><div class="card-body"><h3 class="fw-bold mb-0">Studio</h3><p class="price">€24 <small>per month</small></p>
<ul><li>Five seats included</li><li>Shared brand kit</li><li>Priority support</li></ul>
<a href="#" class="btn btn-primary w-100">Choose Studio</a></div></div>
</div></div>
<div class="col-md-5"><span class="lbl">Pointing at an interface</span>
<div class="tb card flex-row align-items-center gap-2 px-3 py-2" role="img" aria-label="Spreadsheet toolbar with Share and Export buttons, Export highlighted"><span class="file fw-semibold me-auto text-truncate py-1">Q3 revenue.xlsx</span><span class="chip">Share</span><span class="chip on">Export</span></div>
<div class="callout d-flex justify-content-end align-items-end gap-1"><p class="hd-note text-end">Export lives here now, next to Share</p>
<svg class="hd" viewBox="0 0 76 80" aria-hidden="true"><path pathLength="1" d="M4 72C26 74 38 44 28 38C18 32 12 50 30 52C50 54 62 32 66 8"/><path class="hd-head" pathLength="1" d="M58 18L66 8L70 20"/></svg></div>
</div>
</div>
<div><span class="lbl">Arrow library</span>
<div class="hd-set row row-cols-2 row-cols-md-4 g-2 g-md-3">
<div class="col"><figure class="card align-items-center gap-3 m-0 px-3 pt-4 pb-3"><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M8 48Q50 4 90 30"/><path class="hd-head" pathLength="1" d="M84 18L90 30L77 29"/></svg><figcaption>Swoosh</figcaption></figure></div>
<div class="col"><figure class="card align-items-center gap-3 m-0 px-3 pt-4 pb-3"><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M6 44C26 44 36 14 24 12C12 10 14 34 34 34C54 34 70 26 90 22"/><path class="hd-head" pathLength="1" d="M78 18L90 22L80 31"/></svg><figcaption>Loop</figcaption></figure></div>
<div class="col"><figure class="card align-items-center gap-3 m-0 px-3 pt-4 pb-3"><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M8 14Q18 30 28 40Q38 26 46 16Q56 30 64 42Q76 30 88 22"/><path class="hd-head" pathLength="1" d="M75 24L88 22L83 34"/></svg><figcaption>Zigzag</figcaption></figure></div>
<div class="col"><figure class="card align-items-center gap-3 m-0 px-3 pt-4 pb-3"><svg class="hd" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M14 10C60 6 80 20 62 34C48 44 40 30 52 26C66 22 70 40 66 54"/><path class="hd-head" pathLength="1" d="M75 45L66 54L63 41"/></svg><figcaption>Curl</figcaption></figure></div>
</div></div>
</div>
CSS
/* Bootstrap grid, .card and .btn around hand-drawn SVG arrows. Each path has pathLength="1", so one dash of
   length 1 can be slid away to draw it; the head draws after the line. Notes are real text; the SVGs are aria-hidden. */
.hd-ui{--ink:#17142b;--accent:#4232d8;--line:#e4e2ef;--muted:#4a4660;--pen:#c2410c;
  --hand:"Segoe Print","Bradley Hand","Chalkboard SE","Comic Sans MS",cursive}
.hd{display:block;overflow:visible;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.hd path{stroke-dasharray:1;animation:hd-draw 1.1s cubic-bezier(.6,.1,.3,1) .2s both}
.hd .hd-head{animation-duration:.35s;animation-delay:1.25s}
@keyframes hd-draw{from{stroke-dashoffset:1}}
.hd-note{font-family:var(--hand);font-size:1.12rem;line-height:1.25;color:var(--pen)}
.hd-ui .card{--bs-card-border-color:var(--line);--bs-card-border-radius:14px}
/* 1. Pointing at a pricing card */
.ann .hd-note{width:112px;rotate:-5deg}
.ann .hd{flex:none;width:84px;height:58px;margin-top:40px;color:var(--pen)}
.plan.card{--bs-card-border-color:var(--accent);--bs-card-border-width:2px;--bs-card-border-radius:16px;--bs-card-spacer-x:22px;--bs-card-spacer-y:20px;min-width:0}
.plan h3{font-size:1rem}
.plan .price{margin:6px 0 12px;font-size:1.8rem;font-weight:750;letter-spacing:-.02em}
.plan .price small{font-size:.84rem;font-weight:500;color:var(--muted);letter-spacing:0}
.plan ul{margin:0 0 16px;padding-left:18px;font-size:.86rem;line-height:1.7;color:var(--muted)}
.plan .btn{--bs-btn-bg:var(--accent);--bs-btn-border-color:var(--accent);--bs-btn-hover-bg:#3427b8;--bs-btn-hover-border-color:#3427b8;
  --bs-btn-padding-y:10px;--bs-btn-font-size:.9rem;--bs-btn-font-weight:650;--bs-btn-border-radius:10px}
.plan .btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px;box-shadow:none}
/* 2. Pointing at part of an interface */
.tb .file{font-size:.9rem}
.tb .chip{padding:7px 12px;border:1px solid #d6d3e4;border-radius:8px;font-size:.82rem;font-weight:600}
.tb .chip.on{border-color:var(--accent);background:var(--accent);color:#fff}
.callout{padding-right:22px}
.callout .hd-note{max-width:170px;margin-bottom:6px;rotate:3deg}
.callout .hd{flex:none;width:76px;height:80px;color:var(--pen)}
/* 3. Arrow library */
.hd-set .hd{width:100%;max-width:120px;height:72px;color:var(--ink)}
.hd-set .col:nth-child(2) path{animation-delay:.4s}.hd-set .col:nth-child(2) .hd-head{animation-delay:1.45s}
.hd-set .col:nth-child(3) path{animation-delay:.6s}.hd-set .col:nth-child(3) .hd-head{animation-delay:1.65s}
.hd-set .col:nth-child(4) path{animation-delay:.8s}.hd-set .col:nth-child(4) .hd-head{animation-delay:1.85s}
.hd-set figcaption{font-size:.8rem;color:var(--muted)}
@media (max-width:420px){.ann{flex-direction:column;align-items:stretch!important}.ann .hd-note{width:auto}.ann .hd{align-self:center;margin:24px 0 20px;rotate:60deg}}
@media (prefers-reduced-motion:reduce){.hd path{animation:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="flex flex-col gap-11">
<div class="grid grid-cols-1 gap-x-7 gap-y-10 md:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)]">
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5d5a72] uppercase">Pointing at a card</span>
<div class="flex items-center gap-1 max-[420px]:flex-col max-[420px]:items-stretch">
<p class="w-28 flex-none -rotate-5 text-center font-hand text-[1.12rem] leading-tight text-pen max-[420px]:w-auto">Most teams of five pick this one</p>
<svg class="hd-draw mt-10 block h-[58px] w-[84px] flex-none overflow-visible fill-none stroke-pen stroke-[2.4] max-[420px]:my-6 max-[420px]:self-center max-[420px]:rotate-60" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 112 72" aria-hidden="true"><path pathLength="1" d="M6 12C38 2 84 8 104 56"/><path class="hd-head" pathLength="1" d="M93 47L104 56L106 42"/></svg>
<div class="min-w-0 flex-1 rounded-2xl border-2 border-accent bg-white px-[22px] py-5"><h3 class="font-bold">Studio</h3><p class="mt-1.5 mb-3 text-[1.8rem] font-[750] tracking-[-.02em]">€24 <small class="text-[.84rem] font-medium tracking-normal text-muted">per month</small></p>
<ul class="mb-4 list-disc pl-[18px] text-[.86rem] leading-[1.7] text-muted"><li>Five seats included</li><li>Shared brand kit</li><li>Priority support</li></ul>
<a href="#" class="block rounded-[10px] bg-accent p-2.5 text-center text-[.9rem] font-[650] text-white hover:bg-[#3427b8] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-accent">Choose Studio</a></div>
</div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5d5a72] uppercase">Pointing at an interface</span>
<div class="flex items-center gap-2 rounded-[14px] border border-line bg-white px-3.5 py-3" role="img" aria-label="Spreadsheet toolbar with Share and Export buttons, Export highlighted"><span class="mr-auto truncate text-[.9rem] font-[650]">Q3 revenue.xlsx</span><span class="rounded-lg border border-[#d6d3e4] px-3 py-[7px] text-[.82rem] font-semibold">Share</span><span class="rounded-lg border border-accent bg-accent px-3 py-[7px] text-[.82rem] font-semibold text-white">Export</span></div>
<div class="flex items-end justify-end gap-0.5 pr-[22px]"><p class="mb-1.5 max-w-[170px] rotate-3 text-right font-hand text-[1.12rem] leading-tight text-pen">Export lives here now, next to Share</p>
<svg class="hd-draw block h-20 w-[76px] flex-none overflow-visible fill-none stroke-pen stroke-[2.4]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 76 80" aria-hidden="true"><path pathLength="1" d="M4 72C26 74 38 44 28 38C18 32 12 50 30 52C50 54 62 32 66 8"/><path class="hd-head" pathLength="1" d="M58 18L66 8L70 20"/></svg></div>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5d5a72] uppercase">Arrow library</span>
<div class="grid grid-cols-2 gap-3 md:grid-cols-4">
<figure class="flex flex-col items-center gap-3 rounded-[14px] border border-line bg-white px-3.5 pt-[22px] pb-4"><svg class="hd-draw block h-[72px] w-full max-w-[120px] overflow-visible fill-none stroke-ink stroke-[2.4]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M8 48Q50 4 90 30"/><path class="hd-head" pathLength="1" d="M84 18L90 30L77 29"/></svg><figcaption class="text-[.8rem] text-muted">Swoosh</figcaption></figure>
<figure class="flex flex-col items-center gap-3 rounded-[14px] border border-line bg-white px-3.5 pt-[22px] pb-4"><svg class="hd-draw block h-[72px] w-full max-w-[120px] overflow-visible fill-none stroke-ink stroke-[2.4] [--hd-delay:.4s]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M6 44C26 44 36 14 24 12C12 10 14 34 34 34C54 34 70 26 90 22"/><path class="hd-head" pathLength="1" d="M78 18L90 22L80 31"/></svg><figcaption class="text-[.8rem] text-muted">Loop</figcaption></figure>
<figure class="flex flex-col items-center gap-3 rounded-[14px] border border-line bg-white px-3.5 pt-[22px] pb-4"><svg class="hd-draw block h-[72px] w-full max-w-[120px] overflow-visible fill-none stroke-ink stroke-[2.4] [--hd-delay:.6s]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M8 14Q18 30 28 40Q38 26 46 16Q56 30 64 42Q76 30 88 22"/><path class="hd-head" pathLength="1" d="M75 24L88 22L83 34"/></svg><figcaption class="text-[.8rem] text-muted">Zigzag</figcaption></figure>
<figure class="flex flex-col items-center gap-3 rounded-[14px] border border-line bg-white px-3.5 pt-[22px] pb-4"><svg class="hd-draw block h-[72px] w-full max-w-[120px] overflow-visible fill-none stroke-ink stroke-[2.4] [--hd-delay:.8s]" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 100 60" aria-hidden="true"><path pathLength="1" d="M14 10C60 6 80 20 62 34C48 44 40 30 52 26C66 22 70 40 66 54"/><path class="hd-head" pathLength="1" d="M75 45L66 54L63 41"/></svg><figcaption class="text-[.8rem] text-muted">Curl</figcaption></figure>
</div></div>
</div>
CSS
/* Palette, the handwriting font stack and one utility that draws an SVG: every path has pathLength="1", so a
   dash of length 1 slides away; .hd-head (the arrowhead) follows. Set [--hd-delay:…] to stagger. Off under reduced motion. */
@theme {
  --color-ink: #17142b;
  --color-accent: #4232d8;
  --color-line: #e4e2ef;
  --color-muted: #4a4660;
  --color-pen: #c2410c;
  --font-hand: "Segoe Print","Bradley Hand","Chalkboard SE","Comic Sans MS",cursive;
  @keyframes hd-draw{from{stroke-dashoffset:1}}
}
@utility hd-draw{
  & path{stroke-dasharray:1;animation:hd-draw 1.1s cubic-bezier(.6,.1,.3,1) var(--hd-delay,.2s) both}
  & .hd-head{animation-duration:.35s;animation-delay:calc(var(--hd-delay,.2s) + 1.05s)}
  @media (prefers-reduced-motion:reduce){& path{animation:none}}
}

About this set

Curved, hand-drawn SVG arrows that draw themselves in, for annotating a pricing table, a product screenshot or a feature you want people to notice. The first example points a curved arrow from a handwritten note, Most teams of five pick this one, at a pricing card. The second loops an arrow up from a note to the Export button of a toolbar. The arrow library shows four standalone styles, a swoosh, a loop, a zigzag and a curl, each drawn after the one before. Every path has pathLength=”1″, which makes the dash maths independent of the real length: the stroke gets a dash of 1, the animation slides the dash offset from 1 to 0, and the arrowhead path draws after the line. The notes use a stack of handwriting system fonts, so nothing is downloaded. Arrows are aria-hidden and the note beside each one is real text, so the meaning is in the words, not the drawing. On narrow screens the card annotation stacks and the arrow turns to point down at the card. Under prefers-reduced-motion the arrows appear fully drawn.

What’s included

  • Self-drawing stroke animation using pathLength="1"
  • Arrowhead draws after the line
  • Annotation examples for a pricing card and a toolbar button
  • Library of four arrow styles: swoosh, loop, zigzag and curl
  • Handwriting font stack with no web font download

Accessibility

The SVG arrows are aria-hidden, and each annotation is real text next to the thing it describes. The toolbar mock-up is a role="img" with a written description. The pricing link shows a 2px focus outline. The draw animation runs once and is removed under prefers-reduced-motion, leaving the arrows fully drawn.

Customise it

Change --pen for the annotation colour, --hand for the font stack and stroke-width on .hd for line weight. Stagger the draw with animation-delay; in Tailwind set [--hd-delay:…] on an svg that has the hd-draw utility.