Skip to content
Primitiv home
Framework
Consumption mode

Figure

stableSource Figma

A <figure>/<figcaption> pairing that wraps self-contained content (an image, illustration, chart, code listing, table) with an optional caption in one of three positions — below (default), above, or overlaid on the media. size scales the caption's type off the body/{size} ramp (the media is size-independent, as in Figma); the caption's own align matches Figma's Figcaption Align axis.

Playground

Density

Preview

16 : 9 media
Figure 1. A placeholder image.
Size
Caption position
Align
import { Figure } from "@/components/ui/figure";
<Figure captionPosition="below" size="md">  <Figure.Media>    <img src="/chart.png" alt="Quarterly growth" />  </Figure.Media>  <Figure.Caption align="start">Figure 1. Quarterly growth.</Figure.Caption></Figure>

Density is set by a data-density ancestor — the Context system, not a Figure prop.

Installation

npx primitiv add figure

Import

import { Figure } from "@/components/ui/figure";

No headless primitive — this one ships only as a copied styled surface, so primitiv add is the only way in, whichever mode you are reading.

Anatomy

<Figure>  <Figure.Media />  <Figure.Caption /></Figure>

Props

Figure

Extends HTMLElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultDescription
captionPosition"below" | "above" | "overlay"belowCaption placement relative to the media.
size"xs" | "sm" | "md" | "lg" | "xl"mdCaption type size, matching Figma's Size axis — it drives the nested Figcaption's body/{size} scale only; the media is size-independent. data-density scales each size further.

Figure.Media

Headless only — the copied file renders this part for you and exports no separate component, so there is nothing to import under Styled.

Extends HTMLDivElement — every native attribute of that element is accepted and forwarded.

No props of its own.

Figure.Caption

Headless only — the copied file renders this part for you and exports no separate component, so there is nothing to import under Styled.

Extends HTMLElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultDescription
align"start" | "center" | "end"startCaption text alignment, matching Figma's Figcaption Align axis. Direction-aware — start/end follow the reading direction, flipping under RTL.

Styling contract

--primitiv-figure-caption-gap--primitiv-figure-media-radius--primitiv-figure-caption-color--primitiv-figure-overlay-scrim--primitiv-figure-overlay-caption-color--primitiv-figure-font-family--primitiv-figure-font-size--primitiv-figure-font-weight--primitiv-figure-line-height

Accessibility

  • Figure renders a real <figure> and Figure.Caption a real <figcaption>, so assistive tech associates the caption with its content — the caption is not just text that happens to sit nearby.
  • The media inside still carries its own meaning: an <img> needs its alt, a chart its description. The <figcaption> names the figure, it does not replace the media's own alternative text.
  • The overlay caption sits over the media with a scrim for contrast, but the association is structural (figure/figcaption), so it survives regardless of where the caption is placed.

Examples

Caption position

captionPosition places the <figcaption>: below (the default) and above sit it outside the media, while overlay floats it over the bottom of the media with a scrim — the hero-image treatment. Figure.Caption takes an align for left/centre/right within the row.

Density
16 : 9 media
captionPosition="below"
16 : 9 media
captionPosition="overlay"
import { Figure } from "@/components/ui/figure";
<Figure captionPosition="overlay">  <Figure.Media>    <img src="/cover.jpg" alt="" />  </Figure.Media>  <Figure.Caption align="center">On the cover</Figure.Caption></Figure>