Menu


Contrast pair


Two colors drawn as text on its background, with their contrast ratio and the mark it earns: AAA, AA, AA large or fails.

Customize the preview


Choices
View
Contrast 14.08 to 1,AAA

Text default on surface default

Options


Contrast pair options
ChoiceValuesDefaultWhat it does
resultsmbk-contrast-pair--result-<value>aaaaaaa-largefailsaaa
The mark the ratio earns: AAA at 7:1 and up, AA at 4.5:1, AA large at 3:1 (large text and controls only), else fails. Worked out from the two colors unless given; it sets the badge’s words and type.

Partssmbk-contrast-pair__samplesmbk-contrast-pair__textsmbk-contrast-pair__resultsmbk-contrast-pair__ratiosmbk-contrast-pair__badgesmbk-contrast-pair__namessmbk-contrast-pair__hidden

Examples


Usage

One pair, drawn as it is, with its ratio and the mark it earns, worked out from the two colors.

Contrast 5.03 to 1,AA

Text subtlest on surface default

A contrast grid

Pairs side by side, one for each mark: AAA, AA, AA large and fails.

Contrast 14.08 to 1,AAA

Text default on surface default

Contrast 5.03 to 1,AA

Text subtlest on surface default

Contrast 3.38 to 1,AA large

Border control on surface default

Contrast 1.47 to 1,Fails

Border subtle on surface default

A dark pair

The pair keeps its own colors whatever the page’s theme.

Contrast 6.98 to 1,AA

Text subtle on surface default, dark

Guidelines


Purpose

Shows one text color on one background as they really look, with their contrast ratio and the mark it earns: AAA, AA, AA large or fails. It is for tools and pages that check a color system against the contrast rules. To show a range of colors with their contrast against white, use the color range.

When to use

  • To show whether a text color can be read on a background, in a theme builder, a token editor or a color system's documentation.
  • Side by side in a grid, one per pair a system uses.

When not to use

  • To show a whole range of colors. Use the color range.
  • For a color that is not text or a control's edge: the marks are the rules for text and for what identifies a control.

Usage rules

Do

  • Name the two roles, so the reader knows which pair the mark belongs to.
  • Let the pair work out its ratio and mark from its colors.

Don't

  • Give a mark the two colors do not earn.
Contrast 14.08 to 1,AAA

Text default on surface default

Do

Name the two roles, so the reader knows which pair the mark belongs to.

Contrast 1.47 to 1,AA

Border subtle on surface default

Don't

Give a result the two colors do not earn: let the pair work it out.

Anatomy

  • The block: a row on the default surface inside a subtle edge.
  • The sample part: a 48 px square in the background color with "Aa" in the text color, drawn with the two colors given.
  • The text part, a column: the result part (the ratio part in Label MD, then a small soft badge, the badge part, that says the mark in words) over the names part, the two roles in Body XS, such as "Text subtle on surface default".
  • The result axis: AAA at 7:1 and up and AA at 4.5:1 take the success badge, AA large at 3:1 the warning badge, and fails the danger badge.

Behavior

The ratio is worked out from the two colors as WCAG 2.2 counts it, a see-through color laid over what is under it, and written with two decimals, never rounded up past what it is. The mark follows from the ratio. A page that has measured the pair itself can give the ratio, or the mark, and the pair shows them as given.

The sample keeps its two colors whatever the page's theme: a pair is drawn as it is.

Accessibility


What it does for you

  • The sample is hidden from screen readers. They read the ratio in words ("Contrast 5.03 to 1"), the mark and the two roles.
  • The mark is a word in the badge, not a color alone.
  • In forced colors mode the sample keeps its two colors, since they are what it shows, and the edges take the reader's text color.

What you must do

  • Name the two roles, so a reader who cannot see the sample still knows which pair the mark belongs to.
  • Give every word it says by itself, such as the badge's words and the ratio read aloud, in the page's language; each keeps its English unless the page gives its own.

How to test

  • With a screen reader, the pair is read as the ratio, the mark and the two roles, and the sample is not read.
  • The mark shows as a word in the badge, so it reads without color.
  • In a forced-color mode, the sample still shows its two colors.
  • With text at 200% and the page 320 pixels wide, the pair fills the width it is given and its words stay whole.

Source


The files the contrast pair is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.

Stylesheet
contrast-pair.css
/* smbk-contrast-pair — Contrast pair. Figma "Contrast pair" 7416:115 (Result: AAA, AA, AA large, Fails).
 * One color pair drawn as it is, on the default surface inside a subtle edge: a 48 px sample (the background color with
 * "Aa" in Heading XS in the text color), then the ratio in Label MD with a small soft badge (smbk-badge) that says the
 * result in words, and under them the two roles' names in Body XS. The sample's two colors are data: the wrapper writes
 * them to two custom properties on the block, and only the sample reads them. The badge's type follows the result. */

.smbk-contrast-pair {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--smbk-space-inline-lg);
  min-width: 0;
  padding: var(--smbk-space-inset-md);
  border: var(--smbk-border-width-hairline) solid var(--smbk-color-border-subtle);
  border-radius: var(--smbk-radius-container-sm);
  background: var(--smbk-color-surface-default);
}

