Обеспечьте читаемость текста с помощью динамических цветов фона

Веб-функции: contrast-color()

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

Определить оптимальный цвет текста

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

ОБЯЗАТЕЛЬНО. Задайте свойство color с помощью функции contrast-color(), передав цвет фона (или его дополнительный параметр) в качестве аргумента.

.badge {
  /* Define the background color as a custom property */
  --badge-bg: #e02424;
  background-color: var(--badge-bg);
  /* MANDATORY: Dynamic contrasting color */
  color: contrast-color(var(--badge-bg));
}

НЕПРАВИЛЬНО: использовать contrast-color() для background-color. Он специально разработан для цветов переднего плана (таких как текст или границы), чтобы контрастировать с заданным цветом фона.

Интеграция с темами

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

РЕКОМЕНДУЕТСЯ. Ссылаться на общие фоновые переменные внутри contrast-color(), чтобы гарантировать синхронизацию в приложении.

.theme-card {
  background-color: var(--theme-surface-color);
  color: contrast-color(var(--theme-surface-color));
}

.theme-container[data-theme="light"] {
  --theme-surface-color: #f4f4f4;
}
.theme-container[data-theme="dark"] {
  --theme-surface-color: #1a1a1a;
}
.theme-container[data-theme="custom"] {
  --theme-surface-color: var(--user-custom-theme);
}

НЕ ЗАДАВАЙТЕ в коде отдельный цвет текста для каждого состояния взаимодействия, если фон динамический. В противном случае возникнет несоответствие, которое и призван предотвратить атрибут contrast-color().

Резервные стратегии

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

Стратегия Контент Примечания
Синтаксис относительных цветов Автоматический расчет контрастности высокого качества только для CSS. Резервный вариант на основе CSS самого высокого качества.
Тень текста Улучшает читаемость на любом фоне. Может выглядеть "грязным" или "светящимся", подходит не для всех дизайнов.
Контур текста Сохранение насыщенности шрифта при обеспечении контрастности краев. Используйте paint-order, чтобы избежать утончения букв, если это возможно.
Полупрозрачный оверлей Обеспечьте минимальную контрастность фона за текстом. Изменяет цвет фона под текстом. Требуется отдельный текстовый элемент.
Фильтры SVG Контрастность, которая меняется в зависимости от фона. Требуется отдельный текстовый элемент; реализация не очень удобна.

Для браузеров, которые поддерживают RCS, это самый надежный автоматический резервный вариант для динамических цветов.

@supports (color: oklch(from red l c h)) {
  .badge {
    /* Highest threshold that passes WCAG. Higher values may be more legible. */
    --threshold: 0.623;
    --l: max(0, sign(var(--threshold) - l));
    color: oklch(from var(--badge-bg) var(--l) 0 h);
  }
}

@supports (color: contrast-color(red)) {
  .badge {
    color: contrast-color(var(--badge-bg));
  }
}

Резервные ССЦ

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

Вариант 1. Обводка текста

.badge--stroke {
  -webkit-text-stroke: 4px black;
}

Вариант 2. Обводка текста с помощью paint-order для сохранения формы букв

.badge--stroke {
  -webkit-text-stroke: 4px black;
  paint-order: stroke fill;
}

Вариант 3. Полупрозрачный фон

.badge--overlay {
  position: relative;
  color: #fff;
  overflow: hidden;
}
.badge--overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgb(0 0 0 / 0.4);
  z-index: 0;
}
.badge--overlay span {
  position: relative;
  z-index: 1;
}

Вариант 4. Тень текста

.badge--shadow {
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
}

Вариант 5. Фильтры SVG

  1. Загрузить это определение фильтра SVG. Если вам нужно обеспечить поддержку версий Safari, затронутых этой ошибкой WebKit, которая не позволяет использовать внешние URL и URL данных для фильтров, то код необходимо добавить непосредственно в HTML-документ. В противном случае его следует загрузить из внешнего файла, например SVG, или встроить в CSS.

    <!-- Technique from https://miunau.com/posts/dynamic-text-contrast-in-css/ -->
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0" style="display: none;">
      <defs>
        <filter id="contrast-filter" color-interpolation-filters="sRGB">
          <feColorMatrix type="matrix"
            values="0.2126 0.7152 0.0722 0 0
              0.2126 0.7152 0.0722 0 0
              0.2126 0.7152 0.0722 0 0
              0 0 0 1 0"/>
          <feMorphology operator="dilate" radius="2"/>
          <feComponentTransfer>
            <feFuncR type="linear" slope="-255" intercept="128"/>
            <feFuncG type="linear" slope="-255" intercept="128"/>
            <feFuncB type="linear" slope="-255" intercept="128"/>
          </feComponentTransfer>
          <feComposite operator="in" in2="SourceGraphic"/>
        </filter>
      </defs>
    </svg>
    
  2. Заключите текст в тег <span>.

    <div class="badge--svg">
      <span>Badge content</span>
    </div>
    
  3. Примените фильтр к <span>.

.badge--svg span{
  color: var(--badge-bg);
  filter: url(#contrast-filter);
}