По любви · Android × Figma · 10.09.2026

Записка, жалоба, обучение, видео, фильтры и подписка сошлись с макетом

Ещё десять экранов ленты — от шторки записки до пейволла — собраны из данных нод: переменных, стилей, компонентов, матриц обрезки, дуг и эффектов. Каждый кадр снят на устройстве в честном масштабе ×3 (1170×2532) и сведён с рендером Figma. Ниже — кадр рядом с рендером, четыре числа на кадр и разбор того, что оказалось не так. Главная находка партии: затемнение полноэкранного кружка видео размывает подложку — из-за этого шесть кадров полгода числились «непереводимым остатком рендерера». Это продолжение артефактов по кадрам S01–S10, S11–S20 и S21–S30.

72 / 73кадров с зелёной геометрией при пороге 0,3 %
0,005 %лучшая геометрия
0,270 %худшая среди закрытых
26расхождений найдено и закрыто
2новых экрана собрано с нуля

Смотровая

Кадр рядом с рендером

Тяните шторку, чтобы совместить макет и приложение. «Карта» показывает, где остаётся расхождение по геометрии: синим закрыты системный статус-бар, полоска home, клавиатура и диалоги разрешений iOS — их рисует Android, они в сравнении не участвуют (спека 6.4).

Рендер кадра из Figma Снимок экрана приложения

Числа

Четыре измерения на кадр

Полное — доля пикселей, отличающихся сильнее ΔE 6 вне масок. Без текста — то же с исключёнными текстовыми блоками. ±1 px — пиксель считается расхождением, только если не совпал ни с одним пикселем рендера в окне ±1: сдвиг растра на долю пикселя не есть ошибка вёрстки. Геометрия — два последних вместе, то есть чистая форма, положение и материал; по ней и выносится вердикт. Шкала показывает геометрию относительно порога 0,3 %.

ЭкранКадрСостояние Полное %Без текста % ±1 px %Геометрия % Шкала до порога 0,3 %

Разбор

Что оказалось не так

Каждая строка — расхождение из реестра docs/figma/fig/ledger.md: что показала нода, что делало приложение и сколько это стоило в числах.

Затемнение размывает подложку — формулой его не повторить

Полгода шесть кадров полноэкранного кружка видео числились «классом остатка»: считалось, что затемнение поверх карточки не выражается никакой заливкой. У ноды overlay и правда две заливки — #252525 при 0,55 и #9C9C9C режимом OVERLAY, — но кроме них стоит BACKGROUND_BLUR радиусом 100. Цвет пикселя зависит от соседей, поэтому любая поточечная подгонка упиралась в |Δ| ≈ 8,5. Теперь подложка размывается сама: σ подобрана по рендеру (40 при радиусе 100 — |Δ| 1,08 против 13,31 без размытия), а Modifier.blur на эмуляторе молчит, поэтому фотография уменьшается в восемь раз, размывается и растягивается обратно.

−34,7 п.п.кадр 6580:25953
34,74 % → 0,08 %

Вьюпорт VectorDrawable срезал обводку, стоящую по центру

Крест «дизлайка» рисовался с обрубленными углами. Обводка в Figma стоит по центру контура и выходит за рамку ноды на половину толщины (7 при обводке 14), а android:viewportWidth режет по себе. Починка не в вёрстке, а в приборе: tools/fig_pics.py считает габарит нарисованного — с половиной обводки и с учётом clip-path, — расширяет вьюпорт, сдвигает контуры группой и пишет поля в pics/index.json, откуда их берёт раскладка.

−2,0 п.п.кадр 6046:32735
2,19 % → 0,22 %

Краска картинки стоит у ноды кадра, а не у мастера

Сердце «лайка» в мастере собрано двумя заливками — Pale Beige и Terracotta при 46 %. На кадре «2.5» у той же картинки осталась одна заливка: форма та же, тон другой. Раскладка теперь красит картинку по краске самой ноды и кладёт по слою на каждую заливку, а не полагается на цвет, впечённый в ресурс.

краска из ноды6046:32735 · 5525:31765
слой на заливку

Габариты инстансов в дампе — от прошлой раскладки

У барабанов фильтров в ноде 116×228, а рендер рисует 151×140: скрытые строки авто-лейаут не считает, и числа в API остались от старой версии компонента. Замер рендера показал заливку с 12 до 163 и с 227 до 378 — пара делит 366 между двумя барабанами и чертой 32. Правило спеки: эталон формы — рендер, а не absoluteBoundingBox.

151×1406162:24722
вместо 116×228 из дампа

Размытый круг — не радиальный градиент «на глаз»

Фон карточки премиум-фильтров — три терракотовых круга с размытием слоя 120. Плоское приближение Φ((R−d)/σ) здесь не годится: радиус круга того же порядка, что σ, и двумерное размытие срезает вершину — в центре круга 60,5 альфа выходит 0,52, а не 1. Точное значение — Q-функция Маркума; она считается по 24 точкам, между ними идёт радиальная заливка, а σ подобрана по сетке из 110 замеров рендера.

