Annotated Image
Point at parts of an image with numbered callouts joined by leader lines to their labels.
import { AnnotatedImage, ImageAnnotation } from '@youj/react'
Installation & usage
npm i @youj/reactFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use an annotated image to say what matters in a picture: a vehicle's sensors, the parts of a product, the regions of a screenshot. Each point carries its number and a leader line runs to its label in the gutter beside the media, so the eye goes from the point along the line to the words. For a sequence of screens to step through, use a Carousel.
import { AnnotatedImage, type ImageAnnotation } from '@youj/react'
const notes: ImageAnnotation[] = [
{ id: 'reefer', x: 71, y: 34, title: 'Reefer unit', detail: '34.1°F · set 34°F' },
{ id: 'load', x: 40, y: 44, title: 'Load', detail: '14 of 26 pallets' },
]
<AnnotatedImage src="/truck.jpg" alt="Refrigerated truck, side view" annotations={notes} />A point sits at x and y, from 0 to 100 across and down the media. Its label goes in the gutter on the point's side, or the one side names; labels in a gutter keep their order and are nudged apart so they never overlap, and they follow the media as it resizes. Under 40rem of its own width the gutters fold into a numbered list under the media, without lines. Pass media instead of src for a video, an illustration or a canvas, and caption for a line under the figure.
Hovering or focusing a callout, or hovering its label, lights the pair. Pressing a callout pins it: value, defaultValue and onValueChange hold the pinned id, or null.
Description
A refrigerated truck drawn in the theme's tones, with numbered callouts joined by leader lines to labels in the gutters.
Code
annotated-image-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsannotations, src, media… (9)
Props
| Prop | Type | |
|---|---|---|
annotationsRequired | ImageAnnotation[] | – |
src | stringThe image. Leave it out and pass | – |
alt | stringThe image's text alternative. default '' | '' |
media | ReactNodeYour own media in place of the image: a video, an illustration, a canvas. Points are placed over it. | – |
caption | ReactNodeUnder the figure, as its caption. | – |
value | string | nullThe lit annotation's id (controlled); | – |
defaultValue | string | nullThe annotation lit at first. default null | null |
onValueChange | (id: string | null) => voidFires when a callout is pressed: its id, or | – |
label | stringNames the list of labels. Default: the labels' "Annotations". | – |
Other props pass through: extends ComponentProps<'figure'>. | ||
Annotations
| Prop | Type | |
|---|---|---|
idRequired | string | – |
xRequired | numberWhere the point sits across the media, 0–100 from its left edge. | – |
yRequired | numberWhere the point sits down the media, 0–100 from its top. | – |
titleRequired | ReactNodeThe label's first line: "Reefer unit". | – |
detail | ReactNodeA second line: "34.1°F · set 34°F". | – |
side | 'left' | 'right'The gutter its label sits in. Default: the side its point is on. | – |
StylingParts and data attributes
Styling
Parts: annotated-image, annotated-image-stage (with data-layout wide or stacked), annotated-image-media, annotated-image-callout, annotated-image-lines, annotated-image-line, annotated-image-labels, annotated-image-label (with data-side), annotated-image-title, annotated-image-detail and annotated-image-caption; the lit pair carries data-active. The gutters are set by --annotated-gutter (between 9rem and 13rem).
MotionCallouts, lines, labels · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Calloutspop in one after another as the figure scrolls into view | spring.snappy0.2s, bounce 0 | 0.2s, bounce 0 | |
| Leader linesdraw from the point to the label | duration.slowease.emphasized340ms · cubic-bezier(0.6, 0, 0.1, 1) | 340mscubic-bezier(0.6, 0, 0.1, 1) | |
| Labelsfade in as their line lands | duration.baseease.enter220ms · cubic-bezier(0.16, 1, 0.3, 1) | 220mscubic-bezier(0.16, 1, 0.3, 1) | |
| Resizelabels glide to their new places | duration.baseease.standard220ms · cubic-bezier(0.2, 0, 0, 1) | 220mscubic-bezier(0.2, 0, 0, 1) |
Reduced motion
Drawn in place; labels jump to their new places.
Reduced motion
Callouts, lines and labels are drawn in place, and labels jump when the media resizes.
AccessibilityFigure, callouts, list
Accessibility
- It is a
figure;captionis itsfigcaption. Give the image itsalt, or yourmediaits own name. - Callouts are buttons in number order, each named by its number and described by its label; pressing one sets
aria-pressed. They have a wider hit area than the disc. - The labels are an ordered list named "Annotations" (
annotationsin the labels); the lines are decorative. - The media never mirrors, so neither do the points; in RTL the labels' text runs right to left in the same gutters.