FairGarden Design

Figure

A figure with its caption: the media, then a label ("Fig. 3"), a caption that says what to notice, and a credit in the same run. Text never sits on the image.

import { Figure, FigureCaption, FigureMedia } from '@fairgarden-private/design/components/Figure'

The label is set in the mono data face, the caption in caption type and the credit ("Photo: Name / Program") in small type, all in the muted ink. Number figures only where the text refers to them, but give every figure a caption or a credit, because print needs one.

Photo, technical figure and plate

Photo, technical figure and plate

photo is the default: an editorial photo with an 8 px radius (squared when it runs full-bleed) and its caption right-flush below it. technical is for charts, diagrams and drawings: square, never scaled up, with the caption left-aligned below and the label on its own line; framed adds a 1 px rule frame. plate is a specimen plate: the media becomes a nested white face over a fine dot grid, framed in that face's own edge color. It is a light island inside a field or the night band, so a two-ink chart can sit on it on any ground. number generates "Fig. 3", numbering="catalog" gives "Figure 001", and detail marks a crop.

The reed line marks the high-water mark of the spring flood. Photo: Ada Reyes / Marsh Program
Fig. 3 Leaf of the swamp white oak, lobes shallow and rounded.
Figure 001 (DETAIL) Specimen from the north meadow collection.
FigureKinds.tsx
import { Figure, FigureCaption, FigureMedia } from '@fairgarden-private/design/components/Figure'
import styles from './kinds.module.css'

/** Stands in for a photo: a flat drawing of a marsh at dusk. */
function MarshPhoto() {
  return (
    <svg className={styles.photo} viewBox="0 0 480 320" role="img" aria-label="A marsh at dusk">
      <rect className={styles.sky} width="480" height="320" />
      <path className={styles.hills} d="M0 190 120 140l110 40 90-50 160 60v130H0z" />
      <path className={styles.water} d="M0 230h480v90H0z" />
      <path className={styles.reeds} d="M40 320v-80M52 320v-96M64 320v-70M410 320v-88M424 320v-104M436 320v-76" />
    </svg>
  )
}

/** Stands in for a technical drawing: a leaf in outline with its dimension line. */
function LeafDrawing() {
  return (
    <svg className={styles.drawing} viewBox="0 0 320 200" role="img" aria-label="Leaf outline, 18 cm long">
      <path className={styles.line} d="M40 100C90 30 230 30 280 100 230 170 90 170 40 100Z" />
      <path className={styles.line} d="M40 100h240M110 100l40-40M170 100l40-40M110 100l40 40M170 100l40 40" />
      <path className={styles.line} d="M40 186h240M40 180v12M280 180v12" />
    </svg>
  )
}

/** The three kinds: an editorial photo, a framed technical figure and a specimen plate. */
export function FigureKinds() {
  return (
    <div className={styles.stack}>
      <Figure>
        <FigureMedia>
          <MarshPhoto />
        </FigureMedia>
        <FigureCaption credit="Photo: Ada Reyes / Marsh Program">
          The reed line marks the high-water mark of the spring flood.
        </FigureCaption>
      </Figure>
      <Figure kind="technical" framed>
        <FigureMedia>
          <LeafDrawing />
        </FigureMedia>
        <FigureCaption number={3}>Leaf of the swamp white oak, lobes shallow and rounded.</FigureCaption>
      </Figure>
      <Figure kind="plate">
        <FigureMedia>
          <LeafDrawing />
        </FigureMedia>
        <FigureCaption number={1} numbering="catalog" detail>
          Specimen from the north meadow collection.
        </FigureCaption>
      </Figure>
    </div>
  )
}

Side caption

Side caption

With sideCaption, the caption moves into the adjacent column once the figure itself is at least 768 px wide, top-aligned beside the media. Narrower, it stays below.

Fig. 4 Water level peaks in April, a month earlier than in 2020. Source: County water survey
FigureSideCaption.tsx
import { Figure, FigureCaption, FigureMedia } from '@fairgarden-private/design/components/Figure'
import styles from './side-caption.module.css'

/** From 768 px of the figure's own width, the caption moves beside the media. */
export function FigureSideCaption() {
  return (
    <Figure kind="technical" sideCaption className={styles.figure}>
      <FigureMedia>
        <svg className={styles.chart} viewBox="0 0 480 200" role="img" aria-label="Water level by month, highest in April">
          <path className={styles.axis} d="M40 20v160h420" />
          <path className={styles.series} d="M40 150 110 120l70-70 70 20 70 40 70 10 70 20" />
        </svg>
      </FigureMedia>
      <FigureCaption number={4} credit="Source: County water survey">
        Water level peaks in April, a month earlier than in 2020.
      </FigureCaption>
    </Figure>
  )
}