−2,5 п.п.кадр 11088:17912
2,71 % → 0,24 %

Прозрачность стоит и у стопов, и у самой заливки

Подвал обучающего видео V3 выходил темнее рендера на четверть. У градиента альфа задана дважды: у каждого стопа (0,9 → 0,5 → 0,25 → 0) и opacity 0,8 у всей заливки. Множители перемножаются. Замер по строкам показал ровно эту разницу: 47,2 против 62,5 на y=700.

−4,6 п.п.кадр 10760:37510
4,72 % → 0,17 %

«По центру» у контейнера ещё не значит «по центру»

У summary двух кадров свайпа стоит counterAxisAlignItems CENTER, и признак «имя по центру» читался по нему. Но все ряды внутри шириной во всю карточку (326), и рендер их не двигает — центрировать нужно только там, где по центру стоит сам ряд name-box. Из-за подмены город уезжал на 127 dp, а имя на 60.

−0,46 п.п.кадр 5378:5729
0,65 % → 0,18 %

Две ловушки Compose, которые не видит метрика геометрии

Modifier.fillMaxWidth().width(374.dp) оставляет ширину кадра: fillMaxWidth задаёт точные ограничения, и width их уже не сужает — значение ячейки вставало по 390 вместо 374. А PlNodeText центрирует содержимое в блоке, если не задано выравнивание: «Т-БАНК» оказывался на 152 вместо 60. Вторую ошибку метрика геометрии не ловит вовсе — текстовые блоки из неё исключены, — её видно только в полном числе и в отдельной проверке габаритов текста.

+16 и +92 dp6162:24693 · 13353:32447
обе закрыты

Кружок видео читается по своей ноде, а не по состоянию экрана

Одно и то же место кадра меняет и заливку, и обводку: при загрузке без кадра видео кружок залит чёрным, у кружка 96 обводка белая 3, у полноэкранного 326 — нулевая, у ошибки — Beigeish Gray со значком 40 / video-play, у остановленного видео сверху лежит #000 при 0,35. Лоадер, кнопка звука и «play» — не части кружка, а отдельные ноды кадра со своими координатами.

4 кадра6580:25953 и соседние
из красных в зелёные

Без подписки фильтры не просто серые

У запертых премиальных ячеек меняется всё сразу: замок вместо значка, плашки залиты Brand/Powdery подписью Dark Beige и без обводки, барабаны — Powdery 50 %, ползунок и правая засечка Dark Powdery, закрашенной части дорожки нет вовсе. Это не «состояние disabled» компонента, а другой набор красок в каждой ноде.

−0,40 п.п.хвост 10696:34576
0,40 % → 0,007 %

Плашка скидки красится по своему предложению

«−20 %» над карточкой «3 месяца» терракотовая, когда выбрано это предложение, и чёрная, когда выбрано соседнее. Плашка стоит на 12 выше верхнего края коробки и принадлежит ей, а не строке.

−0,33 п.п.кадр 6303:30527
0,52 % → 0,19 %

Перечёркнутая цена — вектор, а не textDecoration

Старая цена «899 ₽» перечёркнута отдельной нодой Line 9 37×11,5 обводкой терракотой 1,33 — линия идёт наискось, снизу вверх. В стиле текста никакого зачёркивания нет, и подчёркивание средствами шрифта дало бы другую линию.

вектор6263:22434
37×11,5 · 1,33

Один список чипов — две раскраски

Чипы «что в подписке» на кадрах ленты залиты Powdery 50 % с подписью Dark Beige, а на кадре из секции «Доработки» — Brand/Terracotta 60 % с белыми значком и подписью. Список тот же, набор тот же — краски разные.

−10,5 п.п.кадр 10346:36298
10,52 % → 0,04 %

Имя варианта против его заливки

Снек ошибки сохранения фильтров называется вариантом positive, а залит Additional/Error Red. Цвет решается по заливке, а не по имени варианта: иначе красная полоса рисовалась бы чёрной.

−4,5 п.п.кадр 6245:33377
4,66 % → 0,19 %

Собрано

Два новых экрана

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

«Настройки поиска» — 21 кадр

Стопка ячеек с чертами, ползунок радиуса с овальным манипулятором 40×38, пара барабанов 151×140 с тремя строками, три состояния переключателя, премиальные ячейки с замками, карточка премиум-фильтров с размытыми кругами, сообщества V4 со значками 32 и длинные кадры V3 в 2948 и 2580 dp — они сняты по частям с прокруткой и сведены с теми же частями рендера.

0,011…0,236 %21 кадр
Главная · V3 · V4 · V5

«Подписка» — 9 своих кадров