/* 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-contrast-pair) {
  position: relative;
}

.smbk-contrast-pair__sample {
  position: relative;
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--smbk-size-48);
  height: var(--smbk-size-48);
  overflow: hidden;
  border-radius: var(--smbk-radius-control-md);
  background: var(--smbk-contrast-pair-background);
  color: var(--smbk-contrast-pair-foreground);
  font-family: var(--smbk-typography-heading-xs-font-family);
  font-size: var(--smbk-typography-heading-xs-font-size);
  font-weight: var(--smbk-typography-heading-xs-font-weight);
  line-height: var(--smbk-typography-heading-xs-line-height);
  letter-spacing: var(--smbk-typography-heading-xs-letter-spacing);
  forced-color-adjust: none;
}

/* The sample's edge, drawn inside and over the color, as the color swatch draws its own. */
.smbk-contrast-pair__sample::after {
  content: '';
  position: absolute;
  inset: 0;
  border: var(--smbk-border-width-hairline) solid var(--smbk-color-border-edge-shade-subtle);
  border-radius: inherit;
  pointer-events: none;
}

.smbk-contrast-pair__text {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--smbk-space-stack-xs);
  min-width: 0;
}

.smbk-contrast-pair__result {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--smbk-space-inline-sm);
}

.smbk-contrast-pair__ratio {
  color: var(--smbk-color-text-default);
  font-family: var(--smbk-typography-label-md-font-family);
  font-size: var(--smbk-typography-label-md-font-size);
  font-weight: var(--smbk-typography-label-md-font-weight);
  line-height: var(--smbk-typography-label-md-line-height);
  letter-spacing: var(--smbk-typography-label-md-letter-spacing);
  font-variant-numeric: tabular-nums;
}

.smbk-contrast-pair__names {
  margin: 0;
  color: var(--smbk-color-text-subtle);
  font-family: var(--smbk-typography-body-xs-font-family);
  font-size: var(--smbk-typography-body-xs-font-size);
  font-weight: var(--smbk-typography-body-xs-font-weight);
  line-height: var(--smbk-typography-body-xs-line-height);
  letter-spacing: var(--smbk-typography-body-xs-letter-spacing);
  overflow-wrap: anywhere;
}

/* Words for screen readers only: the ratio said in full. */
/* .smbk-contrast-pair__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 sample keeps its two colors, since they are what it shows; its edge and the block's take the reader's text color. */
@media (forced-colors: active) {
  .smbk-contrast-pair,
  .smbk-contrast-pair__sample::after {
    border-color: CanvasText;
  }
}
Tokens it reads
Contrast pair tokens
Token and CSSValuePreviewFoundation
space.inline.lg--smbk-space-inline-lgcomfortable: 12pxcompact: 8pxchanges with densitySpacing
space.inset.md--smbk-space-inset-mdcomfortable: 8pxcompact: 6pxchanges with densitySpacing
border-width.hairline--smbk-border-width-hairline1pxBorder width
color.border.subtle--smbk-color-border-subtlelight: #c4cdd8dark: #2d3237changes with themeColor
radius.container.sm--smbk-radius-container-sm8pxRadius
color.surface.default--smbk-color-surface-defaultlight: #f3f5f8dark: #181a1cchanges with themeColor
size.48--smbk-size-4848pxSize
radius.control.md--smbk-radius-control-md6pxRadius
typography.heading-xs--smbk-typography-heading-xs-font-family--smbk-typography-heading-xs-font-size--smbk-typography-heading-xs-font-weight--smbk-typography-heading-xs-line-height--smbk-typography-heading-xs-letter-spacing--smbk-typography-heading-xs-text-transformfontFamily: Funnel Display, Funnel Display Fallback, system-ui, sans-seriffontSize: min: 20px · max: 28pxfontWeight: 400lineHeight: 1.2letterSpacing: 0px

The quick brown fox

Typography
color.border.edge-shade-subtle--smbk-color-border-edge-shade-subtlelight: #0000001adark: #00000033changes with themeColor
space.stack.xs--smbk-space-stack-xs4pxSpacing
space.inline.sm--smbk-space-inline-smcomfortable: 6pxcompact: 4pxchanges with densitySpacing
color.text.default--smbk-color-text-defaultlight: #232527dark: #e7ebf0changes with themeColor
typography.label-md--smbk-typography-label-md-font-family--smbk-typography-label-md-font-size--smbk-typography-label-md-font-weight--smbk-typography-label-md-line-height--smbk-typography-label-md-letter-spacing--smbk-typography-label-md-text-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px

The quick brown fox

Typography
color.text.subtle--smbk-color-text-subtlelight: #4e555edark: #9ba5b1changes with themeColor
typography.body-xs--smbk-typography-body-xs-font-family--smbk-typography-body-xs-font-size--smbk-typography-body-xs-font-weight--smbk-typography-body-xs-line-height--smbk-typography-body-xs-letter-spacing--smbk-typography-body-xs-text-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 12pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px

The quick brown fox

Typography
Manifest
contrast-pair.manifest.json
{
  "name": "contrast-pair",
  "block": "smbk-contrast-pair",
  "summary": "Two colors drawn as text on its background, with their contrast ratio and the mark it earns: AAA, AA, AA large or fails.",
  "adapt": "css",
  "axes": {
    "result": {
      "values": [
        "aaa",
        "aa",
        "aa-large",
        "fails"
      ],
      "default": "aaa",
      "description": "The mark the ratio earns: AAA at 7:1 and up, AA at 4.5:1, AA large at 3:1 (large text and controls only), else fails. Worked out from the two colors unless given; it sets the badge’s words and type."
    }
  },
  "flags": {},
  "parts": [
    "sample",
    "text",
    "result",
    "ratio",
    "badge",
    "names",
    "hidden"
  ],
  "figmaNodeId": "7416:115"
}