In print

A figure never separates from its caption. Content photos print in grayscale inside a 0.75 pt frame, no taller than 100 mm; decorative photos (empty alt) are left out. The plate's dot grid drops, and its frame prints black.

API Reference

Figure

The figure. Put a FigureMedia and a FigureCaption inside. Every figure gets a caption or a credit, for print; figure and caption never separate across printed pages.

PropTypeDescription
framed
boolean | undefined

Technical figures: a --border-size-1 --role-rule frame. Default false.

kind
'photo' | 'technical' | 'plate' | null | undefined

photo (default): an editorial photo, --ds-radius-8, the caption right-flush below it. technical: a chart, diagram or drawing, square, the caption left-aligned below it with the label on its own line. plate: a specimen plate on a nested white face over a fine dot grid, framed in the face’s --role-edge; a two-ink chart may sit on it on any ground.

primary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale: caption, label, credit and frames. Never defaulted; omitted, it inherits the scope [D133].

secondary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale: unused by the figure itself. Never defaulted.

sideCaption
boolean | undefined

From 768 px of the figure’s width, the caption sits in the adjacent column, top-aligned, beside the media. Default false.

FigureMedia

The media box: an img, svg or chart. Photos fill the column; technical figures keep their native width and never scale up [D78]. A decorative photo (empty alt) is left out of print. On a plate it is the nested white face, which takes the figure’s primary and secondary.

FigureCaption

The caption: the label in type-data, the caption text (the children) in type-caption and the credit in type-small, all --role-muted. Number figures only where the text refers to them.

PropTypeDescription
label
React.ReactNode | undefined

Replaces the generated label entirely.

credit
React.ReactNode | undefined

The credit, in the same run: “Photo: Name / Program” (type-small).

detail
boolean | undefined

Adds “(DETAIL)” to the label, for a crop.

number
number | string | undefined

The figure number; renders “Fig. 3″ (or “Figure 003″ with numbering="catalog").

numbering
'short' | 'catalog' | undefined

short (default): “Fig. 3″, “Fig. 3.2″. catalog: “Figure 001″ (menus, specimen sets, product sheets).

Additional types

figure
type figure = figure
FigureCaptionProps

Props for FigureCaption: figcaption props plus the numbering and the credit.

type FigureCaptionProps = React.ClassAttributes<HTMLElement> &
  React.HTMLAttributes<HTMLElement> & {
    number?: number | string;
    numbering?: 'short' | 'catalog';
    label?: React.ReactNode;
    detail?: boolean;
    credit?: React.ReactNode;
  }
FigureMediaProps

Props for FigureMedia: div props.

type FigureMediaProps = React.ComponentPropsWithRef<'div'>
FigureProps

Props for Figure: figure props and the kind, frame, caption-placement and color axes.

type FigureProps = React.ClassAttributes<HTMLElement> &
  React.HTMLAttributes<HTMLElement> & {
    kind?: 'photo' | 'technical' | 'plate' | null;
    framed?: boolean;
    sideCaption?: boolean;
    primary?:
      | 'ruby'
      | 'olive'
      | 'sage'
      | 'slate'
      | 'sand'
      | 'gray'
      | 'mauve'
      | 'brown'
      | 'bronze'
      | 'gold'
      | 'red'
      | 'crimson'
      | 'tomato'
      | 'pink'
      | 'plum'
      | 'indigo'
      | 'iris'
      | 'violet'
      | 'purple'
      | null;
    secondary?:
      | 'ruby'
      | 'olive'
      | 'sage'
      | 'slate'
      | 'sand'
      | 'gray'
      | 'mauve'
      | 'brown'
      | 'bronze'
      | 'gold'
      | 'red'
      | 'crimson'
      | 'tomato'
      | 'pink'
      | 'plum'
      | 'indigo'
      | 'iris'
      | 'violet'
      | 'purple'
      | 'amber'
      | 'blue'
      | 'cyan'
      | 'grass'
      | 'green'
      | 'jade'
      | 'lime'
      | 'mint'
      | 'orange'
      | 'sky'
      | 'teal'
      | 'yellow'
      | null;
  }

Specification: DESIGN-SYSTEM.md §8.5 (figures and captions).