Skip to content

Commit e618e6f

Browse files
authored
Merge pull request #7944 from FerrinThreatt/colorBranch
Add contrast() function to check contrast ratio for two colors
2 parents f78009a + e8569f1 commit e618e6f

File tree

1 file changed

+125
-1
lines changed

1 file changed

+125
-1
lines changed

src/color/p5.Color.js

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import { RGB, RGBHDR, HSL, HSB, HWB, LAB, LCH, OKLAB, OKLCH } from './creating_reading';
1010

11+
1112
import {
1213
ColorSpace,
1314
to,
@@ -25,7 +26,8 @@ import {
2526

2627
OKLab,
2728
OKLCH as OKLCHSpace,
28-
29+
contrastWCAG21,
30+
contrastAPCA,
2931
P3
3032
} from 'colorjs.io/fn';
3133
import HSBSpace from './color_spaces/hsb.js';
@@ -41,6 +43,9 @@ const map = (n, start1, stop1, start2, stop2, clamp) => {
4143

4244
const serializationMap = {};
4345

46+
47+
48+
4449
class Color {
4550
// Reference to underlying color object depending on implementation
4651
// Not meant to be used publicly unless the implementation is known for sure
@@ -326,6 +331,125 @@ class Color {
326331
return colorString;
327332
}
328333

334+
/**
335+
* Checks the contrast between two colors. This method returns a boolean
336+
* value to indicate if the two color has enough contrast. `true` means that
337+
* the colors has enough contrast to be used as background color and body
338+
* text color. `false` means there is not enough contrast.
339+
*
340+
* A second argument can be passed to the method, `options` , which defines
341+
* the algorithm to be used. The algorithms currently supported are
342+
* WCAG 2.1 (`'WCAG21'`) or APCA (`'APCA'`). The default is WCAG 2.1. If a
343+
* value of `'all'` is passed to the `options` argument, an object containing
344+
* more details is returned. The details object will include the calculated
345+
* contrast value of the colors and different passing criteria.
346+
*
347+
* For more details about color contrast, you can check out
348+
* <a href="https://colorjs.io/docs/contrast">this page from color.js</a>, and the
349+
* <a href="https://webaim.org/resources/contrastchecker/">WebAIM color contrast checker.</a>
350+
*
351+
* @param {Color} other
352+
* @returns {boolean|object}
353+
* @example
354+
* <div>
355+
* <code>
356+
* let bgColor, fg1Color, fg2Color, msg1, msg2;
357+
* function setup() {
358+
* createCanvas(100, 100);
359+
* bgColor = color(0);
360+
* fg1Color = color(100);
361+
* fg2Color = color(220);
362+
*
363+
* if(bgColor.contrast(fg1Color)){
364+
* msg1 = 'good';
365+
* }else{
366+
* msg1 = 'bad';
367+
* }
368+
*
369+
* if(bgColor.contrast(fg2Color)){
370+
* msg2 = 'good';
371+
* }else{
372+
* msg2 = 'bad';
373+
* }
374+
*
375+
* describe('A black canvas with a faint grey word saying "bad" at the top left and a brighter light grey word saying "good" in the middle of the canvas.');
376+
* }
377+
*
378+
* function draw(){
379+
* background(bgColor);
380+
*
381+
* textSize(18);
382+
*
383+
* fill(fg1Color);
384+
* text(msg1, 10, 30);
385+
*
386+
* fill(fg2Color);
387+
* text(msg2, 10, 60);
388+
* }
389+
* </code>
390+
* </div>
391+
*
392+
* <div>
393+
* <code>
394+
* let bgColor, fgColor, contrast;
395+
* function setup() {
396+
* createCanvas(100, 100);
397+
* bgColor = color(0);
398+
* fgColor = color(200);
399+
* contrast = bgColor.contrast(fgColor, 'all');
400+
*
401+
* describe('A black canvas with four short lines of grey text that respectively says: "WCAG 2.1", "12.55", "APCA", and "-73.30".');
402+
* }
403+
*
404+
* function draw(){
405+
* background(bgColor);
406+
*
407+
* textSize(14);
408+
*
409+
* fill(fgColor);
410+
* text('WCAG 2.1', 10, 25);
411+
* text(nf(contrast.WCAG21.value, 0, 2), 10, 40);
412+
*
413+
* text('APCA', 10, 70);
414+
* text(nf(contrast.APCA.value, 0, 2), 10, 85);
415+
* }
416+
* </code>
417+
* </div>
418+
*/
419+
contrast(other_color, options='WCAG21') {
420+
if(options !== 'all'){
421+
let contrastVal, minimum;
422+
switch(options){
423+
case 'WCAG21':
424+
contrastVal = contrastWCAG21(this._color, other_color._color);
425+
minimum = 4.5;
426+
break;
427+
case 'APCA':
428+
contrastVal = Math.abs(contrastAPCA(this._color, other_color._color));
429+
minimum = 75;
430+
break;
431+
default:
432+
return null;
433+
}
434+
435+
return contrastVal >= minimum;
436+
}else{
437+
const wcag21Value = contrastWCAG21(this._color, other_color._color);
438+
const apcaValue = contrastAPCA(this._color, other_color._color);
439+
return {
440+
WCAG21: {
441+
value: wcag21Value,
442+
passedMinimum: wcag21Value >= 4.5,
443+
passedAAA: wcag21Value >= 7
444+
},
445+
APCA: {
446+
value: apcaValue,
447+
passedMinimum: Math.abs(apcaValue) >= 75
448+
}
449+
};
450+
}
451+
};
452+
329453
/**
330454
* Sets the red component of a color.
331455
*

0 commit comments

Comments
 (0)