Skip to content

Diff card

One story × viewport of a dta diff report: only “after”, with each changed element outlined and the tokens that explain it named under it, a table of those tokens, and links to the whole images. “Show before” swaps the image in place, so a subtle step (Nova 500 → 600) shows by flicking. The dta diff report writes the same row; this component is its reference.

Explained: after, outlined and named

Light

orbit-card--default 1280 px · 0.83% changed

changedexplained

With this changefeature/rounder-cards · 9f3c1a2Before this changemain · 2ee9b31

Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24
Explained by 2 tokens
Explained byBeforemain · 2ee9b31With this changefeature/rounder-cards · 9f3c1a2Outlined
Colors.Brand.Primary{Nova.500} #5aa2ff{Nova.600} #3d87e8span.button
Radius.Card{Orbit.radius-sm} 8{Orbit.radius-lg} 24div.card, span.button

Whole images: beforeafter

Graphite

orbit-card--default 1280 px · 0.83% changed

changedexplained

With this changefeature/rounder-cards · 9f3c1a2Before this changemain · 2ee9b31

Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24
Explained by 2 tokens
Explained byBeforemain · 2ee9b31With this changefeature/rounder-cards · 9f3c1a2Outlined
Colors.Brand.Primary{Nova.500} #5aa2ff{Nova.600} #3d87e8span.button
Radius.Card{Orbit.radius-sm} 8{Orbit.radius-lg} 24div.card, span.button

Whole images: beforeafter

Story: /stories/diff-card/explained/

"Show before" pressed

Light

orbit-card--default 1280 px · 0.83% changed

changedexplained

With this changefeature/rounder-cards · 9f3c1a2Before this changemain · 2ee9b31

Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24
Explained by 2 tokens
Explained byBeforemain · 2ee9b31With this changefeature/rounder-cards · 9f3c1a2Outlined
Colors.Brand.Primary{Nova.500} #5aa2ff{Nova.600} #3d87e8span.button
Radius.Card{Orbit.radius-sm} 8{Orbit.radius-lg} 24div.card, span.button

Whole images: beforeafter

Graphite

orbit-card--default 1280 px · 0.83% changed

changedexplained

With this changefeature/rounder-cards · 9f3c1a2Before this changemain · 2ee9b31

Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24
Explained by 2 tokens
Explained byBeforemain · 2ee9b31With this changefeature/rounder-cards · 9f3c1a2Outlined
Colors.Brand.Primary{Nova.500} #5aa2ff{Nova.600} #3d87e8span.button
Radius.Card{Orbit.radius-sm} 8{Orbit.radius-lg} 24div.card, span.button

Whole images: beforeafter

Story: /stories/diff-card/before/

Unexplained: diff overlay on

Light

orbit-card--default 1280 px · 0.64% changed

changedunexplained

With this changefeature/bigger-title · 4d81e07 + uncommitted changesBefore this changemain · 2ee9b31

unexplained · no token

No changed token accounts for these pixels: a component, content or layout change. The dashed box marks where; the overlay shows which pixels.

Whole images: beforeafterdiff

Graphite

orbit-card--default 1280 px · 0.64% changed

changedunexplained

With this changefeature/bigger-title · 4d81e07 + uncommitted changesBefore this changemain · 2ee9b31

unexplained · no token

No changed token accounts for these pixels: a component, content or layout change. The dashed box marks where; the overlay shows which pixels.

Whole images: beforeafterdiff

Story: /stories/diff-card/unexplained/

Notes listed where they would cover the story; same tokens share an entry

Light

orbit-menu--default 1280 px · 0.71% changed

changedexplained

With this changefeature/rounder-cards · 9f3c1a2Before this changemain · 2ee9b31

Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24
Explained by 2 tokens
Explained byBeforemain · 2ee9b31With this changefeature/rounder-cards · 9f3c1a2Outlined
Colors.Brand.Primary{Nova.500} #5aa2ff{Nova.600} #3d87e8span.button
Radius.Card{Orbit.radius-sm} 8{Orbit.radius-lg} 24div.card, span.button

Whole images: beforeafter

Graphite

orbit-menu--default 1280 px · 0.71% changed

changedexplained

With this changefeature/rounder-cards · 9f3c1a2Before this changemain · 2ee9b31

Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24Radius.Card · 8 → 24Colors.Brand.Primary · Nova 500 → 600Radius.Card · 8 → 24
Explained by 2 tokens
Explained byBeforemain · 2ee9b31With this changefeature/rounder-cards · 9f3c1a2Outlined
Colors.Brand.Primary{Nova.500} #5aa2ff{Nova.600} #3d87e8span.button
Radius.Card{Orbit.radius-sm} 8{Orbit.radius-lg} 24div.card, span.button

Whole images: beforeafter

