Two colors drawn as text on its background, with their contrast ratio and the mark it earns: AAA, AA, AA large or fails.
Text default on surface default
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
resultsmbk-contrast-pair--result-<value> | aaaaaaa-largefails | aaa | 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. |
Examples
Usage
One pair, drawn as it is, with its ratio and the mark it earns, worked out from the two colors.
Text subtlest on surface default
A contrast grid
Pairs side by side, one for each mark: AAA, AA, AA large and fails.
Text default on surface default
Text subtlest on surface default
Border control on surface default
Border subtle on surface default
A dark pair
The pair keeps its own colors whatever the page’s theme.
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.
Text default on surface default
Name the two roles, so the reader knows which pair the mark belongs to.
Border subtle on surface default
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
/* 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
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| space.inline.lg--smbk-space-inline-lg | comfortable: 12pxcompact: 8pxchanges with density | Spacing | |
| space.inset.md--smbk-space-inset-md | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| border-width.hairline--smbk-border-width-hairline | 1px | Border width | |
| color.border.subtle--smbk-color-border-subtle | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| radius.container.sm--smbk-radius-container-sm | 8px | Radius | |
| color.surface.default--smbk-color-surface-default | light: #f3f5f8dark: #181a1cchanges with theme | Color | |
| size.48--smbk-size-48 | 48px | Size | |
| radius.control.md--smbk-radius-control-md | 6px | Radius | |
| 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-transform | fontFamily: 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-subtle | light: #0000001adark: #00000033changes with theme | Color | |
| space.stack.xs--smbk-space-stack-xs | 4px | Spacing | |
| space.inline.sm--smbk-space-inline-sm | comfortable: 6pxcompact: 4pxchanges with density | Spacing | |
| color.text.default--smbk-color-text-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| 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-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| 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-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 12pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
Manifest
{
"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"
}