Skip to content

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/react

For 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.

Reference

Propsannotations, src, media… (9)

Props

AnnotatedImage props
PropType
annotationsRequiredImageAnnotation[]–
srcstring

The image. Leave it out and pass media for anything else.

–
altstring

The image's text alternative.

default ''

''
mediaReactNode

Your own media in place of the image: a video, an illustration, a canvas. Points are placed over it.

–
captionReactNode

Under the figure, as its caption.

–
valuestring | null

The lit annotation's id (controlled); null for none.

–
defaultValuestring | null

The annotation lit at first.

default null

null
onValueChange(id: string | null) => void

Fires when a callout is pressed: its id, or null when it's pressed again.

–
labelstring

Names the list of labels. Default: the labels' "Annotations".

–
Other props pass through: extends ComponentProps<'figure'>.

Annotations

ImageAnnotation props
PropType
idRequiredstring–
xRequirednumber

Where the point sits across the media, 0–100 from its left edge.

–
yRequirednumber

Where the point sits down the media, 0–100 from its top.

–
titleRequiredReactNode

The label's first line: "Reefer unit".

–
detailReactNode

A 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

Motion tokens
MomentTokens
Calloutspop in one after another as the figure scrolls into view
spring.snappy

0.2s, bounce 0

0.2s, bounce 0
Leader linesdraw from the point to the label
duration.slowease.emphasized

340ms · cubic-bezier(0.6, 0, 0.1, 1)

340mscubic-bezier(0.6, 0, 0.1, 1)
Labelsfade in as their line lands
duration.baseease.enter

220ms · cubic-bezier(0.16, 1, 0.3, 1)

220mscubic-bezier(0.16, 1, 0.3, 1)
Resizelabels glide to their new places
duration.baseease.standard

220ms · 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; caption is its figcaption. Give the image its alt, or your media its 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" (annotations in 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.