← All proposals

Content Diff

A component, ContentDiff: the old value on a red line with a minus, the new value on a green line with a plus, in one bordered block, like a text diff.

Net-new componentOpen in Storybook

proposals/ContentDiff/HANDOFF.md69 lines · View on GitHub

Handoff: Content Diff

What and why

A before and after of a piece of content, stacked like a text diff: the old value on a red line with a minus, the new value on a green line with a plus, in one bordered block. For text an agent proposes to change (page copy); code diffs use the Code diff recipe. PDS has nothing like it. It started as the body of the Suggestion Card's SuggestionChange and was taken out as its own component on 2026-10-21; SuggestionChange now composes it.

  • Figma / Jira: Figma page under Preview; Jira none. Source: the designer's screenshot of the stacked diff in the Suggestion Card.
  • Context: context.md

Exploration and decisions

  • Stacked, not columns. It reads top to bottom, gives long text the full width and matches the diff look used elsewhere.
  • A definition list. dl with a visually hidden dt ("Before", "After") and a dd per value; beforeLabel and afterLabel are required strings so they can be translated. The minus and plus icons are aria-hidden.
  • Color is not the only cue: the minus and plus carry the meaning too.
  • Colors are the status tokens: critical for before, success for after (medium weight).

Full list: decisions.md.

API

Prop Type Default Purpose
after ReactNode required The new value.
afterLabel string required Name of the new value for screen readers (hidden). Translated.
before ReactNode required The current value.
beforeLabel string required Name of the current value for screen readers (hidden).
className string none Additional class names.
size 'xs' | 's' 's' Text size, as a pds-copy-* class. The icons stay the same size.

It extends the props of a dl. Variants and states: size s (default) and xs, switched with the size control. Story: Default.

Tokens used

  • --pds-border-radius-default, --pds-border-width-default
  • --pds-color-border-default
  • --pds-color-status-critical-background, --pds-color-status-critical-foreground
  • --pds-color-status-success-background, --pds-color-status-success-foreground
  • --pds-spacing-2xs, --pds-spacing-5xs, --pds-spacing-xs
  • --pds-typography-fw-medium

Impact

From impact.md: no existing PDS component is reached. SuggestionCard builds on it (SuggestionChange); it uses the default size s, which is what it rendered before the size prop, so it is unchanged (look at it in Storybook to confirm).

Reused vs net-new

  • Reused: the local Icon wrapper (minus, plus, the PDS icons, size s), pds-copy-s and pds-copy-xs, the visually-hidden class.
  • Net-new: .pds-content-diff with __line, --before, --after, __marker and __text; all props. See custom.md (no custom variables, no raw values).

Accessibility

  • A dl with hidden dt names, so a screen reader hears "Before: ..." and "After: ...". Give the block a name from the screen around it (for example the field), as SuggestionChange does.
  • Contrast: the status foreground on the status background. a11y panel on Default at both sizes, light and dark (2026-10-22): no violations, no inconclusive checks. Dark mode was not checked by eye.
  • The size prop only changes the text size; semantics and the icons are the same.

Decisions and open questions

  • Size added 2026-10-22 (after handoff): size s or xs, text only; see decisions.md.
  • Highlight the changed words inside each line?
  • Should it take a visible title (the field name) itself?

Port notes

  • New component (npm run generate:component). Swap the local Icon wrapper and mergeClasses for the real ones. Port it before SuggestionCard.

Preview

npm run storybook, then Component Proposals/Content Diff. Figma: https://www.figma.com/design/AMGWJbraJoi2PKWnxo29Ah/PDS-Proposals?node-id=489-43