Заголовок с подписью, два предложения 179×140 с обводкой по центру рамки, плашка скидки, чипы с переносом, условия с двумя кнопками и нижняя панель 390×170, прижатая к низу кадра поверх условий. Ещё два кадра экрана — шторка Apple Pay и страница оплаты в Safari — это чистый системный хром iOS: своей вёрстки они не имеют и в сравнение не идут.

0,005…0,270 %9 кадров
+2 системных экрана iOS

Мера

Чего не видит «геометрия»

Число, по которому мы закрываем кадр, считает форму и материал, но исключает текстовые блоки — иначе в него попадала бы разница отрисовки букв. Значит, ошибку в самом тексте оно не поймает. Владелец заметил на одном кадре, что заголовок «Что в подписке» стоит у левого края вместо центра, — и мы прогнали отдельную меру по всем 73 кадрам: она измеряет каждый текстовый блок на снимке против рендера и печатает смещение в долях dp. Из 29 подозрительных блоков после правок осталось 10, и 8 из них измеритель сам помечает как ненадёжные. Проверка добавлена в обязательный ритуал перед закрытием экрана.

Заголовок шторки набран не тем шрифтом

Все четыре шторки — жалоба, «Не пропусти симпатию», обновление и техработы — в макете подписаны Manrope 20, а рисовались TT Runs 22. Буквы крупнее, и «На что хочешь пожаловаться?» переносилось в две строки там, где в макете одна.

−43,33 dp4 шторки
сдвиг заголовка → 0,33 dp

Заголовок «Что в подписке» — по центру, а не у левого края

Блок заголовка 358 с выравниванием по центру: чернила в макете начинаются со 119,6, а у нас стояли на 16. Ошибка была на всех девяти кадрах подписки.

−102 dp9 кадров подписки
сдвиг → 0

В прокрученной анкете потерялся ряд круглых кнопок

Между карточкой и описанием в колонке стоит ряд из трёх кнопок 80. Сами кнопки экран рисует по координатам, а колонке нужна их высота — без неё всё описание уезжало на 112 dp вверх, и внизу не было ссылки «Пожаловаться».

−15 п.п.кадр 6023:31660
17,999 % → 2,958 %

Мелочи, которые видно только мерой

Подпись шторки записки была серой вместо чёрной; сдвиг строки переключателя на 4 dp оказался свойством самой строки, а не состояния «выключено»; длинный город «Петропавловск-Камчатский» рендер обрезает многоточием, а мы выпускали его под значки; у слова-плашки поверх видео потерялся трек в 1 dp; подпись подсказки рисовалась во всю ширину кадра вместо блока 280 и переносилась не там же; заголовок «В три раза больше записок» переносился на слово позже — у нашего статического TT Runs глифы уже, чем у вариативного в Figma.

6 правокшрифт · цвет · перенос
все проверены мерой

Вопросы

Что стоит посмотреть в макете

Здесь не ошибки вёрстки, а места, где макет спорит сам с собой или с собственным рендером. Мы делаем как в рендере и передаём пару дизайнеру.

ЧтоГдеКак сейчас
Сердце подсказки обучения5525:32092 в ноде видимое и лежит поверх затемнения, а рендер не рисует его ни на одном из четырёх кадров — в прямоугольнике 64×64 ноль светлых пикселей
Кадр «4 video exists»6618:19686 вся сводка в ноде скрыта и держит прежние данные («Москва», «30», «56 %»), а рендер рисует другие («2 км», «28», «87 %», чип «Книги»); низ фотографии тоже не совпадает с растром из файла — дамп и рендер из разных версий
Снек ошибки фильтров6245:33379 вариант называется «positive», заливка — Additional/Error Red
Чипы «что в подписке»10346:36366 на кадре «Доработки» терракота 60 % с белым текстом, на тех же кадрах ленты — Powdery 50 % с Dark Beige
Подпись «from rewind»6399:19310 «вовзращай» — опечатка в самом макете
Строка переключателя в запертой ячейке6823:16373 сдвинута на 4 относительно такой же строки в открытой ячейке

Границы

Что не закрыто

Форма зелёная — это половина работы. Вот честный остаток.

ЧтоПочемуЧто нужно
Кадр 6618:19686 — 5,265 % дамп страницы и рендер кадра из разных версий файла: скрытая сводка держит прежние данные, а низ фотографии не совпадает с растром из .fig — подбор сдвига не помогает перевыгрузить страницу из Figma либо принять кадр как есть — решение за владельцем
Два платёжных кадра шторка Apple Pay и страница оплаты в Safari — системный хром iOS на Android им отвечает другой платёжный лист; сравнивать нечего
Экраны в приложении фильтры и подписка собраны и снимаются фикстурой, но в навигацию не включены заменить прежние FiltersScreen и экран подписки, переснять кадры
Поведение экрановтрасса запросов против карт Flutter не снята файловый лог запросов в debug-сборке и инструмент разбора
Плотность чернил на части кадров форма закрыта, а текст по своей мере выходит за предел разобрать метрики шрифта на кадрах с дробным кеглем