Управление темами системы доступного дизайна с помощью CSS Color-Contrast()

КРАТКОЕ РЕЗЮМЕ ↬ Разработка доступных продуктов может быть сложной задачей, особенно когда некоторые требования выходят за рамки разработки. Одно дело — обеспечить alt текста для изображений и меток для полей формы, другое — определить доступную цветовую палитру. Функция CSS color-contrast() может стать краеугольным камнем для разработчиков в обеспечении достаточно контрастного и доступного пользовательского интерфейса — от работы с передачей дизайна до поддержки пользовательских тем в системе дизайна.


Конечно, нет недостатка в системах дизайна, которые можно использовать при создании следующего проекта. Между Carbon, Wanda и Nord от IBM существует множество потрясающих систем дизайна, из которых можно выбирать. И хотя каждая из них имеет свои нюансы и мнения, большинство из них имеют схожую цель — упрощение процесса разработки для создания красивых доступных пользовательских интерфейсов.

Это достойная восхищения цель, и, честно говоря, именно она привела меня к тому, что моя карьера переключилась на системы проектирования. Но ключевой особенностью, лежащей в основе многих систем дизайна, является расширяемость для создания тематики. А почему бы и нет? Без определенной гибкости для брендинга каждый продукт, использующий определенную систему, будет выглядеть одинаково, как Bootstrap в 2012 году.

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

Другие просто не могут и/или не хотят этого делать.

Одно дело — требовать alt текста на элементе img или label для элемента input, но принудительное обеспечение доступной цветовой палитры — это совершенно другой зверь. Это зверь с зазубренными желтыми зубами, свирепыми красными глазами и зеленой чешуей, покрывающей его тело, как листы крокодиловой брони.

По крайней мере, вы так думаете. Но, как знать, может быть, это чудовище состоит не более чем из нечетких оттенков черного и чуть более темного.

И в этом кроется проблема.

Функция CSS Color-Contrast()

Создание инклюзивных продуктов означает не поддержку устройств, а поддержку людей, которые ими пользуются.

Функция CSS color-contrast() — это экспериментальная функция, которая в настоящее время является частью Color Module 5. Ее цель — и причина, по которой эта статья вызвала такой ажиотаж — выбрать самый контрастный цвет из списка при сравнении с базовым цветом.

Я заинтригован

Именно в докладе Рейчел Эндрю на AxeCon 2022 «Новый CSS с учетом доступности» я познакомился с color-contrast(). Я записал эту функцию в блокнот и обвел ее несколько раз, чтобы она стала заметной. Поскольку в последнее время мои мысли были полностью заняты системами дизайна, мне стало интересно, насколько большое влияние может оказать эта маленькая функция CSS в данном контексте.

В своей презентации Рейчел продемонстрировала новую функцию, динамически определяя цвета текста на основе фона. Итак, давайте начнем с этого, установив цвета фона и текста для статьи.

