Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions krdv-guidelines-prototype/audits/2026-10-14-global.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# ERDCBB: глобальный технический и визуальный аудит

Дата: 14 октября 2026 года. База: main / 4dcbba6. Ветка исправлений: audit/full-site-2026-10-14. Утверждённая айдентика и тексты правил не менялись.

## Методика

- Реально открыты и визуально просмотрены все 14 публичных страниц; проверены начальные экраны и первые содержательные блоки.
- 56 сквозных проверок браузером Chromium: 14 страниц × 320, 390, 768, 1366 px. Для каждой проскроллены все секции, проверены все видимые изображения, горизонтальное переполнение, ошибки JavaScript, ошибочные локальные HTTP-ответы, наличие заголовков и корректность ссылок.
- 28 аудитов axe-core 4.10.3: 14 разделов × desktop/mobile. Автоматических нарушений набора WCAG 2 A/AA и WCAG 2.1 A/AA не найдено. Это не сертификат полного соответствия WCAG.
- Сняты изображения экранов каждого раздела и первичных содержательных блоков на десктопе и мобильном.
- Дополнительно проверены переходы клавиатурой, поведение меню, данные ассетов и ссылки на внешние архивы.

## Результаты по разделам

| № | Раздел | Блоков | Проверка |
|---|---|---:|---|
| 00 | Обзор | 3 | Hero, каталог и материалы открываются; локальных ошибок нет |
| 01 | Позиционирование | 2 | Бренд-обещание и визуал читаемы; переполнения нет |
| 02 | Логотип | 6 | Версии, использование, охранное поле, фоны и запреты отображаются; исправлено управление фокусом по якорю |
| 03 | Дочерние общества | 4 | Русские/английские варианты и превью загружаются |
| 04 | Режимы | 3 | Знаки, палитры и программы доступны |
| 05 | Цвет | 2 | Палитры и копирование работают; данные цветов оставлены неизменными |
| 06 | Типографика | 2 | Шрифтовая система и интерактивный предпросмотр работают |
| 07 | Оформление текста | 5 | Правила и типограф без выявленных ошибок загрузки |
| 08 | Корпоративные материалы | 4 | Визитки, QR-карточки и бланки отображаются |
| 09 | Рекламные материалы | 5 | Исправлена тяжёлая загрузка 10 PNG-лифлетов в миниатюры; оригиналы сохранены |
| 10 | Мерч | 1 | 12 примеров, 12 полноразмерных оригиналов; ссылка Miro по ранее утверждённой правке отсутствует |
| 11 | Сделано в ТОР СПВ | 3 | Оба семейства знаков, включая RU/EN ДВ и Арктики, отображаются |
| 12 | Фотостиль | 5 | Видео, изображения людей, природы и мероприятий проверены |
| 13 | Приложение | 2 | Соцсети и контакты присутствуют; данные не изменялись |

## Доказуемые ошибки и что исправлено

1. **Потеря фокуса клавиатуры после перехода из мобильного меню.** Было: после перехода в подраздел активным элементом становился BODY. Стало: фокус программно переносится на заголовок выбранного блока после закрытия меню.
2. **Заголовки при мобильной прокрутке перекрывались фиксированной кнопкой меню.** Добавлены scroll-margin-top и scroll-padding-top (72 px) для секций и контейнера прокрутки, без изменения композиции Hero и размеров типографики.
3. **Тяжёлые изображения в маленьких карточках лифлетов.** Десять исходных PNG: 6 845 065 байт. Десять дополнительных WebP-превью: 463 246 байт. Уменьшение загрузки миниатюр примерно на 93.23%; ссылка из карточки открывает неизменённый исходный PNG.
4. **Старые неиспользуемые медиа.** Удалены 20 архивных растровых файлов общим размером 3 312 231 байт. Отсутствие обращений в React/CSS проверено. Динамическая серия изображений правил логотипа сохранена.
5. **Несоответствие документации текущему сайту.** Карта разделов дополнена знаком Сделано на Дальнем Востоке и в Арктике, в исходном техническом QA уточнены статус публикации и действующее количество тестов.

## Внешние источники и остающиеся ограничения

