So I wanted to write a color contrast calculator for a given pair of foregroundRGBA
and backgroundRGBA
colors, using the Advanced Perpetual Contrast Algorithm (APCA) (read Google WebDev's mini-article on APCA).
While the aforementioned site has some code samples to do the same, they are Invalid when either colors have transparency (0 ≤ α < 1)
Point no. 1 is the biggest problem here, since foreground and/or background colors may be semi-transparent. The problem arises in such cases. And I can't really do anything about it. For example, the RGBColor.toY
function on that site only works for an sRGB color, and not for RGB colors with alpha transparency.
This is where I need help.
How do I write a color contrast calculator using APCA for a given pair of foregroundRGBA
and backgroundRGBA
RGBA
color objects?
I have copied the constants used in the site and have written some code for some functions, and the RGBA
color class (whose objects are to be passed as parameters for the to-be-written APCA color contrast calculator, which can use the RGBA
objects and their attributes for easier calculations and readability).
Constants given and used in the site
const sRGBtrc = 2.4; // Gamma for sRGB linearization. 2.223 could be used instead
// 2.218 sets unity with the piecewise sRGB at #777
const Rco = 0.2126; // sRGB Red Coefficient
const Gco = 0.7156; // sRGB Green Coefficient
const Bco = 0.0722; // sRGB Blue Coefficient
const scaleBoW = 1.14; // Scaling for dark text on light z
const scaleWoB = 1.14; // Scaling for light text on dark — same as BoW, but
// this is separate for possible future use.
const scaleOffset = 0.027; // Offset
const normBGExp = 0.56; // Constants for Power Curve Exponents.
const normTXTExp = 0.57; // One pair for normal text,and one for REVERSE
const revBGExp = 0.62; // FUTURE: These will eventually be dynamic
const revTXTExp = 0.65; // as a function of light adaptation and context
const blkThrs = 0.022; // Level that triggers the soft black clamp
const blkClmp = 1.414; // Exponent for the soft black clamp curve
Misc. functions
// Clamps a value between a given range [minimum, maximum]
const clamp = (value, minimum=0, maximum=1) => {
if (value < minimum) return minimum;
if (value > maximum) return maximum;
return value;
}
// Modified clamp to clamp an RGBA color value in [0, 255]
const clampColor = (value=0) => clamp(Math.round(value), 0, 255);
The RGBA
color class
// An RGBA color class to be used by `foregroundRGBA` and `backgroundRGBA`
class RGBA {
constructor(red=0, green=0, blue=0, alpha=1) {
// Clamp the given r, g, b arguments between 0 and 255
this._red = clampColor(red);
this._green = clampColor(green);
this._blue = clampColor(blue);
// Clamp a between 0 and 1 since it is alpha
this._alpha = clamp(alpha);
}
get r() {
return this._red;
}
set r(value) {
this._red = clampColor(value);
}
get linearizedR() {
return (this._red / 255) ^ sRGBtrc;
}
get g() {
return this._green;
}
set g(value) {
this._green = clampColor(value);
}
get linearizedG() {
return (this._green / 255) ^ sRGBtrc;
}
get b() {
return this._blue;
}
set b(value) {
this._blue = clampColor(value);
}
get linearizedB() {
return (this._blue / 255) ^ sRGBtrc;
}
get a() {
return this._alpha;
}
set a(value) {
this._alpha = clamp(value);
}
// Not given in the site (since the site is using RGB and not RGBA)
// Written based on assumption (since this._alpha is already betwen 0 and 1 unlike the others)
get linearizedA() {
return this._alpha ^ sRGBtrc;
}
// Relative Luminance (Y) property of a given RGBA color
// Works only for RGB and not RGBA (since the formula makes no use of this._alpha and purely returns the value based on RGB and not RGBA)
get Y() {
return (
Math.pow(this._red/255, sRGBtrc) * Rco
+ Math.pow(this._green/255, sRGBtrc) * Gco
+ Math.pow(this._blue/255, sRGBtrc) * Bco
// Assumption again
// Also, if there exists an `Aco`, its value is unknown (part of my question)
// + Math.pow(this._alpha, sRGBtrc) * Aco
);
}
// Modified `toString` to return a CSS-compatible RGBA color
toString() {
return `rgba(${this._red},${this._green},${this._blue},${this._alpha})`;
}
}
The APCA-based color contrast calculator to be written
const getAPCAColorContrast = (foregroundRGBA=new RGBA(), backgroundRGBA=new RGBA(255, 255, 255)) => {
// Code to be written according to the algorithm using passed RGBA objects and their properties
// WHICH MUST BE COMPATIBLE WITH RGBA COLORS AND NOT JUST RGB COLORS
return colorContrast; // Calculated APCA-based color contrast
}
I tried looking for an APCA that can calculate for RGBA colors everywhere but failed.
I will try my best to answer any questions you have to help you give a better answer.
Please let me know if I need to make any edits to make a better question.
I would be thankful to you if you find a way to calculate contrast using APCA for RGBA (not just RGB) colors.
Edit 1: Please note that I do not need code to fill the function (getAPCAColorContrast
). All I need is an APCA algorithm compatible with not just RGB but RGBA. I can write the code myself, once I know and understand the modified APCA algorithm for RGBA foreground and background colors. (This disclaimer has been written here so that the question doesn't get taken down for breaking rules, if any).