Конвертер цветов и проверка контраста

Преобразует между HEX, RGB, HSL, OKLCH и именами цветов CSS и проверяет коэффициент контраста по WCAG между двумя цветами.

Передний план
  • HEX#3b82f6
  • RGBrgb(59, 130, 246)
  • HSLhsl(217, 91.2%, 59.8%)
  • OKLCHoklch(62.31% 0.188 259.81)
  • ИмяТочного имени CSS нет
Фон
  • HEX#ffffff
  • RGBrgb(255, 255, 255)
  • HSLhsl(0, 0%, 100%)
  • OKLCHoklch(100% 0 0)
  • Имяwhite
Контраст
3.68:1Коэффициент контраста
Основной текст
Меньше 18pt или меньше 14pt полужирного
AAAAA
Крупный текст
От 18pt или от 14pt полужирного
AAAAA
Элементы управления и графика
Границы, значки и другие нетекстовые части
AA

Именно для основного текста такого размера и написан порог 4.5:1.

Крупному тексту достаточно 3:1

Что делает этот инструмент

Две задачи, которые всегда оказываются одной. У вас есть цвет, и он нужен в другой записи: файл макета говорит одно, а таблица стилей хочет другое. И у вас есть два цвета, и нужно знать, читается ли текст первым на втором. Поэтому эта страница берёт два цвета, показывает каждый сразу во всех форматах и измеряет контраст между ними.

Всё выполняется в вашем браузере. Ничему из ещё не вышедшей фирменной палитры не нужно ехать на сервер, чтобы преобразоваться.

Пять записей

Любую из них можно вставить, и все они возвращаются в выводе:

  • Шестнадцатеричная — три, четыре, шесть или восемь цифр. Короткие формы удваивают каждую цифру, поэтому #f0a означает #ff00aa. Четыре и восемь цифр несут альфу в последней позиции.
  • RGB — три канала числами или процентами. Принимаются и rgb(59, 130, 246), и rgb(59 130 246 / 50%); обратно инструмент пишет форму с запятыми, потому что именно её по-прежнему использует большая часть кода вокруг вас.
  • HSL — тон в градусах, затем насыщенность и светлота в процентах. Тона заворачиваются, поэтому 480 и -120 допустимы и означают 120 и 240.
  • OKLCH — светлота, хрома и тон в перцептивно равномерном пространстве. Светлоту можно писать как 62.8%, так и 0.628.
  • Имена CSS — все 148, включая rebeccapurple и оба написания каждого серого. Имя появляется в выводе только при точном совпадении.

Одну особенность стоит знать: шестнадцатеричная запись, RGB и OKLCH переживают путь туда и обратно без изменений, а HSL — не вполне. Он печатается с той точностью, которую люди действительно читают: целые градусы, один знак после запятой у процентов, — и её не хватает, чтобы адресовать все 16,7 миллиона цветов sRGB. Переведите шестнадцатеричный цвет в HSL и обратно, и канал может сдвинуться на единицу. Альтернативой был бы вывод вроде hsl(217.22, 91.19%, 59.8%), который никто не хочет вставлять. Ошибка намного ниже того, что различает глаз.

Зачем нужен OKLCH

HSL выглядит так, будто описывает светлоту, и не описывает. Его число светлоты — свойство чисел, а не света. У чистого жёлтого и чистого синего в HSL одинаковые 50% светлоты:

#ffff00   hsl 50%   oklch 96.8%   жёлтый
#0000ff   hsl 50%   oklch 45.2%   синий

Любой видит, что жёлтый из этих двух светлее, и OKLCH это подтверждает. Дело не в теории: именно поэтому палитра, построенная при постоянной светлоте HSL с прокруткой тона, даёт набор цветов, выглядящий дико неровным, а тот же приём в OKLCH даёт набор, похожий на семью. И именно поэтому в OKLCH два цвета одной светлоты обычно дают близкий контраст на одном фоне — чего HSL не может обещать вовсе.

Два других числа — это хрома (грубо говоря, насколько цвет насыщен, от 0 для серого) и тон в градусах. В отличие от насыщенности в HSL, хрома не является процентом от чего-либо, поэтому предельное полезное значение зависит и от светлоты, и от тона.

Когда цвет невозможно показать

OKLCH способен описать больше цветов, чем может воспроизвести обычный экран. Запросите светлоту 70% при хроме 0.4 — и вы назвали зелёный, который существует в принципе и нигде в sRGB. Инструмент прижимает такой цвет к диапазону и помечает его как выходящий за sRGB, вместо того чтобы молча вернуть вам что-то другое.

Предупреждение появляется только тогда, когда прижатие действительно меняет получаемый цвет. Это различие весит больше, чем кажется. Красный sRGB лежит ровно на границе охвата, поэтому опубликованное для него значение OKLCH, округлённое до четырёх знаков, технически выходит наружу на волосок — примерно на две десятитысячных, которые попадают в тот же байт и невидимы. Предупреждать об этом значило бы приучить вас не обращать внимания на предупреждения. Цвет из палитры с широким охватом промахивается на целые шаги: blue-500 из Tailwind v4 просит 261 из возможных 255 в синем канале, и на экране sRGB это действительно не тот цвет, который был запрошен.

Контраст и три его порога