- Проверка сетевых ссылок Яндекс Диска из браузера и CLI давала разные ответы: часть запросов HTTP 200, некоторые HTTP 500. Автоматические HTTP 500 нельзя надёжно интерпретировать как недоступность папки обычному пользователю. Ссылки не удалялись.
- Ссылка MAX ответила 403 автоматическому HTTP-клиенту; её реальная открываемость в приложении требует ручной проверки.
- Для коммерческих TT Norms Pro и TT Tsars необходима документальная проверка лицензии на публичные ассеты.
- Нет финального утверждённого PDF брендбука для пословной сверки материалов, без него нельзя объявить редакционную приёмку завершённой.
- Автоматическая проверка доступности не заменяет тестирование реальными ассистивными технологиями, а Chromium на эмуляциях не заменяет настоящие iOS/Android устройства.

## Релизный барьер

- ESLint, production Vite build и модульные integrity-тесты: npm run check.
- Cross-browser Playwright: Chromium / мобильный Chromium / Firefox / WebKit / мобильный WebKit.
- Добавлены проверки полноты lazy-загрузки ассетов всех 14 разделов в Chromium, фокуса мобильных подразделов и безопасного отступа меню.
- GitHub Actions Quality checks должен успешно завершиться до объединения ветки; Pages workflow повторяет все проверки до деплоя.
2 changes: 1 addition & 1 deletion krdv-guidelines-prototype/brandbook-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
| 08 | Корпоративные материалы | `#/corporate` | Визитки, Визитки с QR-кодом, Визитки ДО, Бланки ДО |
| 09 | Рекламные материалы | `#/advertising` | Лифлеты, Роллап, Навигация кабинетов, Режимник, Наградные документы |
| 10 | Мерч | `#/merch` | Примеры брендированной продукции |
| 11 | Сделано в ТОР СПВ | `#/made` | Знак, Применение |
| 11 | Сделано в ТОР СПВ | `#/made` | Знак, Сделано на Дальнем Востоке и в Арктике, Применение |
| 12 | Фотостиль | `#/photostyle` | Принципы съемки, Единый прогресс, Выступления, Природа, Люди |
| 13 | Приложение | `#/appendix` | Социальные сети, Контакты |

Expand Down
6 changes: 3 additions & 3 deletions krdv-guidelines-prototype/design-qa.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ERDCBB — технический аудит 12 октября 2026

Исходный коммит: `9bdf906b6003e315aca718132403bcfa6b5e6f5d` от 24.08.2026. Этот документ описывает отдельную рабочую ветку и **не означает**, что изменения уже развернуты на GitHub Pages.
Исходный коммит первоначального аудита: 9bdf906b6003e315aca718132403bcfa6b5e6f5d (24.08.2026). Этот документ сохраняет историю первого этапа; впоследствии изменения были опубликованы. Актуальные результаты на 14.10.2026 см. в audits/2026-10-14-global.md.

## Исправленные проблемы

Expand Down Expand Up @@ -29,9 +29,9 @@
## Проверки

- `npm run build` — успешная сборка.
- `npm test` — 11 тестов целостности данных, маршрутов, графики и редакционных правил проходят.
- `npm test` — 17 тестов целостности данных, маршрутов, графики и редакционных правил проходят.
- `npm run test:e2e` — последний локальный прогон Chromium desktop/mobile завершился с кодом 0; проверяются 14 страниц, навигация, доступность и редакционные правки. Это не заменяет ручную визуальную приёмку.
- Настроен CI Quality checks для pull request: сборка, 12 модульных проверок, Playwright Chromium/Firefox/WebKit с desktop/mobile. GitHub Pages workflow повторяет проверки перед загрузкой релиза; неуспешная проверка блокирует публикацию.
- Настроен CI Quality checks для pull request: сборка, полный модульный набор (17 тестов на 14.10.2026), Playwright Chromium/Firefox/WebKit с desktop/mobile. GitHub Pages workflow повторяет проверки перед загрузкой релиза; неуспешная проверка блокирует публикацию.
- Проверка точного совпадения с утверждённым исходным брендбуком требует финального PDF, который отсутствует в репозитории.

