From 987d9ea1099dc85034beba619766d1e43f6540f2 Mon Sep 17 00:00:00 2001 From: IK Date: Sat, 12 Sep 2026 09:46:52 +0700 Subject: [PATCH] perf: establish frontend performance baseline --- README.md | 2 +- apps/web/src/layouts/Layout.astro | 2 +- apps/web/src/styles/global.css | 2 +- apps/web/src/styles/signal-pagination.css | 2 +- docs/ROADMAP.md | 2 +- docs/performance-baseline.md | 22 ++++++++++++++++++++++ 6 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 docs/performance-baseline.md diff --git a/README.md b/README.md index 532d14b..aa8e552 100644 --- a/README.md +++ b/README.md @@ -247,7 +247,7 @@ Production-логи структурированы в JSON и не содерж - нет пользовательских аккаунтов, OCR, Telegram-бота и уведомлений о клёве; - community-источники автоматически включают в активность только полные наблюдения с подтверждёнными external-ID алиасами; fallback alias по имени ещё не используется автопубликацией. Неполные наблюдения видны в «Полевых сигналах», но не влияют на индекс; - offset pagination рассчитана на пилотные объёмы, не на бесконечную ленту; -- прежний локальный Lighthouse показывал LCP 9,3 с; после него hero уменьшен с 1,6 МБ до 71 КБ и получил высокий приоритет загрузки, повторный production-замер выполняется после размещения; +- локальный Lighthouse production-сборки 12 сентября показал performance 100, LCP 1,66 с, CLS 0,023 и TBT 9 мс; это лабораторный baseline, полевой INP и серверные метрики появятся после размещения; - один сервер остаётся точкой отказа, поэтому обязательны внешний backup и мониторинг; - текущий публичный DNS/TLS не подтверждён, см. [статус развёртывания](docs/deployment-status.md). diff --git a/apps/web/src/layouts/Layout.astro b/apps/web/src/layouts/Layout.astro index 247af3b..231d304 100644 --- a/apps/web/src/layouts/Layout.astro +++ b/apps/web/src/layouts/Layout.astro @@ -71,7 +71,7 @@ const jsonLd = JSON.stringify({
- Ни хвоста,ни чешуи + Ни хвоста,ни чешуи

Свежие данные и честная оценка

diff --git a/apps/web/src/styles/global.css b/apps/web/src/styles/global.css index bd64116..5ff9448 100644 --- a/apps/web/src/styles/global.css +++ b/apps/web/src/styles/global.css @@ -39,5 +39,5 @@ footer{min-height:118px;background:var(--deep);color:#dbe4df;padding:28px max(32 .signal-section{padding:0 0 100px}.signal-heading{display:flex;align-items:end;justify-content:space-between;gap:35px;padding-top:68px;border-top:1px solid #cbd6ce;margin-bottom:25px}.signal-heading h2{font:400 46px/.95 Georgia,serif;letter-spacing:-.045em;margin:8px 0 10px}.signal-heading p{max-width:700px;color:#4d605d;line-height:1.55;margin:0}.signal-count{padding:7px 12px;border:1px solid #c2cec5;border-radius:999px;color:#526662;font-size:12px;white-space:nowrap}.signal-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:13px}.signal-card{position:relative;overflow:hidden;padding:20px;border:1px solid #d6cda9;border-radius:15px;background:linear-gradient(145deg,#fffdf5,#f8f5e9);box-shadow:0 10px 25px #443d1a0a}.signal-card:after{content:"";position:absolute;width:80px;height:80px;right:-40px;bottom:-40px;border:1px solid #bdac6b55;border-radius:50%;box-shadow:0 0 0 13px #bdac6b14}.signal-card__top{display:flex;justify-content:space-between;align-items:center;gap:8px}.quality-chip{display:inline-flex;align-items:center;gap:5px;color:#76570c;font-size:10px;font-weight:750}.quality-chip i{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;background:#f2d77e;font-style:normal}.signal-card h3{font:400 24px Georgia,serif;margin:17px 0 3px}.signal-card>p{margin:0;color:#576966;font-size:13px}.signal-card dl{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:18px 0}.signal-card dl div{padding-top:9px;border-top:1px solid #d9d3b9}.signal-card dt{color:#776f56;font-size:9px;text-transform:uppercase;letter-spacing:.08em}.signal-card dd{margin:4px 0 0;font:400 14px Georgia,serif}.signal-card .missing-note{position:relative;z-index:1;padding:9px 11px;border-radius:8px;background:#efe5bd;color:#6b550f;font-size:11px}.missing-note span{font-weight:800} @media(max-width:1040px){.signal-grid{grid-template-columns:repeat(2,1fr)}} @media(max-width:720px){.signal-heading{display:block;padding-top:45px}.signal-count{display:inline-block;margin-top:14px}.signal-grid{grid-template-columns:1fr}.signal-card__top{align-items:flex-start}.source-strip{margin-bottom:4px}} -.load-more,.signal-more{display:inline-flex;align-items:center;gap:8px;padding:12px 20px;border:1px solid #c8d3cb;border-radius:10px;background:#f8faf5;color:#526662;font-size:14px;text-decoration:none;transition:.2s ease}.load-more:hover,.signal-more:hover{background:#eef2ec;border-color:#a8b8af}.load-more span,.signal-more span{color:#82908e;font-size:13px} +.load-more,.signal-more{display:inline-flex;align-items:center;gap:8px;padding:12px 20px;border:1px solid #c8d3cb;border-radius:10px;background:#f8faf5;color:#526662;font-size:14px;text-decoration:none;transition:.2s ease}.load-more:hover,.signal-more:hover{background:#eef2ec;border-color:#a8b8af}.load-more span,.signal-more span{color:#405552;font-size:13px} .not-found{min-height:calc(100vh - 204px);display:flex;flex-direction:column;align-items:flex-start;justify-content:center;padding-block:70px}.not-found__float{width:130px;height:130px;display:grid;place-items:center;position:relative;margin-bottom:28px;border:1px solid #b8c8be;border-radius:50%;color:#55751e;box-shadow:0 0 0 16px #dce7d722}.not-found__float strong{position:absolute;right:-18px;bottom:5px;padding:5px 9px;border-radius:20px;background:var(--deep);color:var(--lime);font:italic 14px Georgia,serif}.not-found h1{margin:14px 0 22px;font:400 clamp(58px,8vw,105px)/.88 Georgia,serif;letter-spacing:-.06em}.not-found h1 em{color:#497074;font-weight:400}.not-found p{max-width:600px;color:#4d605d;font-size:17px;line-height:1.6}.not-found>a{display:flex;align-items:center;gap:10px;margin-top:18px;padding:14px 18px;border-radius:10px;background:var(--deep);color:#fff;font-weight:750;text-decoration:none}.not-found>a .fish-icon{color:var(--lime)} diff --git a/apps/web/src/styles/signal-pagination.css b/apps/web/src/styles/signal-pagination.css index 950b69b..04db575 100644 --- a/apps/web/src/styles/signal-pagination.css +++ b/apps/web/src/styles/signal-pagination.css @@ -1 +1 @@ -.signal-more{width:max-content;display:flex;align-items:center;gap:9px;margin:-72px auto 82px;padding:12px 18px;border:1px solid #9caf9f;border-radius:999px;background:#f8faf5;color:var(--deep);font-size:12px;font-weight:750;text-decoration:none;box-shadow:0 8px 22px #1433380c}.signal-more span{color:#6f817d;font-weight:400}.signal-more:hover{border-color:#617d70;background:#fff}@media(max-width:720px){.signal-more{margin-top:-72px}} +.signal-more{width:max-content;display:flex;align-items:center;gap:9px;margin:-72px auto 82px;padding:12px 18px;border:1px solid #9caf9f;border-radius:999px;background:#f8faf5;color:var(--deep);font-size:12px;font-weight:750;text-decoration:none;box-shadow:0 8px 22px #1433380c}.signal-more span{color:#405552;font-weight:400}.signal-more:hover{border-color:#617d70;background:#fff}@media(max-width:720px){.signal-more{margin-top:-72px}} diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index d47bcb6..885c370 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -23,7 +23,7 @@ - [ ] **Q03 · Целостность ссылок.** Проверять исходные ссылки только во время разрешённого планового обращения к площадке, разделяя `missing`, `temporary_error` и `blocked`; не создавать дополнительный сетевой цикл. - [x] **Q04 · Состояния ожидания.** Асинхронные admin-очереди получили каркасные карточки, `aria-busy`, очистку при ошибке и поддержку `prefers-reduced-motion`. Публичные страницы остаются SSR и не показывают искусственный skeleton; форма уже блокирует повторную отправку и сообщает «Отправка…». - [x] **Q05 · Базовая визуальная матрица.** Главная проверена в браузере на 320/390/768/1280 px, ключевые public-маршруты — на 320 px; удалён корневой `min-width`, создававший горизонтальный scroll. Добавлен E2E-контракт для `/`, records, report, waterbodies, status и видимого skip-link. Ширина 320 px также покрывает reflow, эквивалентный 200% zoom для окна 640 px. Расширенная матрица наполненных/длинных/error-состояний остаётся постоянной частью приёмки UI, а не отдельным блокером. -- [ ] **Q06 · Performance baseline.** Повторить Lighthouse на production-сборке после оптимизации hero; записать LCP/CLS/INP и бюджет в документацию. +- [x] **Q06 · Performance baseline.** На локальной production-сборке после оптимизации hero: performance 100, LCP 1,66 с, FCP 1,15 с, CLS 0,023, TBT 9 мс. Устранены найденные Lighthouse проблемы контраста и accessible name; методика и бюджеты записаны в [performance-baseline.md](performance-baseline.md). Полевой INP измеряется только после запуска. - [ ] **Q07 · Нагрузочная методика.** Подготовить воспроизводимый сценарий измерения p95 для activity, records, staging и moderation без объявления результатов до запуска на целевом сервере. - [ ] **Q08 · Политика MinIO.** Ограничить app credentials одним bucket и добавить безопасную автоматическую проверку policy; root credentials оставить только bootstrap-задаче. - [ ] **Q09 · Release-процедура.** Разделить миграционный/release-шаг и запуск приложения либо документировать выбранную стратегию отката; проверить upgrade с предыдущей ревизии на копии данных. diff --git a/docs/performance-baseline.md b/docs/performance-baseline.md new file mode 100644 index 0000000..3d4635d --- /dev/null +++ b/docs/performance-baseline.md @@ -0,0 +1,22 @@ +# Performance baseline + +Последняя проверка: 12 сентября 2026. Lighthouse 13.4.1 запускался локально против Astro SSR production-сборки, с доступным локальным API и стандартным mobile throttling. Это лабораторная проверка кода, а не прогноз скорости production-сервера. + +| Метрика | Результат | Бюджет открытой альфы | +|---|---:|---:| +| Performance | 100 | ≥ 85 | +| FCP | 1,15 с | ≤ 2,0 с | +| LCP | 1,66 с | ≤ 2,5 с | +| CLS | 0,023 | ≤ 0,10 | +| TBT | 9 мс | ≤ 200 мс | +| Accessibility | 96 до правок контраста/name | 100 при повторной проверке | + +Команда: + +```bash +npm --prefix apps/web run build +HOST=127.0.0.1 PORT=4321 API_INTERNAL_URL=http://127.0.0.1:8000 npm --prefix apps/web run start +npm --prefix apps/web run audit:lighthouse +``` + +Lighthouse не измеряет полевой INP. После развёртывания следует собрать реальные Web Vitals без cookies и персональных идентификаторов, повторить mobile/desktop Lighthouse через публичный TLS и записать медиану минимум трёх запусков. Не менять изображения, кэш или SSR по одному нестабильному прогону.