Вывод контраста-цвета() с другими функциями CSS

Вывод контраста-цвета() с другими функциями CSS


У вас есть элемент с настраиваемым цветом фона, и вы хотите рассчитать, должен ли текст переднего плана быть светлым или темным. Кажется достаточно простым, особенно учитывая, насколько сознательно мы должны относиться к доступности.

Недавно было опубликовано несколько проектов спецификаций для этой функции. contrast-color() (ранее color-contrast()) Модуль CSS Color в проекте уровня 5. Но поскольку Safari и Firefox на данный момент являются единственными браузерами, реализовавшими эту функцию, окончательная версия этой функции, вероятно, еще не выпущена. Тем временем в CSS было добавлено много функций; Настолько, что мне захотелось посмотреть, сможем ли мы реализовать это сегодня, кросс-браузерным способом. Вот что у меня есть:

color: oklch(from  round(1.21 - L) 0 0);

Позвольте мне объяснить, как я сюда попал.

ВКАГ 2.2

WCAG предоставляет формулу, используемую для расчета контраста между двумя цветами RGB, а Стейси Ареллано описывает ее очень подробно. Он основан на старых методах, рассчитывает яркость цветов (насколько яркими они воспринимаются) и даже пытается уловить ограничения мониторов и блики экрана:

L1 + 0.05 / L2 + 0.05

…где светлый цвет(L1) находится вверху. Яркость варьируется от 0 до 1, и эта доля отвечает за коэффициент контрастности от 1 (1,05/1,05) до 21 (1,05/05).

Формулы для расчета яркости цветов RGB еще сложнее, но я просто пытаюсь определить, будет ли белый или черный иметь более высокий контраст с данным цветом, и могу избежать этого, немного упростив. В итоге у нас получается что-то вроде этого:

L = 0.1910(R/255+0.055)^2.4 + 0.6426(G/255+0.055)^2.4 + 0.0649(B/255+0.055)^2.4

который мы могу сделать Преобразуйте в CSS следующим образом:

calc(.1910*pow(r/255 + .055,2.4)+.6426*pow(g/255 + .055,2.4)+.0649*pow(b/255 + .055,2.4))

Мы можем округлить все это, используя 1 или 0. round()1 для белого и 0 для черного:

round(.67913 - .1910*pow(r/255 + .055, 2.4) - .6426*pow(g/255 + .055, 2.4) - .0649*pow(b/255 + .055, 2.4))

Давайте умножим его на 255 и используем для всех трех каналов с синтаксисом относительного цвета. Мы заканчиваем этим:

color: rgb(from   
  round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)  
  round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)  
  round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)  
);

Формула, которая при задании цвета возвращает белый или черный цвет на основе WCAG 2. Ее нелегко читать, но она работает… за исключением того, что APCA собирается заменить ее новой, улучшенной формулой в будущих рекомендациях WCAG. Мы можем снова посчитать, хотя APCA — еще более сложная формула. Мы можем воспользоваться функциями CSS, чтобы немного это исправить, но в конечном итоге эта реализация будет недоступна, ее трудно читать и сложно поддерживать.

новый подход

Я сделал шаг назад и задумался о том, что еще нам доступно. У нас есть еще одна новая функция, которую мы можем опробовать: цветовое пространство. “L*Показывает значения в цветовом пространстве CIELAB перцептивная легкость. Оно предназначено для отражения того, что видят наши глаза. Это не то же самое, что яркость, но близко к этому. Возможно, мы сможем догадаться, использовать ли черный или белый цвет для лучшего контраста, основываясь на воспринимаемой светлоте; Давайте посмотрим, сможем ли мы найти число, в котором для любого цвета с меньшей яркостью мы используем черный, а для любого цвета с большей яркостью — белый.

Вы можете интуитивно подумать, что оно должно составлять 50% или 0,5, но это не так. Многие цвета, даже если они яркие, все же лучше сочетаются с белым, чем с черным. Вот несколько примеров использования lch()Постепенно увеличивайте яркость, сохраняя цвет тем же:

Точка перехода, при которой черный текст легче читать, чем белый, обычно находится в диапазоне 60–65. Итак, я собрал приложение быстрого узла, используя Colorjs.io для расчета места обрезки и используя APCA для расчета контраста.

Для oklch()Я обнаружил, что диапазон находится между 0,65 и 0,72, в среднем 0,69.

