Заголовок обложки — тот самый difference поверх фотографии
Редакторская вёрстка устроена так, что фотографию в герой-блок выбирает не тот, кто рисовал макет. Дизайнер положил белый логотип на тёмное небо — красиво. Через неделю в ту же карточку заезжает снимок заснеженного поля, и логотипа на нём попросту нет. Обычно спасаются полупрозрачной чёрной плашкой поверх картинки: приём честный, но фотография после него превращается в серый прямоугольник, а выбирали её ради чего-то другого.
Альтернатива — перестать трогать фон и научить текст подстраиваться под то, что под ним оказалось. Ровно для этого в CSS есть свойство mix-blend-mode. Задачу, под которую обычно пишут скрипт с разбором яркости изображения, оно закрывает одной строкой.
Решение в две строки и откуда оно взялось
Формулировку, с которой чаще всего и начинают знакомство с темой, дал Cody Lindsay Gordon: логотипу назначается mix-blend-mode: exclusion, дальше он подстраивается под яркость снимка сам.
.blend-me {
mix-blend-mode: exclusion;
}
К этому прилагается требование к разметке, о которое спотыкаются в первую очередь: слой, с которым идёт смешивание, обязан находиться ниже элемента и приходиться ему соседом, а не родителем.
За смешиванием стоит поканальная функция двух аргументов. Первый — цвет подложки, в спецификации он обозначен как Cb (backdrop). Второй — цвет накладываемого слоя, Cs (source). Значения нормализованы к отрезку от 0 до 1, а сама функция применяется к красному, зелёному и синему по отдельности. По такой схеме работают двенадцать режимов из шестнадцати; их называют разделяемыми (separable) — каналы друг на друга не влияют. У normal считать нечего (побеждает верхний цвет), формула soft-light кусочная и в ячейку не влезает, а оставшиеся десять сведены в таблицу.
Режим
Формула из спецификации
multiply
B = Cb × Cs
screen
B = 1 − (1 − Cb) × (1 − Cs)
darken
B = min(Cb, Cs)
lighten
B = max(Cb, Cs)
color-dodge
B = min(1, Cb / (1 − Cs))
color-burn
B = 1 − min(1, (1 − Cb) / Cs)
hard-light
multiply или screen в зависимости от того, темнее ли Cs половины
overlay
то же, что hard-light, но слои поменяны местами
difference
B = abs(Cb − Cs)
exclusion
B = Cb + Cs − 2 × Cb × Cs
Четвёрка hue, saturation, color и luminosity живёт по другим правилам — это неразделяемые режимы. Цвет в них раскладывается на тон, насыщенность и светлоту, после чего составляющие берутся из разных слоёв. У color, скажем, тон и насыщенность приходят сверху, а светлота снизу: на этом и держится раскраска чёрно-белых снимков.
Разговоры про формулы заменяет один снимок экрана. Белое слово #fff лежит поверх одной и той же картинки, отличается только значение mix-blend-mode:
Шестнадцать плиток, снятых в Chromium 141: цвет надписи и разметка везде совпадают
ℹ️ Справка
Пропавший текст в плитках multiply, darken и color-burn — не сбой. Белый для всех трёх является нейтральным элементом, формула схлопывается до B = Cb, то есть подложка остаётся нетронутой. С hue, saturation и color история иная: тона и насыщенности у белого источника нет, так что на выходе получается подложка, у которой отняли цвет, — на светлых участках уловить разницу почти невозможно. Так что при жалобе «режим не работает» подозрение должно падать не на браузер, а на нейтральный цвет, поданный на вход.
Слабое место — середина шкалы
Дальше начинается неприятное. Возьмём формулу difference и подставим подложку ровно посреди диапазона: abs(0.5 − 1) = 0.5. Полученное значение совпадает с фоном, надпись растворилась. С exclusion ничего не меняется: 0.5 + 1 − 2 × 0.5 × 1 = 0.5. Контраст обнуляется у обоих режимов именно на средне-серых участках — самой распространённой яркости у настоящих фотографий.
Шкала яркости 0—100 % под одной и той же надписью, снимок из Chromium 141
⛔ Так делать нельзя
Назначить основному тексту интерфейса mix-blend-mode: difference, поставив на этом точку в вопросе контраста. Норматив WCAG — 4.5:1 для обычного текста и 3:1 для крупного (от 24 px, либо от 18.66 px полужирным). Итог смешивания определяется картинкой, которую загрузит редактор, поэтому заранее выдержать эти значения нельзя в принципе. Взгляд на одну-единственную фотографию проверкой не считается.
Вывод получается практический: смешивание хорошо себя ведёт на крупной декоративной типографике, монограммах, цифрах, разделителях и состояниях наведения — там, где просевшая читаемость не рушит сценарий. Подписям, кнопкам и абзацам нужна подложка с предсказуемым поведением.
Контекст наложения: элемент смешивается не со всей страницей
«У меня всё то же самое, только не работает» — самая частая жалоба, и причина у неё почти всегда одна. Смешивание захватывает не всё, что нарисовано ниже, а исключительно содержимое ближайшего контекста наложения (stacking context). Появилась между текстом и картинкой обёртка, создающая собственный контекст, — и смешиваться стало не с чем.
Список свойств, порождающих контекст наложения, заметно длиннее, чем принято помнить: сюда попадают position: fixed и contain: paint, любой filter, любой transform, объявленный will-change, opacity со значением меньше единицы, а ещё потомок flex- или grid-контейнера, которому задан z-index не в значении auto. Порождает контекст и само свойство mix-blend-mode, прямо на том узле, где его объявили.
Три карточки с идентичной разметкой; расходится единственное свойство обёртки. Chromium 141
⚠️ Осторожно
Работает и обратная неприятность. Забыли завести контекст — и партнёром по смешиванию становится вообще всё, что нарисовано под элементом: соседние карточки, заливка секции, фон документа. Достаточно одной строки на общем предке:
.hero {
isolation: isolate;
}
Значение isolate принудительно заводит контекст наложения и запирает смешивание внутри блока. Поддержка во всех браузерах — с января 2020 года.
Отдельного упоминания заслуживает случай, который вообще ни на что не похож. Фоновая заливка, объявленная у корневого элемента либо у body, продвигается браузером на канву окна — а канва в расчёте не участвует. Смешивать оказывается не с чем: партнёром выступает прозрачность, и на экране не меняется ровным счётом ничего. В обложке к этому материалу ошибки нет — снимок отдан внутреннему блоку-сцене, ему же прописан isolation: isolate, и уже поверх этой сцены лежит заголовок с difference.
Соседнее свойство background-blend-mode
Названия похожи, области действия — нет. Там, где mix-blend-mode смешивает элемент с нарисованным под ним, background-blend-mode работает внутри одного элемента: между собой смешиваются несколько background-image и лежащий под ними background-color. Дополнительные обёртки и псевдоэлементы ради затемнения становятся не нужны.
Четыре карточки, один и тот же файл в свойстве background-image. Снято в Chromium 141
Тон и насыщенность отдаёт верхний слой, светлоту — нижний, и вся лента обложек приходит к единой стилистике. Слои в background-image перечисляются сверху вниз, поэтому градиент стоит первым, а фотография последней.
✅ Как правильно
Притемнить снимок под белую надпись, не заводя ни одного дополнительного узла:
Степень притемнения регулируется единственным значением background-color — без второго div, без ::after и без вмешательства в HTML.
Шпаргалка: режим под задачу
Задача
Режим
Что учесть
Крупный логотип или заголовок поверх произвольного фото
difference, exclusion
провал контраста на средне-серых участках
Затемнить картинку под белый текст
multiply с тёмным цветом
предсказуемо, но гасит саму фотографию
Осветлить картинку под тёмный текст
screen со светлым цветом
съедает детали в тенях
Дуотон, единый стиль всех обложек
color или luminosity
считается один раз, можно закешировать в CSS
Наложить текстуру шума или бумаги
multiply, soft-light
держите текстуру мелкой, её вес идёт в трафик
Свечение, вспышки, частицы
screen, plus-lighter
plus-lighter есть в Chromium 141, plus-darker — нет
Перекрасить чёрно-белую иконку
color
на цветном источнике результат непредсказуем
Подсветить область под курсором
difference на элементе-курсоре
свойство не анимируется, анимируйте позицию
❗ Главное
Анимации mix-blend-mode не поддаётся — формулировка MDN звучит как Animation type: Not animatable. Переключать значение внутри @keyframes смысла нет; анимируют соседей — то, что лежит под элементом и над ним: положение, размер, цвет.
Сборка для боевого проекта
Схема, которая выдерживает произвольный контент: предсказуемая подложка в основании, смешивание сверху как улучшение, проверка поддержки через @supports.
Инверсия достаётся логотипу автоматически, а текст, обязанный читаться, держится на градиентной подложке и тени — их контраст считается заранее и проверяется любым чекером. Готовый герой-блок стоит прогнать минимум на четырёх изображениях: очень тёмном, очень светлом, средне-сером и пёстром. Больше всего провалов ловит именно средне-серое.
Источники
MDN: mix-blend-mode — список значений, формальный синтаксис, заметка о контексте наложения
Доходило ли у вас до боевого проекта — или ограничивались градиентной подложкой? И попадались ли случаи, когда режим отрабатывал по-разному в Chromium и Safari: любопытно собрать список расхождений, пойманных руками.
Вы можете написать сейчас и зарегистрироваться позже.
Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.
Редакторская вёрстка устроена так, что фотографию в герой-блок выбирает не тот, кто рисовал макет. Дизайнер положил белый логотип на тёмное небо — красиво. Через неделю в ту же карточку заезжает снимок заснеженного поля, и логотипа на нём попросту нет. Обычно спасаются полупрозрачной чёрной плашкой поверх картинки: приём честный, но фотография после него превращается в серый прямоугольник, а выбирали её ради чего-то другого.
Альтернатива — перестать трогать фон и научить текст подстраиваться под то, что под ним оказалось. Ровно для этого в CSS есть свойство
mix-blend-mode. Задачу, под которую обычно пишут скрипт с разбором яркости изображения, оно закрывает одной строкой.Решение в две строки и откуда оно взялось
Формулировку, с которой чаще всего и начинают знакомство с темой, дал Cody Lindsay Gordon: логотипу назначается
mix-blend-mode: exclusion, дальше он подстраивается под яркость снимка сам.К этому прилагается требование к разметке, о которое спотыкаются в первую очередь: слой, с которым идёт смешивание, обязан находиться ниже элемента и приходиться ему соседом, а не родителем.
Что при этом считает браузер
За смешиванием стоит поканальная функция двух аргументов. Первый — цвет подложки, в спецификации он обозначен как Cb (backdrop). Второй — цвет накладываемого слоя, Cs (source). Значения нормализованы к отрезку от 0 до 1, а сама функция применяется к красному, зелёному и синему по отдельности. По такой схеме работают двенадцать режимов из шестнадцати; их называют разделяемыми (separable) — каналы друг на друга не влияют. У
normalсчитать нечего (побеждает верхний цвет), формулаsoft-lightкусочная и в ячейку не влезает, а оставшиеся десять сведены в таблицу.multiplyB = Cb × CsscreenB = 1 − (1 − Cb) × (1 − Cs)darkenB = min(Cb, Cs)lightenB = max(Cb, Cs)color-dodgeB = min(1, Cb / (1 − Cs))color-burnB = 1 − min(1, (1 − Cb) / Cs)hard-lightmultiplyилиscreenв зависимости от того, темнее ли Cs половиныoverlayhard-light, но слои поменяны местамиdifferenceB = abs(Cb − Cs)exclusionB = Cb + Cs − 2 × Cb × CsЧетвёрка
hue,saturation,colorиluminosityживёт по другим правилам — это неразделяемые режимы. Цвет в них раскладывается на тон, насыщенность и светлоту, после чего составляющие берутся из разных слоёв. Уcolor, скажем, тон и насыщенность приходят сверху, а светлота снизу: на этом и держится раскраска чёрно-белых снимков.Разговоры про формулы заменяет один снимок экрана. Белое слово
#fffлежит поверх одной и той же картинки, отличается только значениеmix-blend-mode:ℹ️ Справка
Пропавший текст в плитках
multiply,darkenиcolor-burn— не сбой. Белый для всех трёх является нейтральным элементом, формула схлопывается доB = Cb, то есть подложка остаётся нетронутой. Сhue,saturationиcolorистория иная: тона и насыщенности у белого источника нет, так что на выходе получается подложка, у которой отняли цвет, — на светлых участках уловить разницу почти невозможно. Так что при жалобе «режим не работает» подозрение должно падать не на браузер, а на нейтральный цвет, поданный на вход.Слабое место — середина шкалы
Дальше начинается неприятное. Возьмём формулу
differenceи подставим подложку ровно посреди диапазона:abs(0.5 − 1) = 0.5. Полученное значение совпадает с фоном, надпись растворилась. Сexclusionничего не меняется:0.5 + 1 − 2 × 0.5 × 1 = 0.5. Контраст обнуляется у обоих режимов именно на средне-серых участках — самой распространённой яркости у настоящих фотографий.⛔ Так делать нельзя
Назначить основному тексту интерфейса
mix-blend-mode: difference, поставив на этом точку в вопросе контраста. Норматив WCAG — 4.5:1 для обычного текста и 3:1 для крупного (от 24 px, либо от 18.66 px полужирным). Итог смешивания определяется картинкой, которую загрузит редактор, поэтому заранее выдержать эти значения нельзя в принципе. Взгляд на одну-единственную фотографию проверкой не считается.Вывод получается практический: смешивание хорошо себя ведёт на крупной декоративной типографике, монограммах, цифрах, разделителях и состояниях наведения — там, где просевшая читаемость не рушит сценарий. Подписям, кнопкам и абзацам нужна подложка с предсказуемым поведением.
Контекст наложения: элемент смешивается не со всей страницей
«У меня всё то же самое, только не работает» — самая частая жалоба, и причина у неё почти всегда одна. Смешивание захватывает не всё, что нарисовано ниже, а исключительно содержимое ближайшего контекста наложения (stacking context). Появилась между текстом и картинкой обёртка, создающая собственный контекст, — и смешиваться стало не с чем.
Список свойств, порождающих контекст наложения, заметно длиннее, чем принято помнить: сюда попадают
position: fixedиcontain: paint, любойfilter, любойtransform, объявленныйwill-change,opacityсо значением меньше единицы, а ещё потомок flex- или grid-контейнера, которому заданz-indexне в значенииauto. Порождает контекст и само свойствоmix-blend-mode, прямо на том узле, где его объявили.⚠️ Осторожно
Работает и обратная неприятность. Забыли завести контекст — и партнёром по смешиванию становится вообще всё, что нарисовано под элементом: соседние карточки, заливка секции, фон документа. Достаточно одной строки на общем предке:
Значение
isolateпринудительно заводит контекст наложения и запирает смешивание внутри блока. Поддержка во всех браузерах — с января 2020 года.Отдельного упоминания заслуживает случай, который вообще ни на что не похож. Фоновая заливка, объявленная у корневого элемента либо у
body, продвигается браузером на канву окна — а канва в расчёте не участвует. Смешивать оказывается не с чем: партнёром выступает прозрачность, и на экране не меняется ровным счётом ничего. В обложке к этому материалу ошибки нет — снимок отдан внутреннему блоку-сцене, ему же прописанisolation: isolate, и уже поверх этой сцены лежит заголовок сdifference.Соседнее свойство background-blend-mode
Названия похожи, области действия — нет. Там, где
mix-blend-modeсмешивает элемент с нарисованным под ним,background-blend-modeработает внутри одного элемента: между собой смешиваются несколькоbackground-imageи лежащий под нимиbackground-color. Дополнительные обёртки и псевдоэлементы ради затемнения становятся не нужны.Дуотон умещается в четыре строки:
Тон и насыщенность отдаёт верхний слой, светлоту — нижний, и вся лента обложек приходит к единой стилистике. Слои в
background-imageперечисляются сверху вниз, поэтому градиент стоит первым, а фотография последней.✅ Как правильно
Притемнить снимок под белую надпись, не заводя ни одного дополнительного узла:
Степень притемнения регулируется единственным значением
background-color— без второгоdiv, без::afterи без вмешательства в HTML.Шпаргалка: режим под задачу
difference,exclusionmultiplyс тёмным цветомscreenсо светлым цветомcolorилиluminositymultiply,soft-lightscreen,plus-lighterplus-lighterесть в Chromium 141,plus-darker— нетcolordifferenceна элементе-курсоре❗ Главное
Анимации
mix-blend-modeне поддаётся — формулировка MDN звучит как Animation type: Not animatable. Переключать значение внутри@keyframesсмысла нет; анимируют соседей — то, что лежит под элементом и над ним: положение, размер, цвет.Сборка для боевого проекта
Схема, которая выдерживает произвольный контент: предсказуемая подложка в основании, смешивание сверху как улучшение, проверка поддержки через
@supports.Инверсия достаётся логотипу автоматически, а текст, обязанный читаться, держится на градиентной подложке и тени — их контраст считается заранее и проверяется любым чекером. Готовый герой-блок стоит прогнать минимум на четырёх изображениях: очень тёмном, очень светлом, средне-сером и пёстром. Больше всего провалов ловит именно средне-серое.
Источники
exclusionи требование к порядку элементов💬 Вопрос к сообществу
Доходило ли у вас до боевого проекта — или ограничивались градиентной подложкой? И попадались ли случаи, когда режим отрабатывал по-разному в Chromium и Safari: любопытно собрать список расхождений, пойманных руками.
TOP HOSTERS: KAMATERA (30 дней бесплатного теста!)
Универсальный хостер №1 - 4VPS.su (2Гб\с сервера) - 10% скидка на первый заказ или 15% бонус на первое пополнение