diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index 888c9606d6..de91ea4906 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -8,6 +8,7 @@ import { RGB, RGBHDR, HSL, HSB, HWB, LAB, LCH, OKLAB, OKLCH } from './creating_reading'; + import { ColorSpace, to, @@ -25,7 +26,7 @@ import { OKLab, OKLCH as OKLCHSpace, - + contrastWCAG21, P3 } from 'colorjs.io/fn'; import HSBSpace from './color_spaces/hsb.js'; @@ -41,6 +42,9 @@ const map = (n, start1, stop1, start2, stop2, clamp) => { const serializationMap = {}; + + + class Color { // Reference to underlying color object depending on implementation // Not meant to be used publicly unless the implementation is known for sure @@ -326,6 +330,99 @@ class Color { return colorString; } + /** + * Checks the contrast between two colors, to make sure that they + * are different enough to be readable. The result of this function is + * a color contrast ratio that can be compared to `COLOR_CONTRAST_MINIMUM_GRAPHICS`, + * or to `COLOR_CONTRAST_MINIMUM_TEXT`. The higher the ratio, the more + * different colors are, and the more legible to a user. + * + * Graphics, interface elements, and large text should have a color + * contrast ratio of at least 4.5 (`COLOR_CONTRAST_MINIMUM_GRAPHICS`) + * + * Smaller text - less than at least 14 point or 19 pixels - + * should have a color contrast ratio of at least 7 + * (`COLOR_CONTRAST_MINIMUM_TEXT`) + * + * The constants are based on WCAG AAA recommendations, which you can also explore in this + * contrast checker tool. + * The contrast function in p5.js uses the WCAG 2.1 method the + * color.js contrast + * utility. + * + + * + * @param {Color} other + * @returns {{ ratio: Number }} + * @example + *
+ * + * + * // The contrast checker can be used both during development + * // with `print()`, or to help select readable colors on the fly. + * // This example shows both uses. + * + * let bgColor; + * let fg1Color; + * let fg2Color; + * + * function setup() { + * createCanvas(100, 100); + * bgColor = color(0); + * fg1Color = color(120); + * fg2Color = color(255); + * + * describe('A small square canvas with acentered text outlined by a thick stroke. The text reads 'click again!'. On every mouse click, the background, square outline, and text colors randomize, with high enough contrast for readability.'); + * } + * + * function draw() { + * background(bgColor); + * stroke(fg1Color); + * noFill(); + * strokeWeight(5); + * rect(10, 10, 80, 80); + * + * noStroke(); + * fill(fg2Color); + * textAlign(CENTER, CENTER); + * textSize(20); + * text("click\nagain!", 50, 50); + * } + * + * function mouseClicked(){ + * let newBgColor; + * let newFg1Color; + * let newFg2Color; + * + * // The loop may go for a long time, but it will not go on forever + * // It will stop the first time that the random colors contrast enough + * for (let i = 0; i < 10000; i += 1){ + * newBgColor = color(random(255), random(255), random(255)); + * newFg1Color = color(random(255), random(255), random(255)); + * newFg2Color = color(random(255), random(255), random(255)); + * if ( + * newBgColor.contrast(newFg2Color) >= COLOR_CONTRAST_MINIMUM_TEXT && + * newBgColor.contrast(newFg1Color) >= COLOR_CONTRAST_MINIMUM_GRAPHICS && + * newBgColor.contrast(newFg1Color) < COLOR_CONTRAST_MINIMUM_TEXT ){ + * + * bgColor = newBgColor; + * fg1Color = newFg1Color; + * fg2Color = newFg2Color; + * + * break; + * } + * } + * + * print("Contrast (rect)", bgColor.contrast(fg1Color)); + * print("Contrast (text)", bgColor.contrast(fg2Color)); + * } + * + *
+ */ + contrast(other_color) { + return contrastWCAG21(this._color, other_color._color); + }; + /** * Sets the red component of a color. * diff --git a/src/core/constants.js b/src/core/constants.js index 942b48c9ad..d42ed5ab72 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -1371,3 +1371,22 @@ export const EXCLUDE = Symbol('exclude'); * @private */ export const JOIN = Symbol('join'); + +/** + * Can be used with `Color.contrast` to check if graphics, UI elements, and large text + * have enough contrast. + * @typedef {'color-contrast-minimum-graphics'} COLOR_CONTRAST_MINIMUM_GRAPHICS + * @property {COLOR_CONTRAST_MINIMUM_GRAPHICS} COLOR_CONTRAST_MINIMUM_GRAPHICS + * @final + */ +export const COLOR_CONTRAST_MINIMUM_GRAPHICS = 4.5; + + +/** + * Can be used with `Color.contrast` to check if text smaller than 14pt (~19px) + * has enough contrast. + * @typedef {'color-contrast-minimum-text'} COLOR_CONTRAST_MINIMUM_TEXT + * @property {COLOR_CONTRAST_MINIMUM_TEXT} COLOR_CONTRAST_MINIMUMD_TEXT + * @final + */ +export const COLOR_CONTRAST_MINIMUM_TEXT = 7.0;