gendesign/tradein-mvp/frontend/scripts/og/logo-mera.html
bot-backend a82382f47a
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m5s
feat(mera-b2c): микроразметка schema.org и картинки превью для публичного сайта
Сайт открыт для индексации 10.09.2026, но поисковику он до сих пор
представлялся девятью страницами без единого структурированного факта о
том, кто их публикует и что продаёт, а ссылка на него в мессенджере
разворачивалась голым текстом без картинки.

МИКРОРАЗМЕТКА. Конструкторы узлов вынесены в `_lib/schema.ts` — чистый
модуль без React, по образцу соседнего `_lib/analytics.ts`. Все значения
берутся из `content.ts`, ни одной строки не продублировано:

- `Organization` + `WebSite` одним `@graph` в layout поддерева, то есть на
  всех девяти страницах сразу. Остальные узлы ссылаются на организацию
  через `@id`, а не повторяют реквизиты у себя.
- `Service` с `Offer` на лэндинге.
- `FAQPage` в блоке возражений.
- `BreadcrumbList` на статьях, статье, документах и странице для бизнеса —
  ровно по тем крошкам, что видны на экране.
- Существующий `Article` переведён на ссылку `publisher: { "@id": ... }`.

РАЗМЕТКА НЕ ОБЕЩАЕТ ТОГО, ЧЕГО НЕТ. Два места, где это стоило внимания:

- `Offer.availability` вычисляется из `PUBLIC_ESTIMATE_ENABLED`. Флаг сейчас
  `false` (платёжного контура в коде нет), поэтому в разметку уходит
  `PreOrder`, а не `InStock`: цена опубликована, купить нельзя, и врать об
  этом поисковику нечего. Включится приём оплаты — значение сменится само.
- `FAQPage` строится из того же массива `ITEMS`, который рисует `<details>`,
  а не из `FAQ` напрямую: в `ITEMS` часть вопросов заменена макетными
  формулировками, и разметка от сырого `FAQ` разошлась бы с видимым текстом
  молча. Выдуманных дат, рейтингов и отзывов не добавлено нигде.

КАРТИНКИ. Две штуки, и это не дубль: `og-mera.png` 1200×630 — превью ссылки,
`logo-mera.png` 512×512 — логотип организации в разметке, который поисковик
обрезает близко к квадрату и где баннер превратился бы в обрезок надписи.
Исходники обеих лежат рядом в `scripts/og/*.html` вместе с командой
перерисовки: картинка должна оставаться правимой, а не только
переоткрываемой в графическом редакторе.

Готовые PNG, а не `opengraph-image.tsx`: satori внутри `ImageResponse` рисует
только переданными ему байтами шрифта и кириллицу по умолчанию не покрывает,
плюс не видит ни CSS Modules, ни наших `--b2c-*`. Разбор — в шапке
`scripts/og/og-mera.html`.

`images` продублирован в `twitter`: Next не переносит их из `openGraph`, когда
метаданные заданы объектом, и карточка обещала бы крупное превью без картинки.

ПЕРИМЕТР. Обе картинки лежат в `public/` и раздаются Next'ом по
basePath-корню, куда rewrite `@meraPages` не достаёт, — отсюда два отдельных
`handle` по образцу robots.txt. Оба названы в `ROOT_HANDLES_ALLOWED`, иначе
двусторонний гейт периметра покраснел бы, и это правильно: корневой `handle`
— вторая дверь в тот же периметр.

Смоук проверяет не только 200, но и Content-Type: перепутанный rewrite отдаёт
200 с HTML, обходчик мессенджера молча его отбрасывает, и по логам приложения
этого не видно — запрос туда не доходит.

Проверено: tsc, eslint, 215 тестов mera-public, изоляция B2C-дерева, caddy
validate, bash -n смоука, прод-сборка с basePath и разбор отрендеренного
HTML — JSON-LD парсится, og:image и twitter:image на месте.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CiUFZ3rmTNpp3DRajUo8KQ
2026-09-10 19:06:35 +03:00

87 lines
2.6 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Логотип МЕРА для микроразметки</title>
<!--
ИСХОДНИК ЛОГОТИПА ДЛЯ МИКРОРАЗМЕТКИ (Organization.logo), 512x512.
Отсюда рендерится `public/logo-mera.png`. Нужен ОТДЕЛЬНО от og-mera.png:
превью ссылки — широкая карточка 1200x630, а логотип организации поисковики
показывают в карточке компании и обрезают близко к квадрату, поэтому баннер
на его месте выглядел бы обрезанной надписью.
КАК ПЕРЕРИСОВАТЬ (нужен установленный Chrome/Chromium):
chrome --headless --disable-gpu --hide-scrollbars \
--screenshot=public/logo-mera.png --window-size=512,512 \
scripts/og/logo-mera.html
Цвета — копия b2c-tokens.ts, ровно по тем же причинам, что расписаны в
шапке og-mera.html.
-->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@800&family=IBM+Plex+Mono:wght@500&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 512px; height: 512px; overflow: hidden; }
body {
background: #FFFFFF;
font-family: "Manrope", system-ui, sans-serif;
color: #121A1D;
-webkit-font-smoothing: antialiased;
display: grid;
place-items: center;
}
.mark {
display: flex;
flex-direction: column;
align-items: center;
gap: 30px;
}
/* Тот же знак, что в шапке сайта (HeaderV3): контур с точкой внутри. */
.icon {
width: 168px;
height: 168px;
border: 9px solid #16A9BC;
border-radius: 26px;
display: grid;
place-items: center;
}
.dot {
width: 56px;
height: 56px;
background: #16A9BC;
border-radius: 11px;
}
.name {
font-weight: 800;
font-size: 76px;
letter-spacing: 0.06em;
line-height: 1;
}
.sub {
margin-top: -18px;
font-family: "IBM Plex Mono", ui-monospace, monospace;
font-weight: 500;
font-size: 20px;
letter-spacing: 0.18em;
color: #5B686C;
}
</style>
</head>
<body>
<div class="mark">
<div class="icon"><div class="dot"></div></div>
<div class="name">МЕРА</div>
<div class="sub">ДЛЯ СЕБЯ</div>
</div>
</body>
</html>