Другими словами:

  • Когда яркость OKLCH равна 0,72 или выше, черный цвет всегда дает лучший контраст, чем белый.
  • Ниже 0,65 белый всегда дает лучший контраст, чем черный.
  • В диапазоне от 0,65 до 0,72 контрастность обычно составляет 45–60 как в черно-белом, так и в черно-белом режиме.

Итак, просто используя round() А при верхнем пределе 0,72 мы можем сделать новую, меньшую реализацию:

color: oklch(from  round(1.21 - L) 0 0);

Если вам интересно, откуда взялось число 1,21, то это 0,72 с округлением вниз и 0,71 с округлением вверх: 1.21 - .72 = .49 округлить вниз, и 1.21 - .71 = .5 кружить вокруг.

Эта формула работает очень хорошо после запуска в производство нескольких итераций этой формулы. Его легко читать и поддерживать. Тем не менее, эта формула больше соответствует APCA, чем WCAG, поэтому иногда не согласуется с WCAG. Например, WCAG утверждает, что черный цвет имеет более высокую контрастность, если его поместить в черный цвет (4,70 по сравнению с 4,3 у белого). #407ac2Тогда как APCA утверждает обратное: черный цвет имеет контрастность 33,9, а белый — 75,7. Новая формула CSS соответствует APCA и отображается белым цветом:

Вывод контраста-цвета() с другими функциями CSS

Возможно, эта формула может работать лучше, чем WCAG 2.0, поскольку она более точно соответствует APCA. Тем не менее, вам все равно придется проверять доступность, и если вы легально состоите в WCAG, а не в APCA, эта новая, более простая формула может оказаться для вас менее полезной.

ЛЧ против ОКЛЧ

Я проверил цифры для обоих, и помимо того, что OKLCH был разработан как лучшая замена LCH, я также обнаружил, что цифры подтверждают, что OKLCH является лучшим вариантом.

При использовании LCH разница между слишком темным для черного и слишком светлым для белого часто велика, и эта разница еще больше увеличивается. Например, #e862e5 Через #fd76f9 намного темнее черного и намного светлее белого. У LCH эта легкость составляет от 63 до 70; Для OKLCH это от 0,7 до 0,77. Масштабирование легкости OKLCH лучше соответствует APCA.

один шаг вперед

Хотя вариант «Наибольшая контрастность», безусловно, был бы лучше, мы можем применить еще один трюк. Наша текущая логика просто дает нам белое или черное (что color-contrast() Функция в настоящее время ограничена), но мы можем изменить ее, чтобы получить белый или другой заданный цвет. Так, например, белый или исходный цвет текста. начиная с:

color: oklch(from  round(1.21 - L) 0 0);  

/* becomes: */

--white-or-black: oklch(from  round(1.21 - L) 0 0);  
color: rgb(  
  from color-mix(in srgb, var(--white-or-black), )  
  calc(2*r) calc(2*g) calc(2*b)  
);

Это какая-то умная математика, но читать ее неприятно:

  • Если --white-or-black белый, color-mix() результат rgb(127.5, 127.5, 127.5) или ярче; мы в двойном размере rgb(255, 255, 255) или выше, что является абсолютно белым.
  • Если --white-or-black черный, color-mix() Уменьшает значение каждого канала RGB на 50%; Удвоив, мы возвращаемся к исходному значению. .

К сожалению, эта формула не работает в Safari 18 и более ранних версиях, поэтому вам придется ориентироваться на Chrome, Safari 18+ и Firefox. Тем не менее, это дает нам возможность переключаться между белым и базовым цветом текста вместо просто белого и черного с помощью чистого CSS, и мы можем вернуться к белому и черному цвету в Safari.

Вы можете переписать оба из них, используя пользовательские функции CSS, но они по-прежнему поддерживаются не везде:

@function --white-black(--color) {  
  result: oklch(from var(--color) round(1.21 - l) 0 0);  
}

@function --white-or-base(--color, --base) {  
  result: rgb(from color-mix(in srgb, --white-black(var(--color)), var(--base)) calc(2*r) calc(2*g) calc(2*b));  
}

заключение

Я надеюсь, что этот метод подойдет вам, и я хотел бы повторить, что цель этого подхода – поиск диапазона и простой формулы – сделать реализацию гибкой и легко адаптируемой к вашим потребностям. Вы можете легко настроить диапазон так, как вам удобнее.

Leave a Reply

Your email address will not be published. Required fields are marked *