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
Сайт открыт для индексации 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
87 lines
2.6 KiB
HTML
87 lines
2.6 KiB
HTML
<!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>
|