How much a change touched: the lines added and removed, such as "+128 −12", and a row of five small squares that shows the balance.
Options
The diff stat has no choices: it is drawn one way.
Examples
Usage
The lines added and removed, and five squares for the balance.
Balance
Mostly added, mixed, and mostly removed: the squares follow the counts.
One side only
Only added, only removed, and nothing changed, which leaves every square empty.
Beside a change
After a change's title in a list; thousands get a comma.
Guidelines
Purpose
Shows how much a change touched: the lines it added and the lines it removed, such as "+128 −12", then a row of five small squares that shows the balance at a glance, green for added and red for removed. It sits beside a change's title in a list of changes, a review or an agent's report of what it did.
When to use
- Beside a change, a commit or a file in a list of changes, where a reader wants to see its size before opening it.
- In a summary of what an agent or a person changed.
When not to use
Usage rules
Do
- Keep it beside the change it describes.
- Give it the real counts; the squares follow from them.
Don't
- Use it for anything but lines added and removed.
- Draw the squares without the counts.
Keep it beside the change it describes, so a reader sees its size before opening it.
Use it for other counts that go up and down; a stat with its trend says those.
Anatomy
- The block: the two counts and the squares, side by side on one line.
- The added part, the lines added with a plus sign, in the success text color.
- The removed part, the lines removed with a minus sign, in the danger text color.
- The blocks part, five small squares; each block part is green for added, red for removed or the empty track color.
- The hidden part, the sentence a screen reader hears.
Both counts use the small code style with figures of one width, so counts line up down a list.
Behavior
The squares split five between the two counts by their share of the lines, rounded. A side with any lines at all keeps at least one square, so a small removal still shows; a change with no lines leaves all five empty. Counts are written with a comma between thousands.
Accessibility
What it does for you
- A screen reader hears one sentence, such as "128 lines added, 12 lines removed". The signs, the counts as drawn and the squares are hidden from it, so it does not read "plus 128 minus 12".
- The signs and the numbers carry the meaning; the colors and the squares only repeat it.
- In high-contrast mode every square is outlined and the ones with lines are filled with the text color.
What you must do
- Give
labelyour own sentence when the page is in another language or counts something other than lines.
How to test
- With a screen reader, it is read as one sentence with both counts.
- With the colors removed, the signs and the numbers still say what was added and removed.
- In a forced-color mode, every square is outlined and the ones with lines are filled.
- With text at 200% and the page 320 pixels wide, the counts and squares stay whole.
Source
The files the diff stat is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-diff-stat — Diff stat. Figma "Diff stat" 7580:1825 (Balance: Adds, Mixed, Removes).
* How much a change touched: the lines added in the success text color and the lines removed in the danger
* text color, each with its sign, in the small code style, then five small squares that show the balance.
* The signs and the numbers carry the meaning, so the colors are never the only sign; a hidden sentence says
* it all to a screen reader. */
.smbk-diff-stat {
display: inline-flex;
align-items: center;
gap: var(--smbk-space-6);
vertical-align: middle;
font-family: var(--smbk-typography-code-sm-font-family);
font-size: var(--smbk-typography-code-sm-font-size);
font-weight: var(--smbk-typography-code-sm-font-weight);
line-height: var(--smbk-typography-code-sm-line-height);
letter-spacing: var(--smbk-typography-code-sm-letter-spacing);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* The block frames the words it hides for screen readers, so they stay inside it wherever it is placed: without a
frame they hang from the page itself and stretch it, past the end of an area that scrolls. No weight, so a parent
that places the block keeps its say. */
:where(.smbk-diff-stat) {
position: relative;
}
.smbk-diff-stat__added {
color: var(--smbk-color-feedback-success-text-on-subtle);
}
.smbk-diff-stat__removed {
color: var(--smbk-color-feedback-danger-text-on-subtle);
}
.smbk-diff-stat__blocks {
display: inline-flex;
flex-shrink: 0;
gap: var(--smbk-space-2);
}
.smbk-diff-stat__block {
display: block;
width: var(--smbk-size-8);
height: var(--smbk-size-8);
border-radius: var(--smbk-radius-2);
background: var(--smbk-color-surface-track-subtle);
}
.smbk-diff-stat__block--added {
background: var(--smbk-color-feedback-success-icon);
}
.smbk-diff-stat__block--removed {
background: var(--smbk-color-feedback-danger-icon);
}
/* The sentence a screen reader hears; the signs and the squares are hidden from it. */
/* .smbk-diff-stat__hidden: hidden from view, kept for screen readers and the keyboard, by the shared rule in visually-hidden/visually-hidden.css. */
/* ---- high contrast (forced colors) ---- */
/* The browser drops the fills: every square is outlined in the text color and the ones with lines are filled
with it, so the balance still shows; the numbers keep their signs. */
@media (forced-colors: active) {
.smbk-diff-stat__block {
outline: var(--smbk-border-width-hairline) solid CanvasText;
outline-offset: calc(-1 * var(--smbk-border-width-hairline));
}
.smbk-diff-stat__block--added,
.smbk-diff-stat__block--removed {
background: CanvasText;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| space.6--smbk-space-6 | 6px | Spacing | |
| typography.code-sm--smbk-typography-code-sm-font-family--smbk-typography-code-sm-font-size--smbk-typography-code-sm-font-weight--smbk-typography-code-sm-line-height--smbk-typography-code-sm-letter-spacing--smbk-typography-code-sm-text-transform | fontFamily: Geist Mono, Geist Mono Fallback, ui-monospace, monospacefontSize: 12pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| color.feedback.success.text-on-subtle--smbk-color-feedback-success-text-on-subtle | light: #016317dark: #8ddf90changes with theme | Color | |
| color.feedback.danger.text-on-subtle--smbk-color-feedback-danger-text-on-subtle | light: #a51d17dark: #febdb2changes with theme | Color | |
| space.2--smbk-space-2 | 2px | Spacing | |
| size.8--smbk-size-8 | 8px | Size | |
| radius.2--smbk-radius-2 | 2px | Radius | |
| color.surface.track-subtle--smbk-color-surface-track-subtle | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| color.feedback.success.icon--smbk-color-feedback-success-icon | light: #037a1fdark: #54b85bchanges with theme | Color | |
| color.feedback.danger.icon--smbk-color-feedback-danger-icon | light: #cc0d0ddark: #fe7d6dchanges with theme | Color | |
| border-width.hairline--smbk-border-width-hairline | 1px | Border width |
Manifest
{
"name": "diff-stat",
"block": "smbk-diff-stat",
"summary": "How much a change touched: the lines added and removed, such as \"+128 −12\", and a row of five small squares that shows the balance.",
"adapt": "css",
"axes": {},
"flags": {},
"parts": [
"hidden",
"added",
"removed",
"blocks",
"block"
],
"figmaNodeId": "7580:1825"
}