Коэффициент контраста — не разница двух hex-чисел и не среднее красного, зелёного и синего. WCAG использует заданную функцию преобразования sRGB и веса яркости, а затем сравнивает более светлый и тёмный цвета. Явные этапы делают пограничный результат воспроизводимым.
Разверните и нормализуйте три канала
Короткая запись #ABC разворачивается в #AABBCC. Каждый двухзначный канал становится целым от 0 до 255 и делится на 255, давая кодированное значение sRGB от 0 до 1. ContrastCheck принимает только непрозрачный hex из трёх или шести цифр, поэтому альфа-смешивание, CSS-имена и преобразования пространств не попадают в расчёт незаметно.
Преобразуйте sRGB в линейный свет
Для нормализованного канала не выше 0.04045 значение делится на 12.92. Выше порога к нему прибавляют 0.055, делят на 1.055 и возводят в степень 2.4. Относительная яркость равна 0.2126 красного плюс 0.7152 зелёного плюс 0.0722 синего в линейном виде. Зелёный имеет наибольший вес, поэтому одинаковое изменение каналов влияет по-разному.
Сравните светлую и тёмную яркость
Пусть L1 — большая яркость, L2 — меньшая. Коэффициент равен (L1 + 0.05) / (L2 + 0.05), поэтому перестановка цветов не меняет величину, а диапазон идёт от 1:1 до 21:1. ContrastCheck проверяет пороги по точному значению. Только видимое число округляется вниз до двух знаков, чтобы не показать 4.50 при фактическом результате ниже 4.5.