WebberUI

Leader Line Captions

Magazine-style leader-line callouts — numbered captions arranged along a sidebar, connected by SVG leader lines to detail points in the image, drawn one by one on reveal and dimming into focus on hover.

Loading preview…
npx shadcn@latest add https://webberui.com/r/leader-line-captions.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

0.14
0.5
0.55
<LeaderLineCaptions />

Installation

npx shadcn@latest add https://webberui.com/r/leader-line-captions.json

Or, once registries are configured in components.json, install it as @webberui/leader-line-captions.

Usage

image takes any image node (an <img>, a gradient div, whatever), and captions marks detail points in the image with percentage coordinates; the component numbers them and draws the leader lines automatically.

import { LeaderLineCaptions } from "@/components/ui/leader-line-captions";

<LeaderLineCaptions
  image={<img src="/product.jpg" alt="Product exterior" />}
  side="right"
  captions={[
    { x: 30, y: 24, title: "Camera module", description: "Three lenses in a matrix layout." },
    { x: 68, y: 52, title: "Body frame", description: "Aerospace-grade aluminium alloy." },
    { x: 44, y: 82, title: "Speaker grille" },
  ]}
/>

Each caption's x / y is a percentage relative to the image (0–100) and is independent of the image's actual size, so the leader lines realign automatically when it scales.

Controlled focus

Without activeIndex it is uncontrolled, and hovering or focusing a caption focuses it automatically. To keep it in sync with external state, pass activeIndex together with onActiveChange:

const [active, setActive] = React.useState<number | null>(null);

<LeaderLineCaptions
  image={<img src="/map.jpg" alt="Map" />}
  activeIndex={active}
  onActiveChange={setActive}
  captions={captions}
/>

Props

LeaderLineCaptions

PropTypeDefaultDescription
imageReact.ReactNodeThe image content, filling the image frame edge to edge
captionsLeaderCaption[]The list of numbered captions, numbered 1…n automatically in order
side"left" | "right""right"Which side the caption sidebar sits on
aspectRatiostring"4 / 3"Aspect ratio of the image frame (a CSS aspect-ratio value)
staggernumber0.14Interval between leader lines being drawn (seconds)
delaynumber0.1Delay before the reveal starts (seconds)
drawDurationnumber0.5Duration of drawing a single leader line (seconds)
oncebooleantruePlays the reveal animation only the first time the element enters the viewport
activeIndexnumber | nullControlled focus index; when omitted it is uncontrolled
defaultActiveIndexnumber | nullnullInitial focus index in uncontrolled mode
onActiveChange(index: number | null) => voidFired when the focus changes
dimOpacitynumber0.55How much the unfocused image is dimmed while focused (0–1)
classNamestringCustom classes for the outer figure

LeaderCaption

FieldTypeDescription
xnumberHorizontal position of the detail point (percentage of the image width, 0–100)
ynumberVertical position of the detail point (percentage of the image height, 0–100)
titleReact.ReactNodeCaption title
descriptionReact.ReactNodeCaption body (optional)
idstringCustom key / identifier (optional; defaults to the index)

How it works

  • The leader line endpoints are measured live with getBoundingClientRect against the actual positions of the badge and the detail point in the image, and a ResizeObserver watches the container and the sidebar, so everything realigns after a resize, a reflow, or a font load.
  • On reveal, the leader lines emerge one by one as pathLength animates from 0 to 1, and each number "lights up" in turn once its line is more than halfway drawn.
  • Hovering or focusing any caption makes a focus halo appear at that detail point while the rest of the image dims under a radial gradient; on mouse out the halo fades away in place rather than snapping back to the origin.

Accessibility

  • The caption sidebar is wrapped semantically in <figure> / <figcaption>, and each caption is a focusable <button>, so keyboard Tab moves through them one by one and triggers the matching focus effect on the image.
  • The leader line <svg>, the focus halo, and the small number circles are all decorative only (aria-hidden), so they do not interfere with the screen reader; the caption text itself is readable content.
  • When the user has "reduce motion" enabled at the system level, the leader lines and numbers are rendered in their final state with no drawing animation, and the focus dimming switches instantly.

On this page