A11y Integration (beta)
Lint your token schema for a11y errors. Can check your color + typography tokens for contrast.
Install the plugin:
npm i -D @cobalt-ui/lint-a11y
Then add to your tokens.config.js
file:
// tokens.config.js
import a11y from "@cobalt-ui/lint-a11y";
/** @type {import("@cobalt-ui/core").Config} */
export default {
tokens: "./tokens.json",
outDir: "./tokens/",
plugins: [a11y()],
lint: {
// checks
},
};
Rule | Default | Description |
---|---|---|
a11y/contrast | "off" | Run WCAG2 and APCA contrast checks on all of your tokens (you have to manually specify the pairs, but it will work on any tokens in tokens.json ) |
a11y/contrast
The contrast check asserts your token combinations the latest WCAG 2.1 and APCA (WCAG 3 proposal) formulae. Add an array of checks
to test:
import a11y from "@cobalt-ui/lint-a11y";
/** @type {import("@cobalt-ui/core").Config} */
export default {
tokens: "./tokens.json",
outDir: "./tokens/",
plugins: [a11y()],
lint: {
rules: {
"a11y/contrast": [
"error",
{
checks: [
{
tokens: {
foreground: "color.semantic.text",
background: "color.semantic.bg",
typography: "typography.body",
modes: ["light", "dark"],
},
wcag2: "AAA",
apca: true,
},
],
},
],
},
},
};
Check options
Within each check group, specify:
Name | Type | Description |
---|---|---|
tokens | object | A group of tokens to test together. |
tokens.foreground | string | The ID of the foreground color. |
tokens.background | string | The ID of the background color. |
tokens.typography | string | (optional) The ID of a typography stack |
tokens.modes | string[] | (optional) Any modes you’d like to test |
wcag2 | string | number | false | Specify "AA" or "AAA" compliance (or a minimum contrast), or false to disable (default: "AA" ). See WCAG 2 |
apca | "silver" | "silver-nonbody" | number | false | Specify Silver compliance or a specific Lc number (default: false ). See APCA. |
WCAG 2
The WCAG 2 contrast formula is represented by the wcag2
setting and accepts either a string, number, or false
:
{
checks: [
{
tokens: { /* … */ },
wcag2: "AA"; // "AAA" | "AA" | number | false
},
],
}
The WCAG 2 standard is the most common contrast standard, so "AA"
level is enforced by default by this plugin.
Add a typography
token value to automatically figure out if you’re using large text (which lowers the minimum contrast requirement).
APCA (beta)
The APCA contrast algorithm is still in beta, but is a likely candidate for the upcoming WCAG 3 contrast algorithm. Like WCAG 2 there is a “contrast ratio” under-the-hood, but it’s referred to as “perceptual lightness contrast,” or Lc. It’s a number ranging from 0
– 100
, and like WCAG 2 your target number depends on a few factors. The rough equivalence is:
Lc | WCAG 2 Contrast Ratio |
---|---|
90 | 7:1 |
75 | 4.5:1 |
60 | 3:1 |
But the math isn’t as simple as WCAG 2; you’ll have to do some work to determine what your target Lc is for any color/font size/font weight trio. And so, APCA specifies “Bronze” and “Silver” levels of compliance (“Gold” isn’t outlined yet). Cobalt can determine Silver compliance automatically for you, but you’ll need to manually manage Bronze compliance for now (explained below).
{
checks: [
{
tokens: { /* … */ },
apca: "silver"; // "silver" | "silver-nonbody" | number | false
},
],
}
Setting | Description |
---|---|
"bronze" | (not supported in Cobalt; set Lc manually following “Bronze” guide) |
"silver" | Enforce Silver-level compliance for body text. Requires typography token. |
"silver-nonbody" | Silver-level compliance for non-body text (less strict). Requires typography token. |
"gold" | (not supported in APCA yet) |
number | ⚠️ Advanced users: specify Lc. |
false | Disable APCA. |
WARNING
APCA is still a draft, and not part of WCAG 3. But APCA is well-researched and widely-regarded as an improvement over WCAG 2. Compliance with APCA doesn’t guarantee compliance with WCAG 3 when it releases.
Silver (auto) mode vs Number (manual) mode
The same basic principle applies to both WCAG 2 and APCA: contrast is a triangle of color–font size–font weight. Lc only refers to color contrast—you then have to figure out the other two “points” of the triangle—font size and font weight—to complete the picture. It makes sense when you think about it: if you only factored in color contrast, a 2px-tall font would “pass” if the color contrast were good enough.
However, trying to reduce typography into pure math turns into a can of worms quickly, so APCA makes some concessions in letting you, the designer, have more say over what your target Lc is. More flexibility comes at the cost of more manual intervention. To navigate that, Cobalt has 2 “flavors” of APCA support: Silver compliance (auto), and Number (manual).
Silver (auto) mode
Silver mode requires a typography
token; it will err without that. But with all that provided, Cobalt does the work for you. Set apca
to "silver"
(body text) or "silver-nonbody"
(non-body text; less strict). If this works for you, yay.
Number (manual) mode
As mentioned earlier, APCA gives you more autonomy over declaring what your “body” font style is, but at the tradeoff of no concrete numbers. In Number (manual) mode, you’ll have to specify Lc manually. Using manual mode you can still pass Bronze and Silver levels, but all Cobalt can do is catch regressions; the compliance part is up to you. Here’s the guide copied directly from APCA’s (all rights ©️ Myndex):
Lc | Description |
---|---|
90 | Preferred level for fluent text and columns of body text with a font no smaller than 18px/weight 300 or 14px/weight 400 (normal), or non-body text with a font no smaller than 12px. Also a recommended minimum for extremely thin fonts with a minimum of 24px at weight 200. Lc 90 is a suggested maximum for very large and bold fonts (greater than 36px bold), and large areas of color. |
75 | The minimum level for columns of body text with a font no smaller than 24px/300 weight, 18px/400, 16px/500 and 14px/700. This level may be used with non-body text with a font no smaller than 15px/400. Also, Lc 75 should be considered a minimum for larger for any larger text where readability is important. |
60 | The minimum level recommended for content text that is not body, column, or block text. In other words, text you want people to read. The minimums: no smaller than 48px/200, 36px/300, 24px normal weight (400), 21px/500, 18px/600, 16px/700 (bold). These values based on the reference font Helvetica. To use these sizes as body text, add Lc 15 to the minimum contrast. |
45 | The minimum for larger, heavier text (36px normal weight or 24px bold) such as headlines, and large text that should be fluently readabile but is not body text. This is also the minimum for pictograms with fine details, or smaller outline icons, , no less than 4px in its smallest dimension. |
30 | The absolute minimum for any text not listed above, which means non-content text considered as "spot readable". This includes placeholder text and disabled element text, and some non-content like a copyright bug. This is also the minimum for large/solid semantic & understandable non-text elements such as "mostly solid" icons or pictograms, no less than 10px in its smallest dimension. |
15 | The absolute minimum for any non-text that needs to be discernible and differentiable, but does not apply to semantic non-text such as icons, and is no less than 15px in its smallest dimention. This may include dividers, and in some cases large buttons or thick focus visible outlines, but does not include fine details which have a higher minimum. Designers should treat anything below this level as invisible, as it will not be visible for many users. This minimum level should be avoided for any items important to the use, understanding, or interaction of the site. |
Note on how APCA handles typography
APCA’s typography tables are based off Helvetica. Most people aren’t using Helvetica as their brand font, so APCA allows some wiggle room in interpreting your actual contrast numbers (see Notes on Font Size & Weight). Read the guide and see if your actual Lc is different than what Cobalt is reporting, and adjust by-hand (apca: 59
).
Bridge PCA
Bridge PCA is also a creation of Myndex (same as APCA) and is meant to help “bridge” the gap between WCAG 2 and APCA.
Bridge PCA isn’t supported yet but will be in an upcoming release.
Others
Are there other checks that you’d like to see here? Suggest one!