## Открытые вопросы с внешними зависимостями
Expand Down
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
3 changes: 3 additions & 0 deletions krdv-guidelines-prototype/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ function App() {
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
target?.scrollIntoView({ block: "start", behavior: reducedMotion ? "auto" : "smooth" });
setMenuOpen(false);
window.requestAnimationFrame(() => {
target?.querySelector("h2")?.focus({ preventScroll: true });
});
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ function AssetImageGrid({ items, mode = "grid" }) {
rel="noopener noreferrer"
aria-label={t("Открыть обложку в полном размере: " + item.title)}
>
<img src={rasterAsset(item.file)} alt={t(item.title)} loading="lazy" decoding="async" />
<img src={rasterAsset(item.preview || item.file)} alt={t(item.title)} loading="lazy" decoding="async" />
</a>
) : (
<img src={rasterAsset(item.file)} alt={t(item.title)} loading="lazy" decoding="async" />
Expand Down
2 changes: 1 addition & 1 deletion krdv-guidelines-prototype/src/components/GuidelinePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function ContentSection({ section, navigate, activePageId }) {
return (
<section className="content-section" id={section.id}>
<div className="section-title">
<h2>{t(section.title)}</h2>
<h2 tabIndex={-1}>{t(section.title)}</h2>
</div>
<div className="section-body">
{section.lead ? <p className="lead-copy">{t(section.lead)}</p> : null}
Expand Down
20 changes: 10 additions & 10 deletions krdv-guidelines-prototype/src/data/pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,16 +384,16 @@ const pagesData = [
keywords: ["САР", "КОРФ", "Гектар", "Работа", "ТОР", "Ипотека", "АЗРФ", "СПВ"],
assetImageMode: "leaflets",
assetImages: [
{ title: "Территория опережающего развития", file: "leaflet-tor-2026.png" },
{ title: "Арктическая зона Российской Федерации", file: "leaflet-azrf-2026.png" },
{ title: "Специальный административный район", file: "leaflet-sar-2026.png" },
{ title: "Курильские острова", file: "leaflet-kurils-2026.png" },
{ title: "Гектар на Дальнем Востоке и в Арктике", file: "leaflet-hectare-2026.png" },
{ title: "Дальневосточная и арктическая ипотека", file: "leaflet-mortgage-2026.png" },
{ title: "Работа на Дальнем Востоке", file: "leaflet-work-dfo-2026.png" },
{ title: "Работа в Арктической зоне Российской Федерации", file: "leaflet-work-arctic-2026.png" },
{ title: "Приморский край", file: "leaflet-primorye-2026.png" },
{ title: "Хабаровский край и Еврейская автономная область", file: "leaflet-khabarovsk-eao-2026.png" },
{ title: "Территория опережающего развития", file: "leaflet-tor-2026.png", preview: "leaflet-tor-2026.webp" },
{ title: "Арктическая зона Российской Федерации", file: "leaflet-azrf-2026.png", preview: "leaflet-azrf-2026.webp" },
{ title: "Специальный административный район", file: "leaflet-sar-2026.png", preview: "leaflet-sar-2026.webp" },
{ title: "Курильские острова", file: "leaflet-kurils-2026.png", preview: "leaflet-kurils-2026.webp" },
{ title: "Гектар на Дальнем Востоке и в Арктике", file: "leaflet-hectare-2026.png", preview: "leaflet-hectare-2026.webp" },
{ title: "Дальневосточная и арктическая ипотека", file: "leaflet-mortgage-2026.png", preview: "leaflet-mortgage-2026.webp" },
{ title: "Работа на Дальнем Востоке", file: "leaflet-work-dfo-2026.png", preview: "leaflet-work-dfo-2026.webp" },
{ title: "Работа в Арктической зоне Российской Федерации", file: "leaflet-work-arctic-2026.png", preview: "leaflet-work-arctic-2026.webp" },
{ title: "Приморский край", file: "leaflet-primorye-2026.png", preview: "leaflet-primorye-2026.webp" },
{ title: "Хабаровский край и Еврейская автономная область", file: "leaflet-khabarovsk-eao-2026.png", preview: "leaflet-khabarovsk-eao-2026.webp" },
],
},
{
Expand Down
6 changes: 6 additions & 0 deletions krdv-guidelines-prototype/src/styles/responsive.css
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,9 @@
grid-template-columns: 1fr;
}
}

/* Keep anchor destinations clear of the persistent mobile menu without shrinking titles. */
@media (max-width: 860px) {
.page-scroll { scroll-padding-top: 72px; }
.content-section { scroll-margin-top: 72px; }
}
57 changes: 56 additions & 1 deletion krdv-guidelines-prototype/tests/e2e/brandbook.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,12 @@ test("ten current leaflet covers display uncropped and open full-size PNG", asyn
await expect(first).toHaveAttribute("target", "_blank");
const image = first.locator("img");
await first.scrollIntoViewIfNeeded();
await expect.poll(() => image.evaluate((img) => img.complete && img.naturalWidth > 500)).toBe(true);
await expect.poll(() => image.evaluate((img) => img.complete && img.naturalWidth >= 400)).toBe(true);
await expect(image).toHaveCSS("object-fit", "contain");
await expect(image).toHaveAttribute("src", /leaflet-[\w-]+-2026\.webp$/);
const previewResponse = await page.request.get(await image.getAttribute("src"));
expect(previewResponse.ok()).toBeTruthy();
expect(previewResponse.headers()["content-type"]).toContain("image/webp");
const response = await page.request.get(await first.getAttribute("href"));
expect(response.ok()).toBeTruthy();
expect(response.headers()["content-type"]).toContain("image/png");
Expand Down Expand Up @@ -483,3 +487,54 @@ test("merch page shows its examples without an external board link or retired ca
}));
expect(viewport.scrollWidth).toBeLessThanOrEqual(viewport.width + 2);
});

