Figure
Wrap images and media in a figure that keeps to the text column or breaks out to container, screen or card width.
On this page
Figure wraps an image, video or embed in a <figure> and controls how wide it gets. By default it follows the column it sits in; width lets it break out of a narrow column to the container, the whole viewport, or the inner edges of a card.
import { Figure, Heading, Image, Paragraph, Section } from '@systhemaui/next'
export default function Demo() {
return (
<Section padding={3}>
<Heading.h2>Ten days on the coast</Heading.h2>
<Paragraph>
A figure placed in a section's text column breaks out to the container width, while the text
around it keeps its comfortable measure.
</Paragraph>
<Figure width="container">
<Image
src="/demo-assets/coast.webp"
alt="Waves breaking on a rocky coast"
width={1800}
height={1200}
aspectRatio="21/9"
/>
<figcaption className="text-small color-label">The northern cliffs at low tide.</figcaption>
</Figure>
<Paragraph>The text continues in its column after the figure.</Paragraph>
</Section>
)
}ImportLink to this section
import { Figure } from '@systhemaui/next'In a React app without Next.js, import it from @systhemaui/react.
Width and breakout behaviourLink to this section
width | Class | Result |
|---|---|---|
default | figure-w-default | Fills the column it sits in. No breakout rule. |
container | figure-w-container | Breaks out of any narrower ancestor to --container-width (or the viewport, when narrower). |
screen | figure-w-screen | Breaks out to the full viewport width and never animates in on scroll. |
card | figure-w-card | Breaks out to the inner edges of the surrounding card. Outside a card it has no effect. |
The breakouts use negative margins (calc(50% - 50vw) for screen), so the figure's grid cell keeps its place and the content before and after it stays aligned.
ExamplesLink to this section
Default, container and screenLink to this section
The three widths inside a section with padding={3}. Switch the preview to the phone viewport: below md the column has no inset, so default and container look the same there.
import { Figure, Image, Paragraph, Section } from '@systhemaui/next'
export default function Demo() {
return (
<Section padding={3}>
<Paragraph type="label">width="default"</Paragraph>
<Figure>
<Image src="/demo-assets/city.webp" alt="" width={1920} height={1080} aspectRatio="21/9" />
</Figure>
<Paragraph type="label">width="container"</Paragraph>
<Figure width="container">
<Image
src="/demo-assets/mountains.webp"
alt=""
width={1920}
height={1080}
aspectRatio="21/9"
/>
</Figure>
<Paragraph type="label">width="screen"</Paragraph>
<Figure width="screen">
<Image src="/demo-assets/coast.webp" alt="" width={1800} height={1200} aspectRatio="21/9" />
</Figure>
</Section>
)
}A screen figure that is the first or last child of a Section or an Article removes that container's padding on its side, so it sits flush against the edge. container figures keep the padding.
Card widthLink to this section
width="card" cancels the card's horizontal padding through the --card-width-offset-x variable every card publishes. As the first or last child, the card also drops its own top or bottom padding, so the image meets the card's rounded edge while the text keeps its padding.
import { Card, Column, Columns, Figure, Heading, Image, Paragraph } from '@systhemaui/next'
export default function Demo() {
return (
<Columns columns={2} gap="md" className="w-full max-w-3xl">
<Column>
<Card variant="default">
<Figure width="card">
<Image
src="/demo-assets/dunes-portrait.webp"
alt=""
width={1200}
height={1600}
aspectRatio="16/10"
/>
</Figure>
<Heading.h4>Desert crossing</Heading.h4>
<Paragraph>The image bleeds to the card's edges.</Paragraph>
</Card>
</Column>
<Column>
<Card variant="default">
<Figure>
<Image
src="/demo-assets/forest-portrait.webp"
alt=""
width={1200}
height={1600}
aspectRatio="16/10"
/>
</Figure>
<Heading.h4>Forest trail</Heading.h4>
<Paragraph>The default width keeps the card's padding.</Paragraph>
</Card>
</Column>
</Columns>
)
}The figure-rendering Payload media blocks (image, video, YouTube, Google Maps and embed) offer the same card width when they sit directly inside a card. See Media inside a card.
CaptionsLink to this section
Put a <figcaption> after the media. Figure adds no caption styling, so style it with a text utility such as text-small.
PropsLink to this section
width defaults to 'default'. All other props go to the <figure> element, including dangerouslySetInnerHTML, which renders the figure without children (the embed block converter uses it).
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | |
width | 'default' | 'container' | 'screen' | 'card' | null | - | |
className | string | - | |
…and all <figure> attributes |
HTML and CSSLink to this section
The same figure in plain HTML:
<figure class="figure-w-container">
<img class="media aspect-21/9 object-cover" src="/images/coast.webp" alt="A rocky coast" />
<figcaption class="text-small">The northern cliffs at low tide.</figcaption>
</figure>w-card is an exact alias of figure-w-card for any element. figure-w-full is a deprecated alias of figure-w-screen, kept for older content; systhema upgrade rewrites it in your source. See Figure width.
| Class | Styles |
|---|---|
figure-w-container | width: min(var(--container-width), 100vw);max-width: 100vw;margin-left: calc((100% - min(var(--container-width), 100vw)) / 2);margin-right: calc((100% - min(var(--container-width), 100vw)) / 2);margin-block: 0; |
figure-w-screen | .py-section:where(:has( > .container > .richtext > .figure-w-screen:first-child, > .container > .richtext > .figure-w-full:first-child, > .container > .figure-w-screen:first-child, > .container > .figure-w-full:first-child)) { padding-top: 0; }+ 2 more rules |
figure-w-full | .py-section:where(:has( > .container > .richtext > .figure-w-screen:first-child, > .container > .richtext > .figure-w-full:first-child, > .container > .figure-w-screen:first-child, > .container > .figure-w-full:first-child)) { padding-top: 0; }+ 2 more rules |
figure-w-card | & { width: calc(100% + var(--card-width-offset-x, 0px) * 2); max-width: calc(100% + var(--card-width-offset-x, 0px) * 2); margin-inline: calc(var(--card-width-offset-x, 0px) * -1); border-radius: 0; }& :is(.media, .media-wrapper) { border-radius: 0; } |
w-card | & { width: calc(100% + var(--card-width-offset-x, 0px) * 2); max-width: calc(100% + var(--card-width-offset-x, 0px) * 2); margin-inline: calc(var(--card-width-offset-x, 0px) * -1); border-radius: 0; }& :is(.media, .media-wrapper) { border-radius: 0; } |
Next.jsLink to this section
@systhemaui/next re-exports Figure from @systhemaui/react unchanged. Put the @systhemaui/next Image or Video inside it.
AccessibilityLink to this section
A <figcaption> names the figure for assistive technology, so it can describe the image while alt stays short, or the image can take alt="" when the caption already says everything. Don't repeat the same text in both.