Коэффициент контраста сравнивает относительную яркость двух цветов. Он идёт от 1:1 для двух одинаковых цветов до 21:1 для чёрного на белом, и превзойти это невозможно. WCAG 2.1 задаёт три порога, и они не взаимозаменяемы:

  • 4.5:1 для основного текста — всё, что меньше 18pt или меньше 14pt полужирного. Именно его имеют в виду, когда говорят, что цвет «не проходит по контрасту».
  • 3:1 для крупного текста — от 18pt или от 14pt полужирного. Крупные литеры сами несут больше сигнала, поэтому планка ниже.
  • 3:1 для элементов управления и графики — рамка поля ввода, стрелка выпадающего списка, столбики диаграммы. Добавлен в WCAG 2.1 и повсеместно упускается, потому что люди проверяют свой текст и никогда — рамку вокруг него.

Уровень AAA поднимает первые два до 7:1 и 4.5:1. К нему стоит стремиться в основном тексте, и с фирменной палитрой он по-настоящему труднодостижим; регламенты и аудиты требуют AA.

Размеры в этих правилах — пункты CSS, а не пиксели: 18pt — это 24px, а 14pt — около 18,7px при настройках по умолчанию. Легко решить, что заголовок в 20px считается крупным текстом. Не считается.

Полупрозрачные цвета

У полупрозрачного текста на сплошном фоне коэффициент контраста определён совершенно однозначно: браузер смешивает два цвета перед отрисовкой, и этот инструмент тоже. Серый текст с непрозрачностью 60% — это просто более светлый серый, как только он лёг, и измеряется он как то, чем лёг. Инструменты, игнорирующие альфа-канал, сообщают коэффициент того цвета, который вы написали, а не того, который кто-либо видит, и это всегда завышение: проверяльщик доступности, ошибающийся в вашу пользу, хуже, чем никакой.

Полупрозрачный фон — другая ситуация, и инструмент от неё отказывается. Что просвечивает, зависит от того, что лежит ниже, а на этот вопрос два цвета на этой странице ответить не могут. Приведите фон к цвету, в который он на самом деле складывается, и измерьте его.

О чём коэффициент не говорит

Пройденный порог — это пол, а не приговор. Три вещи, которых он не покрывает:

  • Цветовая слепота. У красного и зелёного одинаковой яркости коэффициент близок к 1:1, и они неразличимы вообще для всех, — но два цвета могут пройти по коэффициенту и всё равно остаться единственным, что разделяет два состояния, а это не работает для того, кто их не различает. Никогда не несите смысл одним лишь тоном.
  • Тонкие начертания. Формула ничего не знает о толщине штриха. Волосяное начертание при 4.5:1 может читаться тяжелее обычного при 4:1.
  • Сама формула. Известно, что контраст WCAG 2 неблагосклонен к светлому тексту на тёмном фоне и сообщает коэффициенты, которые кажутся выше, чем читаются. APCA, разработанная для WCAG 3, моделирует это лучше, — но она не нормативна, ни один аудит её пока не принимает, а показ двух противоречащих оценок никому бы не помог. Поэтому о ней говорится здесь, а не печатается выше.

Замечание по реализации для тех, кто сверяет результаты со своим кодом: формула яркости использует порог 0.03928, слегка отличающийся от 0.04045 в нынешнем определении sRGB. WCAG цитирует старое значение, поэтому здесь используется оно. Соответствие спецификации важнее внутренней стройности, и эта разница никогда не превращает «проходит» в «не проходит».

Частые вопросы

Почему мой шестнадцатеричный цвет меняется после перевода в HSL и обратно?
Потому что HSL печатается с точностью, которую человек может прочитать и перенабрать, а её не хватает, чтобы назвать каждый цвет sRGB. Сдвигается самое большее на один шаг по каналу, и это незаметно. Шестнадцатеричная запись, RGB и OKLCH возвращаются точно — берите любой из них, если нужен промежуточный формат без потерь.
Что означает «За пределами sRGB»?
Введённое значение OKLCH называет цвет, который ваш экран не может воспроизвести, поэтому он прижат к ближайшему из доступных. Значения из палитр с широким охватом вроде Tailwind v4 делают это регулярно. Предупреждение появляется, только если прижатие меняет отображаемый цвет, поэтому округлённое значение, попадающее в тот же байт, его не вызовет.
Мой текст проходит AA. Значит, он доступен?
Он берёт одну конкретную планку. Коэффициент ничего не говорит о том, является ли цвет единственным, что различает два состояния, о толщине штриха и о том, как сочетание читается человеку со слабым зрением. Считайте прохождение началом проверки, а не её концом.
Почему мой заголовок в 20px не крупный текст?
Правило написано в пунктах, а не в пикселях: 18pt — это 24px, а 14pt полужирного — около 18,7px. Обычный заголовок в 20px оказывается ниже черты и измеряется по 4.5:1, как любой основной текст.
Можно ли проверить контраст для полупрозрачного фона?
Только по двум цветам — нет: ответ зависит от того, что за ним, а страница этого не знает. Вычислите цвет, в который складывается фон, и введите его. А вот полупрозрачный передний план работает и обрабатывается верно: перед измерением он накладывается на фон ровно так, как рисует браузер.
Отправляется ли моя палитра куда-нибудь?
Нет. Разбор, преобразование и расчёт контраста выполняются в вашем браузере; ничто из введённого не покидает ваше устройство.