Evercoder/culori

Add support for WCAG3 contrast using APCA

meodai opened this issue · 8 comments

Given APCA's license requirements around accuracy, correctness and up-to-dateness, as well as its current status as a work in progress, I don't see it as a good fit for inclusion in culori at the moment, but since culori itself is mostly a collection of functions, interoperation is fairly simple.

To obtain the Y (luminance) to pass to the contrast algorithm, you can use the wcagLuminance() function.

import { wcagLuminance } from 'culori';
import { APCAcontrast } from 'apca-w3';

const textY = wcagLuminance('black');
const backgroundY = wcagLuminance('tomato');
const contrast = APCAcontrast(textY, backgroundY);

Note that culori's wcagLuminance function is a bit different (but not fundamentally) from the equations proposed in apca-w3. You can use apca-w3's sRGBtoY() as an alternative computation that matches the exact formula.

@danburzo thanks! I was not aware of the "non commercial" license. Such a strange decisions, especially for something like that

@danburzo looks like there is a different license for the w3 version, that is more permissive: https://github.com/Myndex/apca-w3

I was actually referring to the license for the w3 version :-).

To give an example, since some of the math — such as conversion between color spaces, luminance computation etc. — is already in our codebase, we would want to use that code. apca-w3 uses slightly different constants & formulas (see this w3c/silver issue), which would make our implementation non-compliant (I think!).

It really sounds like this should remain a separate library, at least until it gets merged in some form in w3 specs.

Makes total sense. Thanks for clarifying.

...since some of the math — such as conversion between color spaces, luminance computation etc. — is already in our codebase, we would want to use that code. apca-w3 uses slightly different constants & formulas.... which would make our implementation non-compliant (I think!).

Hi @danburzo and @meodai

Yes, APCA does not use the sRGB to XYZ of IEC, instead, APCA calculates "Estimated Screen Luminance" Which is intended to more correctly model a calibrated sRGB monitor. And this is important for some yet unreleased feaures that more specifically accommodate certain color vision issues.

As far as color spaces, as it is perceptual contrast, It's based around color spaces of physical monitors as opposed to theoretical spaces.

We have been talking about ways to reconcile with the "math convenience" methods, But I'm currently very swamped with other pressing matters.

IN THE MEANTIME

There is a variant that is designed to work with existing standard color maths—convert the color to $CIE\ L*$ (Lstar) and then use DeltaPhiStar (DPS contrast).

It is not polarity aware, and is a more general contrast math, But it is also much much simpler. Here's the repo:

https://github.com/Myndex/deltaphistar

@Myndex thanks for the pointer! So that would basically be:

import { lab65 } from 'culori';

/*
  Delta Phi Star perceptual lightness contrast by Andrew Somers:
  https://github.com/Myndex/deltaphistar 
*/
const PHI = 0.5 + Math.sqrt(1.25);

function dpsContrast(a, b) {
  const dps = Math.abs(Math.pow(lab65(a).l, PHI) - Math.pow(lab65(b).l, PHI));
  const contrast = Math.pow(dps, 1/PHI) * Math.SQRT2 - 40;
  return contrast < 7.5 ? 0 : contrast;
}
Myndex commented

Hi @danburzo

Doh! I just saw this, but yes, that's it... Be aware there may be a change regarding DPS contrast in the next few months, mainly for tighter alignment with APCA light mode.