Story: /stories/diff-card/listed/

  • Only “after”, the change marked. Every changed box gets an outline in signal-bg (stroke-ink) with a 1 px ink-900 line inside and outside it. The tokens that change a box sit in notes under it, stacked when several change the same box, joined to it by a short leader; notes never cover another outlined box. A box changed by two tokens (a button’s colour and radius) carries both notes.
  • Nothing covers the story: notes, markers. A note sits on the image only where everything under it is one flat colour of the screenshot (or below the image, on the stage’s paper). Otherwise its box gets a numbered marker (1, 2, … in reading order: signal with the 1 px ink line, like the outline) and its notes are listed under the image, in the notes’ own style. The marker follows the same rule: it sits next to the outline’s outer edge, outside the box, at the first flat spot of: above (left corner, middle, right corner), right, below, left, the four diagonal corners; then farther out, 4 px at a time up to 48 px. It never crosses another outline, a note or another marker (a marker inside a larger outlined box, next to a nested element, is fine). Only if no flat spot exists within 48 px does it fall back to the first edge spot. The decision is made at the reader’s width, so the same row can keep its notes on the image at 1280 and list them at 390 (story diff-card--listed). Leaders are not checked (a 2 px line may cross an edge). “Show before” hides markers and the list with the outlines, keeping their space.
  • One list entry per set of token changes. Boxes explained by the same set of token changes share an entry: “1, 3” and their notes once (light and dark side by side render the same component twice). Boxes with different sets keep their own entries; markers keep their own numbers on the image.
  • What counts as flat: the report reads the “after” PNG and splits its cropped canvas into 4 × 4 px cells; a cell is flat when all its pixels are opaque and within 6 per channel of its first pixel, and flat cells of nearly the same colour count as one surface. A note may cover only cells of one surface. The map travels with the row (data-flat); without it (a report rendered without its images) every box is marked and listed. In this component the surfaces prop gives the same map from boxes in canvas pixels (each entry one surface); without it every note is listed.
  • Note wording: the token, then its change. Colours show the alias step (Nova 500 → 600, both names when the palette changes: Navy 700 → Sky 500), sizes and other values the resolved numbers (8 → 24).
  • The ink line is not decoration. Signal alone is 2.98:1 on a light story page (paper-100) and 2.14:1 in Graphite’s signal-bg, under the 3:1 a boundary needs; the ink line holds there (14.15:1) and signal holds on dark surfaces such as a navy card (4.91:1 and up). Screenshots do not follow the theme, so the ink is the fixed ink-900, not fg-default, and the stage behind the image is the story page’s paper-100 in both themes.
  • Notes are redline labels: signal-fg on signal-bg (5.74:1), the design system’s one AA exception, marked data-contrast-exception. The “unexplained · no token” note is ink-900 on paper-0 with a dashed ink border (15.95:1).
  • “Show before” is a toggle button: a box, a tick when pressed and the word on or off, aria-pressed, operable with Enter and Space (no keyboard shortcut). It swaps the image in the same place; outlines, leaders, notes and the overlay hide but keep their space, and the heading switches from “With this change” to “Before this change”. Both headings, each with its small meta, share one grid cell, so the larger sets the box and switching moves nothing: no layout shift at any width.
  • Sides in words, not git words: “With this change” with the head in small meta (head: branch and short sha, “feature/x · 9f3c1a2”, “+ uncommitted changes” when the tree was dirty, “working tree” when the result does not say), and “Before this change” with the base ref and short sha (base: “main · 2ee9b31”; only the sha when the base was given as a sha, a directory base by its name). The token table’s columns read “Before” and “With this change”, each with the same ref string under it in fg-subtle meta. Never “head” or “base” in what a reader sees or hears: headings, columns, phone labels, alt texts.
  • Unexplained rows (no token accounts for some pixels): the diff overlay is on by default (a toggle of the same kind, beside “Show before”; the diff image blends with multiply, so changed pixels show in signal and the rest stays clear), the box is dashed, and a sentence says what that means. Explained rows have no overlay toggle: the outline says where, the diff image is one link away.
  • Status chips: “changed” in the info tone like the report’s story status, then “explained” (neutral) or “unexplained” (error, the square).
  • Phone width: the toggles take the full width, one under the other; notes take two lines (token, then change); the token table becomes a list labelled “Before”, “With this change” and “Outlined”, the labels in one column as wide as the widest (a subgrid per row), the refs left to the heading above. In the report the image runs edge to edge.
  • Without JavaScript the image, outlines and links stay, the notes line up under the image and the toggles are left out.

Read from the component’s source:

--action-bg--action-text--bg-subtle--bg-surface--border-default--border-strong--fg-default--fg-muted--fg-subtle--font-mono--font-sans--ink-900--paper-0--paper-100--radius-full--radius-lg--radius-md--signal-bg--signal-fg--space-1--space-2--space-3--space-4--space-6--stroke-focus--stroke-hairline--stroke-ink--type-body-size--type-body-sm-size--type-label-weight--type-meta-line-height--type-meta-size--type-title-size--type-title-weight