Figure
Sets one thing to look at, a picture, a chart, a specimen or a live example, on a stage of its own, framed or not, with a caption that says what it shows, and optionally a title with your controls above and a joined block beneath.
- Category
- Containment
- Tokens
comp.figure- Level
- Base
- Web
tanrei-figureschedule - SwiftUI
TanreiFigureradio_button_unchecked - Jetpack Compose
TanreiFigureradio_button_unchecked - Flutter
TanreiFigureradio_button_unchecked - React Native
Figureradio_button_unchecked
Generated from docs/status/status.json, specs/figure.spec.json
Outlined, filled and plain
A figure shows one thing that is looked at rather than read: a picture in an article, a chart in a report, a preview of what a setting will produce, a specimen. It sets that thing apart and says what it is. The frames around the live examples on this site are figures.
It is not a card. A card groups content and actions about one subject and may be pressed as a whole. A figure shows one thing, is never pressed, and puts its caption outside its frame.
On the web it is <tanrei-figure> around a native <figure>. The element has no shadow root, so the figure and its <figcaption> are the document’s own: read without script, by a crawler and by a reader mode, and named by the browser. The other platforms have not implemented it yet.
<link rel="stylesheet" href="@tanrei/web/figure.css" />
<tanrei-figure>
<figure>
<div data-part="frame">
<header>
<p data-part="title">Revenue by quarter</p>
<div data-part="actions">
<tanrei-button variant="text" label="Open in a new tab"><span slot="icon">open_in_new</span></tanrei-button>
</div>
</header>
<div data-part="stage">
<img src="revenue.svg" alt="A bar chart: revenue rises in every quarter." />
</div>
</div>
<figcaption>Every region grew; the north grew fastest.</figcaption>
</figure>
</tanrei-figure>
<script type="module">
import '@tanrei/web/components/figure';
</script>import { Figure } from '@tanrei/react';
import '@tanrei/web/figure.css';
<Figure variant="plain">
<figure>
<img src={photo.src} alt={photo.alt} />
<figcaption>{photo.caption}</figcaption>
</figure>
</Figure>@tanrei/web/figure.css draws the whole figure before any script runs, so a page of figures does not move when the script arrives. The element adds the same stylesheet to the page by itself.
| Part | On the web |
|---|---|
| The frame | <div data-part="frame">, the first child of the <figure>. It holds the header, the stage and the attachment. |
| The title | An element with data-part="title" in the frame’s <header>: a <p>, or the heading your page’s outline needs. |
| The actions | <div data-part="actions"> in the <header>, around your own controls. |
| The stage | <div data-part="stage">, around what is shown. |
| The attachment | Any element with data-part="attachment" after the stage: a code block, a table, a description list. |
| The caption | The <figcaption>, after the frame. |
Every part but the stage is optional. A lone <img>, <picture>, <video>, <svg> or <canvas> can stand in place of the stage element, and a figure with no frame element at all, <figure><img …><figcaption>…</figcaption></figure> as a Markdown renderer writes it, is drawn too: the picture is then its own frame.
A <tanrei-figure> that holds no <figure> is given one when the element is defined. Its children become the stage, except those that name a part with slot (title, actions, caption, attachment). The headline and caption attributes are written as the title and the caption.
<tanrei-figure headline="Variants" caption="A filled button.">
<tanrei-button variant="filled">Save</tanrei-button>
</tanrei-figure>This form is shorter, and it is drawn only once the script has run. Write the parts yourself where the page must not move, and with a framework that owns the element’s children (React), because the element moves them.
A title, a control, a joined block and a caption
Variants
<tanrei-button variant=“filled”>Filled</tanrei-button>Not inset: what is shown reaches the frame's edges
Wider than its stage: it scrolls there, and the stage is a focus stop
Grid, dots, a grid that fades toward the centre, and a checkerboard
Anatomy
| Part | Description |
|---|---|
1 container | The frame: the shape that holds the header, the stage and the attachment, with a card's corners. Filled with the surface and edged in the outlined look, filled with a quiet tone in the filled look, and neither filled nor edged in the plain look. It carries no state: nothing about it answers a pointer, a press or focus. |
2 outline · optional | The outlined look's edge: one hairline inside the frame. |
3 header · optional | The row at the top of the frame that holds the title and the actions. Drawn only where the figure has one of them. |
4 title · optional | A short line that names what is shown, at the start of the header. Text, never a heading by itself. |
5 actions · optional | The product's own controls at the end of the header (which variant, which theme, open it elsewhere). The figure lays them out and nothing else. |
6 stage | Where the thing shown stands: the product's content, padded inside the frame or reaching its edges, scaled down where it can shrink and scrolled sideways inside the stage where it cannot. |
7 pattern · optional | The ground pattern behind what is shown, where the product asks for one: grid lines, a dot grid, a grid that fades toward the centre, or a checkerboard, drawn across the stage's box over the frame's fill and under the content. Decoration only. |
8 attachment · optional | The product's block joined under the stage, inside the frame: the code an example was made from, the data of a chart, the facts about a specimen. |
9 divider · optional | The line between the header and the stage and between the stage and the attachment, while the separation is a divider and the look has a frame. |
10 caption · optional | The text below the frame that says what is shown, in secondary text on the surface the figure sits on. |
11 focus-ring · optional | The system's focus indicator inside the stage's edge, drawn only while the stage scrolls and has keyboard focus. |
Properties
| Property | Type | Default | Description |
|---|---|---|---|
title | string | "" | The title: a short line above the stage that names what is shown ("Variants", "Revenue by quarter"). Plain text, never a heading; the title slot takes the product's own content in its place. It is the figure's accessible name where it is given. |
caption | string | "" | The caption: the text below the frame that says what is shown. The caption slot takes the product's own content in its place (a caption with a link, inline code or a tag). It is the figure's accessible name where there is no title, and its description where there is one. |
label | string | "" | The accessible name of a figure that draws neither a title nor a caption, in the product's language. Never drawn. Ignored where a title or a caption is given. |
variant | outlined | filled | plain | "outlined" | How the stage is set apart. outlined: the surface inside one hairline, the default. filled: a quiet tone and no edge, where one more line would crowd the page. plain: no fill, no edge, no padding and no line, for a picture that is its own frame. A design system may add values. |
inset | boolean | true | Whether the stage is padded inside the frame. True for a specimen or a live example, which needs room around it; false for a picture, a chart or a map that should reach the frame's edges and be clipped by its corners. The plain look has no frame to be inset from and is never padded. |
separation | auto | divider | gap | "auto" | How the parts inside the frame are told apart. auto takes comp.figure.separation, which follows the system's choice. divider draws a line between the header and the stage and between the stage and the attachment, the attachment flush with the frame. gap draws no line and stands the attachment inside the frame with its own shape. The plain look has no frame and draws neither. |
pattern | none | grid | dots | grid-fade | checkerboard | "none" | The ground pattern drawn behind what is shown, across the stage. none: the plain fill, the default. grid: lines in both directions, the ground of a layout or a design canvas. dots: a dot at every crossing of that grid, a quieter ground for a drawing or a diagram. grid-fade: the grid, clear around the centre and growing toward the edges, so that a specimen at the centre stands on the plain fill and the grid frames it. checkerboard: small squares, the ground that shows where a picture is transparent. Decoration only: it says nothing to assistive technology, and the plain look and forced colors draw none. |
Slots
| Slot | Description |
|---|---|
default | What is shown, in the stage: a picture, a video, a chart, a diagram, a specimen, a live example of the product's own components. The figure does not style, crop, mirror or operate it. |
title | The product's own title content in place of the title prop: text with inline content, or a heading of the level the page's outline needs. The figure draws it in the title's style and changes neither its role nor its level. |
actions | The controls at the end of the header: the product's own icon-only Buttons, tabs, a select or links. The figure lays them out in a row and does nothing else to them. |
caption | The product's own caption content in place of the caption prop: text with links, inline code, a keyboard shortcut or a tag. |
attachment | The block joined under the stage, inside the frame: a code block, a table, a description of the specimen. It is the product's component and keeps its own styles. |
States
Behavior
Requirements in EARS form. Every platform implementation is tested against these ids.
| ID | Requirement |
|---|---|
FIG-1 | THE figure SHALL draw, from top to bottom, a frame that holds its header where it has one (FIG-7), its stage and its attachment where it has one (FIG-11), and below the frame its caption where it has one (FIG-10); the frame SHALL have corners comp.figure.container.shape eased by sys.shape.corner-smoothing, SHALL clip what it holds to those corners, SHALL be exactly as tall as what it holds, with no height and no minimum height of its own, and SHALL draw no elevation. |
FIG-2 | WHILE variant is outlined THE figure SHALL fill its frame with comp.figure.outlined.container.color and draw an edge comp.figure.outline.width wide in comp.figure.outline.color inside the frame's size, following its corners, without changing the frame's size or the place of what it holds; WHILE variant is filled it SHALL fill the frame with comp.figure.filled.container.color and draw no edge; WHILE variant is plain it SHALL draw no fill (comp.figure.plain.container.color), no edge, no padding and no line, so that what is shown is its own frame, and SHALL still clip the stage to comp.figure.container.shape. |
FIG-3 | THE figure SHALL place the content of its default slot in the stage and draw nothing of its own there, between the frame's fill and that content or over it, but the ground pattern the product asks for (FIG-26), under the content. WHILE inset is true and variant is not plain THE stage SHALL be padded by comp.figure.stage.padding-block above and below and comp.figure.stage.padding-inline before and after; WHILE inset is false or variant is plain THE stage SHALL have no padding, so that the content reaches the frame's edges. WHILE variant is outlined or filled THE figure SHALL hand the content comp.figure.content.color as the platform's inherited foreground and SHALL place content narrower or shorter than the stage at the stage's centre; WHILE variant is plain it SHALL hand no color and SHALL place content narrower than the stage at the stage's start edge, in line with the text around the figure. Several pieces of content SHALL be stacked in the order given with no space of the figure's between them: how several specimens are arranged is the product's layout. |
FIG-4 | WHEN content that has proportions of its own and can shrink — a picture, a video, a drawing — is wider than the stage's inner width THE figure SHALL scale it down to that width with its proportions kept (PRS-15), and SHALL NOT scale any content above its own size, crop it or stretch it: an aspect ratio and how a picture fills a box are the content's own. |
FIG-5 | WHEN content that cannot shrink is wider than the stage's inner width THE stage SHALL scroll it sideways inside its own box, beginning at the start edge and with that content placed at the start edge rather than at the centre, the stage's inline padding scrolling with the content so that its last part can be brought clear of the frame's edge; the header, the attachment and the caption SHALL NOT scroll with it, the figure SHALL never be wider than its layout and SHALL never make what holds it scroll sideways, and THE stage SHALL NOT scroll up and down: it is as tall as its content. |
FIG-6 | WHILE the stage scrolls (FIG-5) THE stage SHALL be one stop in the focus order, before anything inside it that takes focus, exposed as a group that carries the figure's name (FIG-16); WHILE it has keyboard focus it SHALL draw the system's focus indicator inside its edge — sys.focus.outer-width wide in the color role focus-ring, set in from the edge by sys.focus.offset so that nothing clips it, following the frame's corners where the stage meets them, and never animated — and the left and right arrow keys, Home and End SHALL scroll it as the platform scrolls a region, the arrow keys moving the view left and right in either layout direction; focus gained by pointer or touch SHALL NOT show the indicator; WHILE the stage does not scroll it SHALL NOT be a stop in the focus order and SHALL expose no group of its own. |
FIG-7 | WHERE the figure has a title (FIG-8) or content in its actions slot THE figure SHALL draw a header at the top of the frame: one row with the title at the start and the actions at the end, comp.figure.header.gap apart, padded by comp.figure.header.padding-block above and below, by comp.figure.header.padding-inline at the start, and at the end by comp.figure.header.padding-inline-end-with-actions where it holds actions and by comp.figure.header.padding-inline where it does not; the title SHALL take the room the actions leave and wrap inside it, the actions SHALL keep their own sizes, and WHEN the actions and one word of the title no longer fit in one row THE actions SHALL move to a row of their own below the title, at the start edge, comp.figure.header.gap below it. WHERE the figure has neither THE figure SHALL draw no header, no space for one and no line under one. |
FIG-8 | THE figure SHALL draw its title — the content of the title slot where it holds any, and the title prop otherwise — in comp.figure.title.typescale and comp.figure.title.color, wrapping and never cut; the title prop SHALL be exposed as plain text and never as a heading, and content given in the title slot SHALL keep the role and the level the product gave it (a heading stays the heading it is) while it is drawn in the title's style. IF both the prop and the slot are given THE slot SHALL be drawn and the prop ignored. THE figure SHALL set the title's letter-spacing by the script of its text, as every title is set (PRS-20): no negative letter-spacing WHERE its language is Chinese, Japanese or Korean, and none WHERE its script joins its letters or stacks marks on them. |
FIG-9 | THE figure SHALL lay out the content of its actions slot in one row at the end of the header, in the order given, comp.figure.header.gap apart, each centred on the height of that row, and SHALL do nothing else to it: the controls keep their own sizes, colors, states, focus indicators, targets and names, the figure listens to none of their events, and what they change in the stage — its theme, its direction, the variant on show — is the product's own doing. |
FIG-10 | WHERE the figure has a caption — the content of the caption slot where it holds any, and the caption prop otherwise — THE figure SHALL draw it below the frame, comp.figure.caption.gap from it, at the figure's start edge, in comp.figure.caption.typescale and comp.figure.caption.color, wrapping inside the figure's inline size and never cut or limited to a number of lines; inline content the product gives in the slot (inline code, a keyboard shortcut, a tag) SHALL sit on the caption's baseline and keep its own styles, and a link in it SHALL be underlined at rest (PRS-17). IF both the prop and the slot are given THE slot SHALL be drawn and the prop ignored. THE caption SHALL be below the frame in every look and SHALL have no other place: text above the stage is the title. |
FIG-11 | WHERE the attachment slot holds content THE figure SHALL place it inside the frame, below the stage, across the frame's whole inline size, with no padding of the figure's around it, clipped by the frame's lower corners, and joined to the stage as the separation names (FIG-13); THE figure SHALL add no style to anything inside it: a block there that has a frame of its own (a code block, a table) is the product's to give in the form that suits the join. |
FIG-12 | THE figure SHALL tell the parts inside its frame apart as its separation names: WHILE separation is auto, as comp.figure.separation names, which follows sys.behavior.separation unless a design system sets it apart; WHILE separation is divider or gap, as that value names, whatever the tokens say. WHEN separation, or the token it follows, changes THE figure SHALL draw the other form at once, without animation, keeping the stage's scroll position; WHEN separation names a value the figure does not define THE figure SHALL treat it as auto and in debug builds report the unknown value. A platform that cannot draw a choice SHALL fall back to the first value, divider, and state that in its README. |
FIG-13 | WHILE the separation is divider and variant is not plain THE figure SHALL draw a line comp.figure.divider.thickness thick in comp.figure.divider.color across the frame's whole inline size between the header and the stage and between the stage and the attachment, wherever both parts of a pair are present, and SHALL place the attachment flush against that line and the frame's edges; WHILE the separation is gap and variant is not plain THE figure SHALL draw no line, SHALL leave the header's own padding as the only space between the header and the stage, and SHALL set the attachment in by comp.figure.attachment.gap from the stage above it and from the frame's inline and lower edges, where the attachment keeps the shape it has by itself. |
FIG-14 | WHILE variant is plain THE figure SHALL draw no line whatever the separation is, SHALL draw the header without inline padding so that the title begins at the figure's start edge, comp.figure.caption.gap above the stage, and SHALL place the attachment comp.figure.attachment.gap below the stage across the figure's whole inline size, where it keeps the shape it has by itself. |
FIG-15 | THE figure SHALL NOT take pointer, touch or keyboard input of its own, apart from the scrolling of FIG-5 and FIG-6, and SHALL NOT change anything it draws under a pointer, a press or focus: its frame, header, title and caption have no hovered, focused, pressed, selected or disabled state, it draws no state layer and no ripple, and it emits no event. Input over the stage, the actions and the attachment SHALL reach the content placed there, which stays as operable, as focusable and in the same focus order — the actions, then the stage's content, then the attachment's, then anything in the caption — as it would be outside a figure. |
FIG-16 | THE figure SHALL be exposed to assistive technology as a figure, or as a named group where the platform has no such role, whose name is its title where it has one, its caption where it has a caption and no title, and label otherwise; WHERE it has both a title and a caption THE caption SHALL be exposed as the figure's description and still be read as text in its place; THE figure SHALL NOT draw label. Everything in the header, the stage, the attachment and the caption SHALL stay exposed in that reading order, and THE figure SHALL neither supply nor replace the text alternative of a picture placed in the stage: that is the picture's own. IF title, caption and label are all empty THE figure SHALL be exposed without a name and in debug builds report that a figure has no name. |
FIG-17 | WHEN the title, the caption, the look, inset or the content of a slot changes THE figure SHALL draw and expose the new value at once, without a motion, keeping the stage's scroll position where the content still reaches it, and SHALL NOT announce the change by itself. |
FIG-18 | THE figure SHALL lay itself out as a block in the flow of its container: it SHALL take the whole inline size the container gives it — a page, a column of prose, a cell of the product's grid, a card, a tab's panel alike — with its start edge on the start edge of the text around it; it SHALL bring no outer space of its own (PRS-15: the space between it and its neighbours is its container's), SHALL never be wider than its container, and SHALL have no minimum width, no maximum width and no measure of its own. THE figure SHALL NOT float beside the text, stick to an edge while the content scrolls or raise itself above the page: where it stands, and how several figures are arranged side by side, is the product's layout. |
FIG-19 | WHILE the text size is enlarged THE figure SHALL grow its title and its caption with it, keep its paddings, gaps and lines, wrap, and grow in height; it SHALL cut nothing and set no fixed height, SHALL lose no content when the reader raises line height, paragraph spacing, letter spacing or word spacing, and SHALL need no sideways scrolling of what holds it at a width of 320 points with text at 200 %: the actions move below the title (FIG-7) and content that cannot shrink scrolls inside the stage (FIG-5). |
FIG-20 | WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE figure SHALL draw an edge comp.figure.outline.width wide in the platform's text color inside its frame in the outlined and the filled looks, so that a look whose fill the mode removes is still a bounded stage, and no edge in the plain look, which has none to lose; it SHALL draw the lines of FIG-13 in that text color, its title and caption in that text color, links in the platform's link color and still underlined, and the focus indicator of FIG-6 in the platform's own color for focus, and no ground pattern (FIG-26) in any look, so that nothing but the person's own background stands behind the content; what is shown in the stage is the content's own to adapt. |
FIG-21 | WHERE the layout direction is right-to-left THE figure SHALL mirror the order of its header and apply its paddings by logical start and end, so that the title is at the right and the actions are at the left; SHALL draw the caption from the right edge; SHALL begin the stage's sideways scroll (FIG-5) at the right edge; and SHALL place a plain figure's narrow content (FIG-3) at the right edge. Top and bottom SHALL NOT change. THE figure SHALL NOT mirror, reorder or realign what is shown in the stage or in the attachment: a picture is never mirrored, and the direction of a live example is its own. |
FIG-22 | THE figure SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the figure is shown THE figure SHALL draw itself for the new direction at once, without a motion of its own, keeping what it shows. A direction the product sets on the stage's content alone (a live example drawn right-to-left inside a left-to-right page) SHALL change that content and nothing of the figure's. |
FIG-23 | THE figure SHALL lay out its title and its caption as text of their own direction, each isolated from the other and from the text around the figure: each takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right name, an identifier, a number with its unit, a version and a date keep their own order in a right-to-left layout; THE figure SHALL still place each by the layout direction, not by the text's own, and SHALL wrap them, never cut them. |
FIG-24 | WHEN variant names a value that the active design system does not have THE figure SHALL render with the default variant's tokens, and in debug builds report the unknown value. |
FIG-25 | IF the default slot holds nothing THE figure SHALL draw nothing, take no space and expose nothing, and in debug builds report that a figure shows nothing: a title, a caption or an attachment alone is not a figure. IF label is not empty while a title or a caption is given THE figure SHALL ignore label. |
FIG-26 | WHILE pattern is not none and variant is not plain THE figure SHALL draw a ground pattern in comp.figure.pattern.color across the stage's whole box, over the frame's fill and under the stage's content, centred on the stage and still while the content scrolls (FIG-5): WHILE pattern is grid, lines comp.figure.pattern.line-thickness wide every comp.figure.pattern.spacing across and down; WHILE pattern is dots, dots comp.figure.pattern.dot-size across at the same spacing; WHILE pattern is grid-fade, that grid, clear from the stage's centre to comp.figure.pattern.clear-extent of the way to its edges and growing from there to full strength at its corners; WHILE pattern is checkerboard, squares of side comp.figure.pattern.checkerboard-size, alternately the frame's fill and comp.figure.pattern.color. The pattern SHALL be decoration: it SHALL take no input, be exposed to no assistive technology, be the same in either layout direction, and be covered by the content wherever the content is opaque. WHILE variant is plain THE figure SHALL draw no pattern, because the plain look has no stage to draw one on. IF pattern names a value the figure does not define THE figure SHALL draw none, and in debug builds report the unknown value. |
Platform name mapping
Parity does not mean identical APIs. Each platform uses its own idiom for the same contract.
| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| Component | tanrei-figure | TanreiFigure | TanreiFigure | TanreiFigure | Figure |
title · Property | headline | title | title | title | title |
caption · Property | caption | caption | caption | caption | caption |
label · Property | label | accessibilityLabel | contentDescription | semanticLabel | accessibilityLabel |
variant · Property | variant | variant | variant | variant | variant |
inset · Property | inset | isInset | inset | inset | inset |
separation · Property | separation | separation | separation | separation | separation |
pattern · Property | pattern | pattern | pattern | pattern | pattern |
| Notes | Form: <tanrei-figure> is a custom element with **no shadow root**, wrapped around a native <figure> that stays in the light DOM, so that the figure and its caption are the document's own — read without script, by a crawler, by reader modes and by a screen reader as a figure with its <figcaption> (once the element is defined it names the figure by that caption itself, aria-labelledby, unless the product gave the <figure> an aria-label: one engine does not take the name from the caption): <tanrei-figure><figure><div data-part="frame"><header><p data-part="title">…</p><div data-part="actions">…</div></header><div data-part="stage">…</div><div data-part="attachment">…</div></div><figcaption>…</figcaption></figure></tanrei-figure>; every part but the stage is optional, and a lone <img>, <picture>, <video>, <svg> or <canvas> in place of the stage element is its own stage. Everything drawn comes from one stylesheet, @tanrei/web/figure.css, whose selectors start at tanrei-figure and are wrapped in :where(); it styles the page before the element is defined and without script, so a page of figures does not move when the script arrives, and the element adopts the same sheet into the root it is connected to. Props are attributes of the wrapper: variant, separation, pattern, label; inset is a boolean property that defaults to true and its attribute is the inverse flush, as the card's (an HTML boolean attribute cannot default to true). The title is the headline attribute (HTML's global title would show a tooltip over the whole figure, as on the callout); headline and caption are conveniences for a figure built by script or by the React wrapper, for which the element writes the parts above itself around its children. What the element adds once defined: aria-labelledby on the native figure naming the title where there are both a title and a caption, and aria-describedby naming the caption; aria-label from label; tabindex="0", role="group" and the same name on the stage while it scrolls sideways, removed when it does not (FIG-6); the resolved separation as a data-separation attribute where a container style query cannot be used; and the debug reports. A native <figure> with one <figcaption> and no title needs none of that. The ground pattern (FIG-26) is the stage's own background-image, a set of CSS gradients of the --tanrei-comp-figure-pattern-* tokens over a background-color of the look's fill, so it takes no element, no input and no place in the accessibility tree, stays in place while the stage scrolls, and stands behind a transparent lone picture too; the fading grid is the grid under a radial gradient of the fill. Under forced-colors: active the frame takes a CanvasText border in the outlined and filled looks, and the stage draws no pattern (FIG-20). Pictures take max-inline-size: 100% and block-size: auto (FIG-4). | TanreiFigure(title:caption:variant:isInset:separation:pattern:) { content } actions: { … } attachment: { … }, with titleContent: and captionContent: view builders for the two slots. The stage is a horizontal ScrollView only while its content is wider than it. SwiftUI has no figure trait: the view is an accessibility container (accessibilityElement(children: .contain)) whose label is the title, or the caption, or accessibilityLabel, with the caption as its hint where there is a title. The content's foreground is set with foregroundStyle in the outlined and filled looks. The ground pattern (FIG-26) is to be drawn with a Canvas behind the stage's content; not yet written. | TanreiFigure(title, caption, contentDescription, variant, inset, separation, pattern, actions = { … }, attachment = { … }) { content }, with titleContent and captionContent lambdas for the two slots. No clickable. The stage takes horizontalScroll only while its content is wider than it. A traversal group (semantics { isTraversalGroup = true }) with the name as its content description; Compose has no figure role. The content color is provided through LocalContentColor in the outlined and filled looks. The ground pattern (FIG-26) is to be drawn with Modifier.drawBehind on the stage; not yet written. | TanreiFigure(title:, caption:, semanticLabel:, variant:, inset:, separation:, pattern:, actions:, attachment:, child:), with titleWidget: and captionWidget: for the two slots. One Semantics(container: true, label:) around the parts, which stay in the tree. The stage is a horizontal SingleChildScrollView only while its child is wider than it. The content's default text and icon styles are set in the outlined and filled looks. The ground pattern (FIG-26) is to be a CustomPaint painter behind the stage's child; not yet written. | <Figure title="Variants" caption="…" actions={…} attachment={…}>…</Figure>; title and caption take a string or a node (the two slots). A View that is not itself accessible, so its children stay reachable, with accessibilityLabel on a wrapping group where the platform exposes one. The stage is a horizontal ScrollView only while its content is wider than it. Follows the app's direction where an ancestor's is not known. The content color is given through Tanrei's text context in the outlined and filled looks. pattern takes the same five values; the ground pattern (FIG-26) is to be drawn with an SVG pattern behind the stage's content; not yet written. |
Tokens
| Token | Aliases | Resolved (light) |
|---|---|---|
--tanrei-comp-figure-attachment-gap | sys.space.space100 | 8px |
--tanrei-comp-figure-caption-color | sys.color.on-surface-variant | |
--tanrei-comp-figure-caption-gap | sys.space.space100 | 8px |
--tanrei-comp-figure-container-shape | sys.shape.corner.medium | 8px |
--tanrei-comp-figure-content-color | sys.color.on-surface | |
--tanrei-comp-figure-divider-color | sys.color.outline-variant | |
--tanrei-comp-figure-divider-thickness | sys.border.hairline | 1px |
--tanrei-comp-figure-filled-container-color | sys.color.surface-container | |
--tanrei-comp-figure-header-gap | sys.space.space100 | 8px |
--tanrei-comp-figure-header-padding-block | sys.space.space100 | 8px |
--tanrei-comp-figure-header-padding-inline | sys.space.space200 | 16px |
--tanrei-comp-figure-header-padding-inline-end-with-actions | sys.space.space100 | 8px |
--tanrei-comp-figure-outline-color | sys.color.outline-variant | |
--tanrei-comp-figure-outline-width | sys.border.hairline | 1px |
--tanrei-comp-figure-outlined-container-color | sys.color.surface | |
--tanrei-comp-figure-pattern-checkerboard-size | sys.space.space100 | 8px |
--tanrei-comp-figure-pattern-clear-extent | — | 0.3 |
--tanrei-comp-figure-pattern-color | sys.color.surface-container-highest | |
--tanrei-comp-figure-pattern-dot-size | — | 3px |
--tanrei-comp-figure-pattern-line-thickness | sys.border.hairline | 1px |
--tanrei-comp-figure-pattern-spacing | sys.space.space300 | 24px |
--tanrei-comp-figure-plain-container-color | — | |
--tanrei-comp-figure-separation | sys.behavior.separation | 0 |
--tanrei-comp-figure-stage-padding-block | sys.space.space400 | 32px |
--tanrei-comp-figure-stage-padding-inline | sys.space.space300 | 24px |
--tanrei-comp-figure-title-color | sys.color.on-surface | |
--tanrei-comp-figure-caption-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-figure-title-typescale | sys.typescale.heading-6 | 500 14px/1.4286 |
Fixtures
Canonical renderings. Every platform snapshots each fixture under the same id so the results can be compared.
variant: outlined (27)
outlined
label: A specimen caption
caption: The medium corner, on a card. title
title: Variants title-caption
title: Revenue by quarter caption: Every region grew; the north grew fastes… title-slot
slots: title caption-slot
slots: caption actions
title: Variants slots: actions actions-only
label: A specimen slots: actions flush
caption: The harbour at low tide. inset: false title-flush
title: Harbour caption: The harbour at low tide. inset: false attachment
title: Variants slots: attachment separation-divider
title: Variants caption: A filled button and its markup. separation: divider slots: actions, attachment separation-gap
title: Variants caption: A filled button and its markup. separation: gap slots: actions, attachment long-caption
caption: Measured on the default system at standa… long-title-actions
title: Every variant of the button at each of i… slots: actions actions-rtl
title: الأشكال caption: زرّ ممتلئ وزرّ محدَّد. direction: rtl slots: actions ltr-title-rtl
title: tanrei-button v0.1 caption: comp.button.container.height.medium: 40p… direction: rtl attachment-rtl
title: الأشكال separation: divider direction: rtl slots: attachment pattern-grid
label: A specimen pattern: grid pattern-dots
label: A specimen pattern: dots pattern-grid-fade
label: A specimen pattern: grid-fade pattern-checkerboard
label: A specimen pattern: checkerboard title-pattern-grid-fade
title: Variants caption: A filled button and its markup. pattern: grid-fade slots: actions, attachment pattern-dots-rtl
title: الأشكال pattern: dots direction: rtl docs-demo
title: Variants, sizes and disabled pattern: grid-fade slots: actions docs-demo-code
title: Variants separation: divider pattern: grid-fade slots: actions, attachment docs-demo-system
title: Single choice, with glyphs, glyphs alone caption: Drawn under m3-like, a system that ships… pattern: grid-fade variant: filled (8)
filled
label: A specimen variant: filled filled-caption
caption: The medium corner, on a card. variant: filled filled-title-caption
title: Revenue by quarter caption: Every region grew; the north grew fastes… variant: filled filled-flush
caption: The harbour at low tide. variant: filled inset: false filled-attachment
caption: A filled button and its markup. variant: filled slots: attachment filled-separation-gap
title: Variants variant: filled separation: gap slots: attachment filled-pattern-grid
label: A specimen variant: filled pattern: grid filled-pattern-dots
label: A specimen variant: filled pattern: dots variant: plain (7)
plain
label: A specimen variant: plain plain-caption
caption: The medium corner, on a card. variant: plain plain-title-attachment
title: Variants caption: A filled button and its markup. variant: plain slots: attachment plain-caption-rtl
caption: الميناء عند الجزر. variant: plain direction: rtl plain-pattern-grid
caption: The plain look draws no pattern. variant: plain pattern: grid docs-specimen
caption: medium · 8px variant: plain docs-picture
caption: The review gallery, with one component's… variant: plain Generated from specs/figure.spec.json, tokens/comp/figure.tokens.json, design/tokens/
variant |
Draws | Use for |
|---|---|---|
outlined (default) |
The surface inside one hairline. | A specimen, a live example, a diagram. |
filled |
A quiet tone and no edge. | A page that already has many lines; a ground for things with shadows. |
plain |
No fill, no edge, no padding, no line. | A picture that is its own frame; a specimen in a grid. |
The outlined look is filled with the surface color, not left empty. Your own components keep the contrast their tokens were held to, whatever panel the figure is placed in.
title(theheadlineattribute on the web) is a short line in a header inside the frame. It names what is shown.captionis below the frame, at the start edge, in secondary text. It wraps and is never cut.- There is no placement setting. Text above the stage is the title. A caption has one place.
- The title is plain text, not a heading. Where your page’s outline needs a heading there, write one with
data-part="title": the figure draws it in the title’s style and keeps its role and level.
- The actions are your own controls at the end of the header: which variant, which theme, open it elsewhere. The figure lays them out in a row and does nothing else. They move below the title when the row is too narrow.
- The attachment is your own block under the stage, inside the frame: the code an example was made from, the data of a chart.
separation |
Draws |
|---|---|
auto (default) |
What the design system chose: lines in the default system. |
divider |
A line under the header and above the attachment, which sits flush with the frame. |
gap |
No line. The attachment stands inside the frame, set in by 8, with its own shape. |
inset(default true) pads the stage by 32 above and below and 24 at the sides.flushon the web removes the padding, so a picture, a chart or a map reaches the frame’s edges and is clipped by its corners.- A picture scales down with its proportions kept, and never up.
- Content that cannot shrink scrolls sideways inside the stage. The figure is never wider than its container.
- Content narrower than a framed stage is centred. In the plain look it starts at the text’s edge.
- The stage is as tall as its content. How several examples are arranged in it is your layout.
pattern |
Draws | Use for |
|---|---|---|
none (default) |
The look’s fill alone. | Most figures; a picture that fills the stage. |
grid |
A hairline every 24, across and down. | A component or a layout shown against a grid. |
dots |
A dot at every crossing of that grid. | A drawing or a diagram, on a quieter ground. |
grid-fade |
The grid, clear around the centre and growing toward the corners. | A specimen at the centre of a large stage. The live examples on this site use it. |
checkerboard |
Squares of 8, the fill and the pattern’s color in turn. | A picture or an icon that may be transparent. |
- The pattern is decoration. It is drawn behind the content, in one quiet color of the surface ladder, and it does not get louder when contrast is raised.
- It stays in place while the content scrolls sideways, and it is the same in either direction.
- The plain look draws none, because it has no stage to draw one on. Forced colors remove it.
- Don’t draw a pattern of your own around the stage. Choose one here, or restyle these through the
comp.figure.patterntokens.
- A figure fills the width it is given and brings no outer space. In prose it is spaced as a block and runs the prose’s full width, while its caption is kept to the measure like a text block.
- Several figures side by side are a grid of yours. A figure fills its cell.
- Write a caption that says what the figure is for: “Revenue grew in every region”, not “Chart”.
- Give a picture its own text alternative. The caption does not replace it.
- Mark an example to follow or to avoid with a small tag at the start of the caption, with its word: “Do”, “Don’t”.
- Don’t use a figure to group related content and actions. That is a card.
- Don’t make a figure pressable. A picture that opens something is a link around the picture.
- Don’t color the frame to say that an example is right or wrong. The figure has no status.
- Role
figure- Labeling
- The figure is named by its title, or by its caption where it has no title, or by label where it draws neither. With both a title and a caption, the title is the name and the caption the description. A picture in the stage has its own text alternative, given by the product; the caption does not replace it. While the stage scrolls, it is a group with the figure's name, so that a person who reaches it by keyboard hears what scrolls.
- Minimum target
- The hit area meets the minimum target size even when the visual size is smaller. minimum 32px · pointer 44px · spacing 8px · touch 48px
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves through the product's controls in the header, then to the stage where it scrolls, then through what the stage, the attachment and the caption hold. The figure adds no stop of its own unless its stage scrolls. |
| ArrowLeft / ArrowRight / Home / End | While the scrolling stage has focus: scroll it sideways, as the platform scrolls a region. |
Notes
- Not interactive: the figure itself takes no press, so it has no target to size and minimumTarget holds trivially. The controls in its actions slot and anything operable in its stage are other components, each with its own target.
- A caption is not a text alternative. The caption says what the figure is for a reader who sees it ("Revenue grew in every region"); the picture's own alternative says what the picture shows to a reader who does not. Where the caption already says everything the picture does, the product marks the picture as decorative rather than repeat the caption.
- A live example in the stage stays fully operable and is read as what it is: the figure hides nothing from assistive technology and does not turn its content into an image. A purely decorative specimen (a swatch, a shape) is the product's to hide and to describe in the caption.
- A figure with neither a title nor a caption needs label; the component reports a figure without any name in debug builds (FIG-16) and cannot tell whether a name is a good one.
- A status such as do and don't is never given by a color on the frame: the figure has no status. A product that marks a figure as an example to follow or to avoid puts a tag with its glyph and its word at the start of the caption, where it is read as part of the figure's name or description.
- The scrolling stage is a focus stop (FIG-6) so that a keyboard user can bring every part of wide content into view (WCAG 2.1.1); it is named, and it shows the system's focus indicator, which is held at 3:1 against every surface role, the two fills of this component among them.
- Contrast in the default system: the title is on-surface on surface or surface-container and the caption is on-surface-variant on the surface the figure sits on, both role pairs held at 4.5:1 by the token invariants. The outlined look's edge and the dividers are decorative: the stage is told apart by its position and its caption, not by the line.
- The ground pattern (FIG-26) is decoration (WCAG 1.1.1): it is not exposed and carries nothing the content and the caption do not. It is drawn in surface-container-highest, a surface role against which on-surface text and the focus indicator are held by the token invariants, so text standing on a line or a dot keeps its contrast; and a raised contrast level does not make it louder. Forced colors remove it (FIG-20).
- Forced colors (FIG-20): the filled look loses its fill and gains an edge in the text color. The plain look stays unframed. No look draws a ground pattern.
Generated from specs/figure.spec.json, tokens/sys/measure.tokens.json
- The native figure stays a figure. It is named by its title, or by its caption where it has no title. With both, the title is the name and the caption the description, and the caption is still read in its place.
labelnames a figure that draws neither. It is never drawn, and it is ignored where a title or a caption is given.- While the stage scrolls sideways it is one stop in the focus order, before anything inside it, and a group with the figure’s name. The arrow keys scroll it, and Home and End take it to either end. A stage that does not scroll adds no stop.
- The focus ring is drawn inside the stage’s edge, so nothing clips it.
- Everything in the header, the stage, the attachment and the caption stays operable and keeps its focus order.
- At 200% text the title and the caption grow and wrap, and the actions move below the title.
- In a right-to-left layout the header is mirrored and the caption starts at the right. What is shown in the stage is never mirrored.
- In forced colors the outlined and filled looks have an edge in the text color. The plain look has none, and no look draws a pattern.
- A figure with no name, a figure whose stage is empty, and an unknown variant, separation or pattern are reported in debug builds.
- Give every figure a title, a caption or a
label. - Give a picture in the stage its own
alt. Where the caption already says everything the picture does, mark the picture as decorative (alt=""). - Keep a live example operable. The figure does not hide its content or turn it into an image.