article {
  --article-bg: #222;

  background: var(--article-bg);
  color: color-contrast(var(--article-bg) vs #FFF, #000);
}
Вход в полноэкранный режим Выход из полноэкранного режима

Начнем с определения пользовательского свойства --article-bg как темно-серого цвета, #222. Это свойство затем используется в качестве базового цвета в функции color-contrast() и сравнивается с каждым элементом в списке цветов, чтобы найти самое контрастное значение.

  • Базовый цвет #222, список цветов #FFF = коэффициент контрастности 15,9
  • Базовый цвет #222, список цветов #000 = коэффициент контрастности 1,31

В результате color статьи будет установлен на белый, #FFF.

Но это можно сделать и дальше.

Мы можем эффективно связать функции color-contrast(), используя результат одной из них в качестве базового цвета другой. Давайте расширим пример article, определив цвет ::selection относительно его текста.

article {
  --article-bg: #222;
  --article-color: color-contrast(var(--article-bg) vs #FFF, #000);

  background: var(--article-bg);
  color: var(--article-color);

  ::selection {
    background: color-contrast(var(--article-color) vs #FFF, #000);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, когда цвет текста определен, будет определен и фон выделения.

Функция color-contrast() не ограничивается сравнением только HEX-кодов. Фактически, она может сравнивать несколько типов цветов одновременно. Предыдущий пример можно модифицировать, чтобы использовать разные типы цветов и получить те же результаты.

article {
  --article-bg: rgb(34, 34, 34);
  --article-color: color-contrast(var(--article-bg) vs hsl(0,0%,100%), black);

  background: var(--article-bg);
  color: var(--article-color);

  ::selection {
    background: color-contrast(var(--article-color) vs hsl(0,0%,100%), black);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

ОТ ПСЕВДОЭЛЕМЕНТОВ К ПСЕВДОКЛАССАМ

Динамическая установка цветов текста и ::selection может быть интригующей, но это не совсем то же самое, что погоня на высокой скорости с Бертом Рейнольдсом — по крайней мере, я так не думаю. Цвета текста и фона, как правило, довольно статичны. После визуализации они не часто меняются.

Итак, давайте переключим передачу и сосредоточимся 🥁 на интерактивных элементах и их псевдоклассах.

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

При навигации по странице с помощью клавиатуры, как правило, на пути встречаются самые разные остановки вкладок — ссылки внутри основного текста, кнопки и входы, может быть даже карта или связанное изображение. Хотя важно, чтобы каждый из этих элементов имел соответствующий индикатор фокуса, это редко бывает так просто, как создание единого, универсального стиля. В этом может помочь использование color-contrast().

:root {
  --body-bg: #131e25;
}

button {
  --btn-bg: #ffba76;
  --btn-color: color-contrast(var(--btn-bg) vs #fff, #000);

  background: var(--btn-bg);
  color: var(--btn-color);

  &:hover {
    --btn-bg: #b15900;
  }

  &:focus {
    --color-list: var(--btn-bg), var(--btn-color), #bbb, #555;
    box-shadow: 0 0 1px 3px
      color-contrast(var(--body-bg) vs var(--color-list));
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

В этом фрагменте многое демонстрирует потенциал color-contrast(), поэтому давайте пройдемся по нему.

Пользовательское свойство --btn-bg используется в качестве базового цвета при выборе значения --btn-color. Каждый раз, когда --btn-bg изменяется, --btn-color также будет переопределен. Это используется в состоянии :hover, позволяя отказаться от сопряжения цветов кнопок вручную и позволить color-contrast() делать это автоматически.

В стилях :focus этот подход может быть расширен за счет использования пользовательского свойства --body-bg в качестве базового цвета. Он сравнивается с текущими стилями кнопок. Это дает возможность использовать контекстно-зависимые стили фокусировки. Если стили фокусировки по умолчанию слишком контрастны, учитывая фоновое расположение элемента, можно использовать цвет, соответствующий этому элементу. Конечно, список цветов может содержать и безопасные запасные варианты на всякий случай.

Требования к совместимым индикаторам фокуса выходят за рамки этой статьи, но в презентации Стефани Эклз «Современные CSS-модернизации для улучшения доступности» они рассмотрены очень подробно и с наглядными примерами.

ОПРЕДЕЛИТЕ ЦЕЛЕВОЙ КОЭФФИЦИЕНТ КОНТРАСТНОСТИ

Ранее я, возможно, несколько легкомысленно относился к необязательному третьему параметру для color-contrast(). На самом деле именно здесь эта функция демонстрирует свой потенциал.

Необязательный третий параметр для color-contrast() определяет целевой коэффициент контрастности. Параметр принимает либо ключевое слово — AA, AA-large, AAA и AAA-large — либо число. Если задан целевой контраст, выбирается первый цвет из списка цветов, который соответствует или превосходит его.

Когда задан целевой контраст, color-contrast() вернет первое значение из списка цветов, которое соответствует цели. Однако, когда ни одно значение из списка цветов не соответствует целевому контрасту, происходит волшебство.

h1 {
  color: color-contrast(#000 vs #111, #222 to AA);
}
Вход в полноэкранный режим Выход из полноэкранного режима

Если посмотреть на базовый черный цвет и список цветов из двух темных оттенков серого, то не найдется ни одного значения, которое бы соответствовало целевому контрасту AA (4.5). Что же происходит?

Если в списке цветов нет значения, которое соответствует целевому контрасту, CSS заполнит пробелы одним из них — либо черным, либо белым.

Именно здесь color-contrast() может действительно расширить возможности систем дизайна для обеспечения определенного уровня доступности.

Давайте разберем это подробнее.

.dark-mode {
  --bg: #000;
  --color-list: #111, #222;
}

.dark-mode {
  background: var(--bg);
  color: color-contrast(var(--bg) vs var(--color-list));

  &.with-target {
    color: color-contrast(var(--bg) vs var(--color-list) to AA);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Волшебство здесь происходит, когда сравниваются два объявления color.

Базовый класс .dark-mode не использует целевой контраст. Это приводит к тому, что color определяется как #222, самое контрастное значение из списка цветов относительно его базового черного цвета. Излишне говорить, что коэффициент контрастности 1.35 может быть самым высоким, но он далек от доступного.

Сравните это с тем, когда классы .dark-mode и .with-target объединены, и указан целевой контраст. Несмотря на использование одного и того же базового цвета и списка цветов, результат значительно отличается. Если ни одно значение в списке цветов не соответствует целевому контрасту AA (4.5), функция выбирает значение, которое соответствует. В данном случае это белый цвет.

Именно здесь потенциал color-contrast() проявляется наиболее ярко.

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

Существует логический разрыв между кодом и результатом. Код не сообщает, что результатом будет белый цвет. И, конечно, этот контроль со стороны продукта приводит к неопределенности при реализации. Если человек использует систему дизайна и передает определенные цвета в своей теме, почему вместо них используются черный и белый?

Первая проблема может быть устранена более глубоким пониманием функции color-contrast(), а вторая — четкой и понятной документацией. Однако в обоих случаях это перекладывает бремя ожиданий на сторону реализации, что не идеально.

В некоторых случаях явный контроль оправдывает затраты. Однако у color-contrast() есть и другие недостатки, которые необходимо учитывать во всех случаях.

Не все то золото, что блестит

Как и у любой экспериментальной или новой функции, у color-contrast() есть неизбежные недостатки, которые необходимо учитывать.

ЦВЕТ И ВИЗУАЛЬНЫЙ КОНТРАСТ — ЭТО РАЗНЫЕ ВЕЩИ

При использовании color-contrast() для определения цвета текста на основе его фона, функция сравнивает именно это — цвета. Что color-contrast() не принимает во внимание, так это другие стили, которые могут повлиять на визуальный контраст, такие как размер, вес и непрозрачность шрифта.

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

Чтобы узнать больше о доступной типографике, я настоятельно рекомендую выступление Кэри Фишер «Основы доступной типографики».

ПОЛЬЗОВАТЕЛЬСКИЕ СВОЙСТВА И ОБРАТНЫЕ ЗНАЧЕНИЯ

Поскольку пользовательские свойства CSS поддерживают обратные значения, когда свойство не определено, мне показалось хорошим решением использовать color-contrast() в качестве прогрессивного улучшения.

--article-color: color-contrast(#000 vs #333, #FFF);
color: var(--article-color, var(--fallback-color));
Вход в полноэкранный режим Выход из полноэкранного режима

Если color-contrast() не поддерживается, свойство --article-color не будет определено, и поэтому будет использоваться --fallback-color. К сожалению, это работает не так.

Интересная вещь происходит в неподдерживаемых браузерах — пользовательское свойство будет определено в самой функции. Вот пример этого из Chrome DevTools:

Поскольку свойство --article-color технически определено, откат не сработает.

Однако это не означает, что color-contrast() нельзя использовать постепенно. Ее можно использовать в паре с функцией @supports(), но будьте осторожны, если решите это сделать. Как бы интересно это ни было, с такой ограниченной поддержкой и потенциальными изменениями синтаксиса и/или функциональности лучше воздержаться от того, чтобы рассыпать этот маленький драгоценный камень по всей кодовой базе.

@supports (color: color-contrast(#000 vs #fff, #eee)) {
  --article-color: color-contrast(var(--article-color) vs #fff, #000);
}
Вход в полноэкранный режим Выход из полноэкранного режима

САМЫЙ ВЫСОКИЙ КОНТРАСТ НЕ ОЗНАЧАЕТ ДОСТУПНЫЙ КОНТРАСТ

Несмотря на то, что color-contrast() может управлять цветами и темами, все же существуют ограничения. Когда функция сравнивает базовый цвет со списком и не указан целевой контраст, она выбирает самое контрастное значение. Если два цвета предлагают наибольший коэффициент контрастности, это не значит, что он является доступным.

h1 {
  background: #000;
  color: color-contrast(#000 vs #111, #222);
}
Вход в полноэкранный режим Выход из полноэкранного режима

В этом примере цвет фона черный. #000 сравнивается с двумя оттенками темно-серого. Хотя #222 был выбран за «наибольший» коэффициент контрастности, его сочетание с черным было бы не очень удачным.

ОТСУТСТВИЕ ПОДДЕРЖКИ ГРАДИЕНТОВ 

Оглядываясь назад, можно сказать, что попытка использовать градиенты с помощью color-contrast() была слишком амбициозной. Тем не менее, после некоторого тестирования выяснилось, что градиенты не поддерживаются. Что, как только я подумал об этом, имеет смысл.

Если градиент переходит от черного к белому, то каким будет базовый цвет? И разве он не должен быть относительно положения содержимого? Не похоже, что функция может интерпретировать пользовательский интерфейс. Однако Мишель Баркер экспериментировал с использованием CSS color-mix() и color-contrast() вместе, чтобы поддержать именно этот случай использования.

Это не ты, color-contrast(), это я. Ну, вообще-то это градиенты, но вы понимаете, о чем я.

Подведение итогов

Это было много кода и демонстраций, поэтому давайте сделаем шаг назад и рассмотрим color-contrast().

Эта функция сравнивает базовый цвет со списком цветов, затем выбирает наиболее контрастное значение. Кроме того, она может сравнить эти значения с целевым коэффициентом контрастности и либо выбрать первый цвет, который соответствует этому порогу, либо использовать динамический цвет, который соответствует. Соедините это с прогрессивным улучшением, и мы получим функцию, которая может значительно улучшить доступность веб-сайтов.

Я считаю, что для color-contrast() есть еще много неисследованных областей и вариантов использования, поэтому я хочу закончить эту статью некоторыми дополнительными мыслями и/или вопросами.

Как вы видите использование этой возможности при работе с различными цветовыми режимами, такими как светлый, темный и высококонтрастный? Может ли система дизайна на основе React раскрывать необязательный параметр targetContrast для своего ThemeProvider, чтобы обеспечить доступность, если тема не соответствует требованиям? Может ли функция вместо этого возвращать наименьшее значение контраста? Если есть два базовых цвета, можно ли использовать функцию для поиска наилучшего значения контраста между ними?

Что вы думаете?

РЕСУРСЫ 

  • «Новый CSS с учетом доступности», Рейчел Эндрю
  • «Первое знакомство с функцией color-contrast()», Крис Койер
  • Color-Contrast() на MDN
  • Статистика поддержки на caniuse.com
  • Color-Contrast() на W3 Color Module Level 5

Оцените статью
Procodings.ru
Добавить комментарий