use-paired-color-tokens (stylelint)

This rule requires that background and text color design tokens styling the same surface are used as the semantic pair they were designed as.

Paired tokens share a name apart from the background-color / text-color part, so --button-background-color-primary-hover pairs with --button-text-color-primary-hover. A pair is guaranteed to have sufficient contrast in every theme, in dark mode and under prefers-contrast; combining one half with an unrelated token is not, and the mismatch usually only shows up in the theme the author did not try.

Rule Scope

The rule only reports a declaration block that sets both a background color and a text color, because a block that sets one of them takes the other from somewhere the rule cannot see: an ancestor, a sibling rule, or another pseudo-element.

Within such a block it reports two things:

  • Two paired tokens that are not each other’s counterpart, whether they come from different components (--sidebar-background-color with --panel-text-color) or from different variants of one component (--button-background-color-menu with --button-text-color).

  • Two tokens of one component whose variants differ where the counterpart does not exist as a token at all, e.g. --urlbar-box-background-color-active with --urlbar-box-text-color-hover. Use the component’s base text color, or file a bug for the missing token.

Tokens without a counterpart make no pairing claim and are left alone. That covers the global --background-color-* and --text-color-* tokens, which are meant to combine freely, a component variant that deliberately has no text color of its own and so falls back to the family’s base one, and any value that is not a design token.

Examples of incorrect usage for this rule

.menu-item {
  background-color: var(--button-background-color-menu);
  color: var(--button-text-color);
}

Examples of correct usage for this rule

.menu-item {
  background-color: var(--button-background-color-menu);
  color: var(--button-text-color-menu);
}
.card {
  background-color: var(--panel-background-color);
  color: var(--text-color-deemphasized);
}

Disabling the rule

A surface that genuinely needs an unpaired combination can disable the rule for the declaration, with a comment saying why:

/* The dropdown sits on the toolbar, not on the panel it belongs to. */
/* stylelint-disable-next-line stylelint-plugin-mozilla/use-paired-color-tokens */
color: var(--toolbar-text-color);

If the pair you want does not exist, prefer filing a bug for the missing token over disabling the rule, and reference it from a TODO.