test("mobile section navigation puts focus on the selected heading", async ({ page, isMobile }) => {
test.skip(!isMobile, "Only the mobile drawer changes focus ownership");
await page.goto("/#/logo");
await ready(page);
await page.locator(".mobile-menu-button").click();
await page.locator('.nav-group.is-open .nav-children button').filter({ hasText: "Охранное поле" }).click();
await expect(page).toHaveURL(/#\/logo\/clearspace$/);
await expect(page.locator("#clearspace h2")).toBeFocused();
await expect(page.locator(".guideline-shell")).not.toHaveClass(/nav-open/);
});

test("mobile subsection headers are not covered by the fixed menu", async ({ page, isMobile }) => {
test.skip(!isMobile, "This offset is only needed with the mobile menu");
await page.goto("/#/merch");
await ready(page);
await page.locator("#examples").evaluate((el) => el.scrollIntoView({ block: "start", behavior: "auto" }));
const rectangles = await page.evaluate(() => {
const menu = document.querySelector(".mobile-menu-button").getBoundingClientRect();
const header = document.querySelector("#examples h2").getBoundingClientRect();
return { menuBottom: menu.bottom, titleTop: header.top };
});
expect(rectangles.titleTop).toBeGreaterThanOrEqual(rectangles.menuBottom + 4);
});

test("all media assets remain available after scrolling through every section", async ({ page, browserName }) => {
test.skip(browserName !== "chromium", "Exhaustive lazy loading runs on both desktop/mobile Chromium; other engines cover routes separately");
const errors = [];
page.on("pageerror", (error) => errors.push(error.message));
for (const [route] of titles) {
await page.goto("/#/" + route);
await ready(page);
const sections = page.locator(".content-section");
const sectionCount = await sections.count();
expect(sectionCount).toBeGreaterThan(0);
for (let i = 0; i < sectionCount; i += 1) {
const section = sections.nth(i);
await section.scrollIntoViewIfNeeded();
const images = section.locator("img");
const count = await images.count();
for (let j = 0; j < count; j += 1) {
const image = images.nth(j);
await image.scrollIntoViewIfNeeded();
await expect.poll(() => image.evaluate((el) => el.complete && el.naturalWidth > 0), { timeout: 6000 }).toBe(true);
}
}
const overflow = await page.locator("#page").evaluate((el) => el.scrollWidth - el.clientWidth);
expect(overflow, "Horizontal overflow in " + route).toBeLessThanOrEqual(2);
}
expect(errors).toEqual([]);
});
Loading