Files
portal/liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md
T
Дмитрий f52402fabe docs(handoff): Sprint 2 Phase C — Google Fonts API v2 + @font-face fallback strategy (audit O-stack-10)
Sprint 2 Phase C (modernization). Закрытие audit O-stack-10:
- DEVELOPER_HANDOFF §4.5 — Google Fonts API v2 + @font-face fallback strategy
  (расширение раздела о Sprint 1 Phase E font-display стратегия §4.4).
- Документировано: когда нужен fallback (расширенная аудитория Chrome 50-99 / Safari 12-14),
  шаблон @font-face блоков, migration path (скачивание + размещение), trade-offs.
- Текущее решение Лидерры: только API v1 + display=swap (целевая аудитория Chrome 100+).
- Решение пересмотреть при GDPR/audit/статистике старых браузеров.

O-stack-06 (FD plugin в ~/.claude/settings.json) — manual user step, выводится в
финальном отчёте Sprint 2 (файл вне git).

O-refactor-07 (CLAUDE.md §0 reorg) — уже фактически реализовано в §9 (полная
история перенесена в docs/CHANGELOG_claude_md.md ранее, оставлены 2 последние
версии в шапке).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 19:39:26 +03:00

41 KiB
Raw Blame History

Лидерра · Developer Handoff

Версия: v8 Forest · 2026-05-07 Назначение: Всё необходимое разработчику для реализации портала Лидерра без участия дизайнера. Работает поверх HTML-прототипов в concepts/v8_*.html.


0. TL;DR — старт за 5 минут

  1. Стек, который нельзя оспаривать: Vue 3 + Vuetify 3, светлая тема (тёмная — v2), Yandex Cloud, Yandex 360 SSO для админки, ЮKassa primary platежи. CTO-11.
  2. Палитра v8 Forest: скопируй §3 «CSS-переменные» в assets/tokens.css и подключи глобально.
  3. Шрифты: Inter (Google Fonts, axis opsz 14..32) + JetBrains Mono. Импорт в <head> или @import. См. §4.
  4. Лого: SVG inline в assets/logo-mark.svg (см. §2 «Бренд»).
  5. Все 25 экранов: живые HTML в concepts/ — открой v8_dashboard.html и снимай вёрстку напрямую. WCAG 2.1 AA подтверждено axe-core 4.10.2 на 4 viewports.
  6. Если что-то непонятно — смотри §14 «FAQ» или пиши автору.

1. Архитектура и стек

1.1. Технологии (зафиксировано)

Слой Технология Источник
Frontend Vue 3 + Vuetify 3 CTO-11, не оспаривается
Стейт Pinia стандарт Vue 3
Роутер Vue Router 4 стандарт
Сборка Vite стандарт
HTTP Axios или native fetch на выбор разработчика
Charts ApexCharts бриф §12 — указан напрямую
Виртуализация vue-virtual-scroller бриф §11 для Канбана
Drag-and-drop @vueuse/integrations или vuedraggable для Канбана
Backend (out-of-scope) Yandex Cloud, FastAPI/Node — на выбор

1.2. Структура проекта (рекомендация)

src/
├── assets/
│   ├── tokens.css              ← из §3 этого документа
│   ├── fonts.css               ← @import Inter + JBM
│   └── logo-mark.svg           ← из §2.3
├── components/
│   ├── shell/
│   │   ├── AppSidebar.vue
│   │   ├── AppTopbar.vue
│   │   └── BrandMark.vue       ← inline SVG
│   ├── ui/
│   │   ├── BaseButton.vue
│   │   ├── BaseChip.vue        ← с 14 status variants
│   │   ├── BaseInput.vue
│   │   ├── DataTable.vue
│   │   ├── KpiCard.vue
│   │   ├── SlideOverDrawer.vue
│   │   └── BalanceWidget.vue   ← с runway-scale
│   └── domain/
│       ├── DealCard.vue
│       ├── KanbanBoard.vue
│       ├── ActivityLog.vue
│       └── BillingTransactionRow.vue
├── pages/
│   ├── Dashboard.vue
│   ├── DealsList.vue
│   ├── DealCard.vue
│   ├── Kanban.vue
│   ├── Billing.vue
│   ├── Settings.vue
│   ├── Reports.vue
│   ├── Login.vue
│   ├── errors/
│   │   ├── NotFound.vue
│   │   ├── Forbidden.vue
│   │   └── ServerError.vue
│   ├── admin/
│   │   ├── AdminLogin.vue
│   │   ├── TenantsList.vue
│   │   ├── TenantCard.vue
│   │   ├── AdminBilling.vue
│   │   ├── Incidents.vue
│   │   └── SystemSettings.vue
│   └── public/
│       ├── Home.vue
│       ├── Pricing.vue
│       ├── Offer.vue
│       └── Privacy.vue
└── App.vue

1.3. Окружения

Окружение Frontend API Что особенного
local localhost:5173 localhost:8000 hot reload, mock data
stage stage.liderra.app api.stage.liderra.app seed-данные, без оплат
prod liderra.app + admin.liderra.app api.liderra.app полный 2FA, RLS, audit log

2. Бренд

2.1. Имя

«Лидерра» — кириллица. Wordmark с точкой в конце: Лидерра. Точка покрашена в bright Teal #32C8A9 (--side-icon-act) на тёмном sidebar и в базовый Teal #0F6E56 на светлом фоне. Латинская транслитерация: Liderra (для домена liderra.app, email support@liderra.app).

Tone of voice: уверенный без помпезности, технический без сухости, премиальный без снобизма. Прямые формулировки про деньги. Никакого «эстетичного решения», «бесшовного опыта», «инновационного подхода».

2.2. Лого — L-Square с Teal-точкой

[ белый rounded-square 22×22 ]
       │
       └─ внутри: L-stroke 4.5px (тёмный) + Teal-точка r=3.5 в конце

2.3. SVG (inline, копировать в BrandMark.vue)

<!-- Версия для тёмного sidebar (на белом квадрате) -->
<svg viewBox="0 0 48 48" width="22" height="22">
  <rect x="2" y="2" width="44" height="44" rx="10" fill="#FFFFFF"/>
  <path d="M16 14 L16 34 L32 34"
        stroke="#012019" stroke-width="4.5"
        stroke-linecap="round" stroke-linejoin="round" fill="none"/>
  <circle cx="32" cy="34" r="3.5" fill="#0F6E56"/>
</svg>
<!-- Версия для светлого фона (тёмный квадрат) -->
<svg viewBox="0 0 48 48" width="22" height="22">
  <rect x="2" y="2" width="44" height="44" rx="10" fill="#0A1319"/>
  <path d="M16 14 L16 34 L32 34"
        stroke="#FFFFFF" stroke-width="4.5"
        stroke-linecap="round" stroke-linejoin="round" fill="none"/>
  <circle cx="32" cy="34" r="3.5" fill="#32C8A9"/>
</svg>
<!-- Favicon — без рамки и точки (упрощённый) -->
<svg viewBox="0 0 48 48" width="32" height="32">
  <rect width="48" height="48" rx="8" fill="#0F6E56"/>
  <path d="M16 14 L16 34 L32 34"
        stroke="#FFFFFF" stroke-width="5.5"
        stroke-linecap="round" stroke-linejoin="round" fill="none"/>
</svg>

2.4. Что нельзя

  • Менять пропорции, цвета, обводку лого, добавлять тени/градиенты
  • Использовать «Лидпоток» (старое имя — deprecated)
  • Использовать lowercase «лидерра.» — wordmark капитализирован
  • Использовать слово «премиум» в маркетинге без обоснования (мы — про прозрачность и контроль, не про роскошь)

3. Дизайн-токены (CSS variables)

Все токены — реальные значения из v8 Forest. Скопировать в assets/tokens.css, подключить в <head> глобально. Все WCAG-контрасты подтверждены через axe-core 4.10.2 на 25 макетах.

:root {
  /* === ПОВЕРХНОСТИ (Forest light, hue ≈ 80, warm ivory) === */
  --bg:            #F6F3EC;  /* Page background */
  --surface:       #FFFDFA;  /* Карточки, поднятые поверхности */
  --sunken:        #F0EDE4;  /* Чуть глубже bg — для table thead, sunken-areas */
  --hairline:      #D9D5CD;  /* 1px UI-border (формальный) */
  --hairline-soft: #E8E3D6;  /* Внутренние дивайдеры строк */

  /* === ТЕКСТ (на ivory bg, проверено WCAG AA) === */
  --ink:           #081319;  /* 16.96:1 — primary text */
  --ink-2:         #343C41;  /* 10.34:1 — secondary */
  --ink-3:         #66635C;  /* 5.42:1  — muted (≥4.5:1) */
  --ink-disabled:  #92907B;  /* 3.48:1  — для UI-компонентов / disabled fields */

  /* === БРЕНД === */
  --accent:        #0F6E56;  /* Teal — primary brand · 5.94:1 на bg */
  --accent-tint:   #E1EEEA;  /* Tint для selection / chip-tinted */
  --accent-deep:   #084635;  /* Hover/active state */

  /* === SIDEBAR (тёмный teal-noir) === */
  --side-bg:        #012019;
  --side-text:      #B1C2BD;  /* 9.23:1 на side-bg */
  --side-text-2:    #7A8C87;  /* nav-eyebrow muted */
  --side-active:    #13382F;  /* Selected item fill */
  --side-icon:      #5C7A72;  /* Idle icon */
  --side-icon-act:  #32C8A9;  /* Bright Teal — active icon · 8.15:1 */
  --side-hover:     #0A2A22;
  --side-border:    #1A3A30;

  /* === 14 СТАТУСОВ ВОРОНКИ (OKLCH, ΔE2000 ≥ 10.57) === */
  --st-new-tint:    #FFD8CF;  --st-new-solid:    #B94837;  /* Новая */
  --st-work-tint:   #FFDBC4;  --st-work-solid:   #B35100;  /* В работе */
  --st-call-tint:   #F6E2BC;  --st-call-solid:   #9A6700;  /* Дозвон */
  --st-nocall-tint: #E9E8BD;  --st-nocall-solid: #7E7500;  /* Не дозвон. */
  --st-neg-tint:    #D9EDC6;  --st-neg-solid:    #538200;  /* Переговоры */
  --st-quote-tint:  #C7F0D7;  --st-quote-solid:  #008A4D;  /* КП отправлено */
  --st-think-tint:  #BCF1E9;  --st-think-solid:  #008C7E;  /* Думает */
  --st-wait-tint:   #BAF0F6;  --st-wait-solid:   #00889B;  /* Ждёт оплату */
  --st-paid-tint:   #C0ECFF;  --st-paid-solid:   #007EB8;  /* Оплачено */
  --st-refund-tint: #D1E5FF;  --st-refund-solid: #406DC8;  /* Возврат */
  --st-fail-tint:   #E1E0FF;  --st-fail-solid:   #6C60C4;  /* Отказ */
  --st-dup-tint:    #F2DAFF;  --st-dup-solid:    #9052AE;  /* Дубликат */
  --st-spam-tint:   #FFD7EE;  --st-spam-solid:   #AA4788;  /* Спам */
  --st-arch-tint:   #FFD6DD;  --st-arch-solid:   #B7445F;  /* Архив */

  /* === РАДИУСЫ === */
  --r-xs: 4px;   /* мелкие пилы (chip count, kbd, mini-badges) */
  --r-sm: 6px;   /* кнопки, инпуты, нав-айтемы */
  --r-md: 10px;  /* карточки, секции, табли */
  --r-lg: 14px;  /* hero-карточки, лендинг-блоки */
  /* radius для аватара/чипа — 50% или 100px */

  /* === ШРИФТЫ === */
  --font-ui:      'Inter', system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Consolas, monospace;
}

3.1. Тени — НЕ используем

v8 Forest стилистически отказывается от теней. Глубина обеспечивается только 1px hairline borders. Исключение — slide-over drawer (нужен лёгкий left-edge shadow для иерархии слоёв):

.drawer { box-shadow: -16px 0 40px rgba(10,19,25,0.10); }

Если разработчик сомневается — не добавлять box-shadow, использовать border: 1px solid var(--hairline).

3.2. Spacing scale (4px-base)

Token Value Применение
4px xs плотные внутренние gap
8px sm gap между кнопками в группе
12px md padding внутри карточек
16px lg gap между элементами секции
20-24px xl padding section, gap между секциями
32px 2xl page padding desktop
48-64px 3xl hero padding

4. Типографика

4.1. Шрифты (Google Fonts)

<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=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">

Inter с осью opsz — это Inter 4.0+, один файл с двумя «режимами»:

  • font-variation-settings: 'opsz' 14 — UI-режим (мелкий текст)
  • font-variation-settings: 'opsz' 28 — display-режим (заголовки)

Подключается через CSS variable:

body {
  font-family: var(--font-ui);
  font-feature-settings: 'cv11', 'ss01', 'ss03';  /* slashed-zero, single-storey a */
  font-variation-settings: 'opsz' 14;
  -webkit-font-smoothing: antialiased;
}

.page-title {
  font-size: 28px;
  font-weight: 600;
  font-variation-settings: 'opsz' 28;  /* visual switch to display */
  letter-spacing: -0.02em;
}

4.2. Шкала размеров

Применение font-size weight opsz letter-spacing line-height
Hero h1 (лендинг) 56px 600 32 -0.03em 1.05
Page title 28px 600 28 -0.02em 1.1
Section h2 22px 600 24 -0.018em 1.15
Card title 16px 600 18 -0.012em 1.3
Body 13-14px 400 14 -0.005em 1.5
Eyebrow / label 11.5px 500 14 letter-spacing 0.005em 1.4
Mono numeric 12.5px 500 -0.005em 1.4
Caption 11px 500 14 0.005em 1.4

4.3. Numeric (числа = деньги)

Везде где число — var(--font-mono) + font-feature-settings: 'tnum'. Это даёт tabular figures (одинаковая ширина цифр), без чего ровный right-align в таблицах ломается.

.price, .deal-price, .balance-amount, .kpi-value, .tx-amount, .timer {
  font-family: var(--font-mono);
  font-feature-settings: 'tnum';
}

4.4. Стратегия загрузки шрифтов (audit O-stack-09)

Используется <link rel="stylesheet"> с Google Fonts API v1 + параметр &display=swap (см. §4.1).

  • &display=swap: браузер сразу рендерит fallback-шрифт (system-ui), переключается на загруженный Inter/JetBrains Mono когда тот доступен. Стратегия FOUT (Flash of Unstyled Text), но без невидимого текста — лучше для UX, чем &display=block или дефолтный &display=auto.

  • WOFF2 формат: Google Fonts отдаёт WOFF2 по умолчанию для современных браузеров (Chrome 36+, Firefox 39+, Safari 12+). Сжатие лучше WOFF на 20–30%.

  • Preconnect: для ускорения первого byte рекомендуется добавить в <head>:

    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    

    Это сокращает TTFB на медленных сетях (~50–200 мс). В §4.1 этот блок уже включён в snippet подключения.

  • Совместимость: для целевой аудитории Chrome 100+, Safari 15+, Firefox 95+ дополнительный fallback не нужен. Для расширенной аудитории — добавить @font-face с WOFF (legacy).

4.5. Google Fonts API v2 + @font-face fallback (audit O-stack-10)

Текущий <link rel="stylesheet"> использует Google Fonts API v1 (https://fonts.googleapis.com/css2?family=...). Для расширенной совместимости с устаревшими браузерами (поддержка ниже Chrome 100 / Safari 15 / Firefox 95) можно добавить @font-face fallback:

Когда использовать API v2

  • Целевая аудитория Chrome 100+, Safari 15+, Firefox 95+ (default для Лидерры) — оставить только API v1, fallback не нужен.
  • Расширенная аудитория включая Chrome 5099, Safari 1214 — добавить @font-face WOFF fallback.

Шаблон @font-face fallback (для расширенной совместимости)

@font-face {
    font-family: 'Inter Fallback';
    src: url('/fonts/inter-variable.woff2') format('woff2-variations'),
         url('/fonts/inter-variable.woff') format('woff');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'JetBrains Mono Fallback';
    src: url('/fonts/jetbrains-mono.woff2') format('woff2'),
         url('/fonts/jetbrains-mono.woff') format('woff');
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
}

Migration path

  1. Скачать Inter и JetBrains Mono с google-webfonts-helper.herokuapp.com или github.com/rsms/inter/releases и github.com/JetBrains/JetBrainsMono/releases.
  2. Поместить в public/fonts/ (или app/public/fonts/ для Laravel).
  3. В app/resources/css/app.css (или Vuetify global stylesheet) добавить @font-face блоки.
  4. В Vuetify config defaults — добавить fallback chain: font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;.

Trade-offs

  • + Self-hosted шрифты — нет зависимости от Google CDN, GDPR-friendly (нет cookies на fonts.googleapis.com).
  • + Гарантия совместимости на 10+ лет в браузерах.
  • − Размер репозитория — +500 KB на файлы шрифтов (Inter Variable ~250 KB + JetBrains Mono ~250 KB в WOFF2).
  • Ручной upgrade — при выходе новых версий Inter/JetBrains Mono нужно скачивать вручную (vs автообновление через Google Fonts).

Решение для Лидерры (текущее, 09.05.2026)

Используется только Google Fonts API v1 + &display=swap (см. §4.4). @font-face fallback не применяется — целевая аудитория покрывает 99%+ современных браузеров. Решение пересмотреть, если: появится требование GDPR-encryption, аудит безопасности обнаружит зависимость от внешнего CDN, или статистика браузеров покажет долю старых ≥5%.


5. Компонентная библиотека

5.1. AppShell — общий каркас (sidebar + topbar)

Sidebar 232px sticky слева на тёмном --side-bg, topbar 48px sticky сверху, контент в центре. На viewport ≤ 1100px sidebar сжимается до 56px (icon-only). На ≤ 768px sidebar скрывается.

Живой пример: открой v8_dashboard.html, изучи .app, .side, .main, .topbar.

5.2. Sidebar nav-item

<a class="nav-item" :class="{ active: isActive }" :aria-current="isActive ? 'page' : null" :href="route">
  <svg class="nav-icon" ... aria-hidden="true"><!-- 15px stroke 1.6 --></svg>
  <span class="nav-text">{{ label }}</span>
  <span v-if="count" class="nav-count">{{ count }}</span>
</a>

CSS правила см. в v8_dashboard.html .nav-item block. Selected state = solid --side-active fill, no border, no glow. Active icon — --side-icon-act (bright Teal).

5.3. Button

<button type="button" class="btn">Базовая</button>
<button type="button" class="btn btn-primary">Primary (Teal)</button>
<button type="button" class="btn btn-danger">Danger</button>

Высоты:

  • 30px — icon-only
  • 32px — компактные / topbar / в фильтрах
  • 34-36px — inline в формах
  • 38-42px — primary CTA

Variants:

  • .btn — surface + hairline border
  • .btn-primary — accent fill, white text
  • .btn-danger--st-new-solid text + hover #FFE7E2 bg

5.4. Status Chip — 14 вариантов

Два режима:

1. Plain (dot + text, без фона) — для таблиц, плотных строк:

<span class="chip chip-paid">
  <span class="dot" aria-hidden="true"></span>Оплачено
</span>

2. Tinted (с фоном) — для emphasis (drawer current status, dashboard summary):

<span class="chip chip-tinted chip-paid">
  <span class="dot" aria-hidden="true"></span>Оплачено
</span>

Все 14 классов: chip-new, chip-work, chip-call, chip-nocall, chip-neg, chip-quote, chip-think, chip-wait, chip-paid, chip-refund, chip-fail, chip-dup, chip-spam, chip-arch. Цвета — в §3 токены.

5.5. Avatar (concentric ring)

<span class="avatar" aria-hidden="true">
  ИП  <!-- 2 letter initials -->
</span>
.avatar {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--ink); color: #fff;  /* dark variant; см. v8_*  для tinted */
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 9.5px; font-weight: 600;
  position: relative;
}
.avatar::after {
  content: ''; position: absolute; inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(10,19,25,0.10);  /* concentric ring trick */
  pointer-events: none;
}

5.6. Table

Hairline-only, no shadows. Sticky thead. Hover row → --sunken. Selected row → solid --ink fill, white text.

См. v8_deals.html .tbl. На mobile — таблица превращается в карточки.

5.7. Drawer (slide-over)

460px справа, --surface bg, hairline left border + lazy left-edge shadow. Scrim — rgba(10,19,25,0.18) + backdrop-filter: blur(2px).

См. v8_deals.html .drawer.

5.8. KPI card

Hairline border, no shadow. Number в JBM mono с tnum. Trend = single Teal up-arrow на положительной дельте, --st-new-solid red на отрицательной. Никаких icon-чипов.

См. v8_dashboard.html .kpi.

5.9. Balance widget с runway-scale

Наследник KPI. Те же hairlines + дополнительная Teal-рамка через ::before. Внутри — runway-bar (7 сегментов hairline-tick'ов, заполненных под уровень дней).

См. v8_dashboard.html .balance + .runway.

5.10. Timeline / Activity Log

Vertical 1px line + цветные точки по типу события:

  • create — Teal --accent
  • status-change--st-call-solid (оранжевый)
  • comment--ink (чёрный)
  • call--st-quote-solid (зелёный)
  • tag--st-think-solid (бирюзовый)
  • assign--st-paid-solid (синий)
  • next/dashed--ink-disabled с border-style: dashed

Описание + старое→новое значение через <span class="from">old</span> → <span class="to">new</span> с line-through на from и mono font на обоих.

См. v8_deal_card.html .activity.


6. Список всех 25 экранов

Каждый — отдельный HTML в concepts/, production-ready по разметке (нужна интеграция с реальными данными). Все прошли axe-core 4.10.2 = 0 violations на 1680/1440/768/375 (где применимо). Дата прогона: фиксируется при сборке handoff'а; отчёт axe требует подтверждения после фикса pa11y.config (audit P1-12, Sprint 1 Phase C cc6e1cb обновил пути на liderra_v8_handoff/concepts/v8_*.html). Перепрогон: npm run a11y из корня репозитория.

6.1. Кабинет тенанта (8 экранов)

ID URL HTML Что внутри
01 /login, /register, /2fa, /forgot-password v8_login.html 5 состояний: login/register с zxcvbn-индикатором/2FA с 6-cell кодом/forgot/recovery с 8 одноразовыми кодами
02 /dashboard v8_dashboard.html KPI strip + Hero balance + Activity chart + Funnel-bar + Recent deals + System health + Quick actions
03 /deals v8_deals.html Таблица 14 строк × 14 status-chips, фильтры (search + selects + density toggle), drawer открыт статически
04 /deals/{id} v8_deal_card.html Hero + параметры + комментарий с 8 шаблонами + напоминание + footer-actions; aside: менеджер + метрики + history
05 /deals/kanban v8_kanban.html 14 колонок DnD (262px каждая), карточки с иконками 🔥//✓
06 /billing v8_billing.html Кошелёк ₽ + ГЦК + тариф; pending-banner; история транзакций; счета и УПД (1С 8.3 XML)
07 /settings v8_settings.html 8 вкладок (Профиль/Безопасность/Проекты/Команда/API/Интеграции/Тихие часы/Уведомления); 3 раскрыты
08 /reports v8_reports.html Форма запроса + список jobs (done/running с progressbar/queued/failed + retry); квота 2/3

6.2. Экраны ошибок (3)

Все в одном файле с review-tabs: v8_errors.html

ID URL Особенности
09 404 Декоративный SVG с маркером, кнопки «На дашборд» / «Назад»
10 403 Replace-key SVG, REQ-id для копирования, mailto support
11 500/503 Status-list (API/Telegram/YooKassa OK/деградация), INC-id, ссылка на status page

6.3. Админка SaaS (6)

Все в одном файле: v8_admin.html. Отдельный домен admin.liderra.app. Тёмная плашка ADMIN рядом с лого.

ID URL Что показано
12 /admin/login SSO Yandex 360 primary + локальный 2FA fallback
13 /admin/tenants Список 142 тенанта, фильтры, статус-чипы (active/trial/overdue/suspended)
14 /admin/tenants/{id} Карточка тенанта: профиль, баланс, действия (impersonation, корректировка, удаление 152-ФЗ), журнал
15 Impersonation-режим Красный sticky banner + специальная аватарка Админ → ИП + крест выхода
16 /admin/incidents Журнал HIGH/MED/LOW, read-only для compliance
17 /admin/system 6 sys-cards: system_settings / tariff_plans / legal_entities / payment_gateways / webhook_log / auth_log

6.4. Лендинг и юр. (4)

Все в одном файле: v8_landing.html

ID URL Особенности
18 / Hero с 3 hi-cards справа + 6 features + 4 tariffs + FAQ + footer (содержимое — Lorem ipsum)
19 /pricing Только tariffs (4 карточки: Start/Basic/Команда POPULAR/Enterprise)
20 /legal/offer TOC + 8 разделов оферты (шаблон Прил. Ж)
21 /legal/privacy TOC + 10 разделов 152-ФЗ Politики

6.5. Бренд-документация

ID Файл Что
BR-1 v8_brand.html 10 имён + 5 знаков + 3 favourite combo. Архив выбора.
BR-2 v8_palette_options.html 5 палитр на выбор (А Graphite/Б Forest/В Slate-Blue/Г Champagne/Д Inverted Hero). Архив.

7. Соглашения по доступности (WCAG 2.1 AA)

7.1. Что обязательно

  • Все интерактивные (<a>, <button>, <input>, <select>, <textarea>, [tabindex]) имеют :focus-visible с outline:

    a:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px;
      border-radius: var(--r-sm);
    }
    
  • Все SVG-иконкиaria-hidden="true" (декоративные)

  • Все icon-only кнопкиaria-label="..."

  • Все form-inputs<label for="..."> или aria-label

  • Selection state — solid fill (см. row.selected на dark --ink), не tint

  • role="tablist" требует все children = <button role="tab"> (не просто <button>)

  • prefers-reduced-motion — выключает анимации scoped через class .has-motion, не через *:

    @media (prefers-reduced-motion: reduce) {
      .has-motion { animation: none !important; transition: none !important; }
    }
    
  • Skip link на каждой странице для клавиатурной навигации:

    <a href="#main" class="skip-link">К контенту</a>
    

7.2. Контраст — проверка перед мержем

Перед каждым PR прогнать через axe DevTools или Playwright + axe-core 4.10.2. 0 violations — обязательное условие для merge. Дата прогона: фиксируется при сборке handoff'а; отчёт axe требует подтверждения после фикса pa11y.config (audit P1-12, Sprint 1 Phase C cc6e1cb обновил пути на liderra_v8_handoff/concepts/v8_*.html). Перепрогон: npm run a11y из корня репозитория.

7.3. Известные «проблемные» комбинации

Эти пары давали contrast violations в процессе и были исправлены — повторять НЕ нужно:

Не делать Контраст Решение
--ink-disabled на --surface для текста ≥9px 3.18:1 --ink-3 (5.42:1)
--st-quote-solid (#008A4D) на белом для 11.5px text 4.36:1 #006A3B + bold (4.5:1+)
Текст 10px на solid --st-neg-solid / --st-quote-solid / --st-paid-solid 4.14-4.33:1 Убрать текст из solid-сегмента, перенести в подпись
aria-hidden="true" на <aside> с фокусируемыми ссылками aria-hidden-focus Заменить на aria-label="..."
<div> с aria-label без role aria-prohibited-attr Добавить role="progressbar" / role="img"
5 tabs в display:flex без flex-wrap на mobile Overflow → axe не видит true bg Добавить flex-wrap: wrap

8. Vue 3 + Vuetify 3 — практические подсказки

8.1. Где брать Vuetify-компонент vs кастомный

Vuetify-готовый Полу-кастомный Полностью кастомный
v-text-field, v-select, v-checkbox, v-snackbar, v-dialog, v-menu v-data-table-server (с правкой стилей под наши токены) StatusChip с 14 вариантами, BalanceWidget с runway, Timeline, Sidebar nav

8.2. Vuetify тема через токены

В vuetify.options.ts:

import 'vuetify/styles';
export default {
  theme: {
    defaultTheme: 'liderra',
    themes: {
      liderra: {
        dark: false,
        colors: {
          primary: '#0F6E56',
          'on-primary': '#FFFDFA',
          surface: '#FFFDFA',
          background: '#F6F3EC',
          error: '#B94837',
          warning: '#9A6700',
          success: '#008A4D',
          info: '#007EB8',
        },
      },
    },
  },
};

Дополнительно в assets/tokens.css подключить ВСЕ переменные из §3 — Vuetify не покрывает status-палитру, sidebar tokens и др.

8.3. Шаблон страницы

<template>
  <AppShell>
    <template #topbar-crumb>
      <span>Рабочая область</span>
      <ChevronIcon />
      <strong>{{ pageTitle }}</strong>
    </template>
    <main id="main" class="content">
      <PageHeader :title="pageTitle" :stats="pageStats" />
      <!-- секции -->
    </main>
  </AppShell>
</template>

AppShell инкапсулирует .app + .side + .topbar + .main. Детальная реализация — отдельный issue.


9. Известные правила проекта (нельзя оспаривать)

Из брифа 01_Scope_i_ekrany.md §6:

  • Тёмная тема — отложена на v2 (CSS-vars для будущей dark theme заложены, но макетов нет)
  • 2FA — на всех тарифах, включая бесплатный
  • Минимум 100 ₽ пополнения, округление вниз при ₽→лиды
  • Pending-платеж — нет кнопки «Отменить» (CTO-11), самовосстановление 30 минут
  • Click-wrap при регистрации — 3 чекбокса (оферта / ПДн / маркетинг-опционально)
  • Yandex Cloud + Yandex 360 SSO для админки
  • JivoSite для helpdesk-чата (Биз-5)
  • Webhook outbound — primary integration option, REST API на втором месте
  • amoCRM — спринт 14-15, Bitrix24/RetailCRM — Post-MVP

10. Что точно НЕ делать на MVP

  • Темная тема UI
  • Wizard «OSINT-рекомендации источников» (Биз-15)
  • Телефонная интеграция (UI VoIP) — Биз-12
  • Промокоды и реферальная программа — Б-6
  • Tab «Удалённые проекты» с покупкой обратно — Биз-14
  • Bitrix24 / RetailCRM коннекторы — только amoCRM
  • Кнопка «Отменить» pending-платежа — CTO-11
  • Кнопка «Отменить подписку» — уход через смену тарифа на Start (Биз-2)

11. API-контракт (намёки для backend)

Это не полный контракт (он out-of-scope этого документа), но есть конкретные требования из дизайна:

11.1. WebSocket / SSE для real-time

Дашборд показывает «LIVE» индикатор на balance widget — это требует push-канала. Минимум: lead.created, payment.completed, balance.updated.

11.2. Activity Log

Каждое изменение поля карточки сделки — запись в deal_activity_log с полями:

{
  id: string,
  deal_id: string,
  actor_id: string | null,         // менеджер или null = system
  type: 'create'|'status-change'|'comment'|'call'|'tag'|'assign'|'reminder',
  timestamp: ISO8601,
  payload: {
    field?: string,                 // 'status' | 'manager_id' | ...
    from?: any,                     // old value
    to?: any,                       // new value
    text?: string,                  // for comments
    duration_sec?: number,          // for calls
  }
}

11.3. Saas Admin Audit Log

Согласно бриф §23.10 + дизайн админки — каждое действие админа пишется в saas_admin_audit_log. Impersonation-режим имеет обязательное поле reason при входе.

11.4. RLS для multi-tenant

Каждый запрос API проверяет tenant_id через RLS на стороне БД. Bypass attempt = инцидент HIGH severity (см. админка/Incidents).

11.5. Webhook signatures

Outbound webhook подписывается HMAC SHA-256 в заголовке X-Liderra-Sign. Документация для клиентского интегратора — отдельный документ.

11.6. Status проекта

GET /status → { api: 'OK' | 'degraded' | 'down', integrations: [...] }

Используется на 500-странице.


12. Тестирование

12.1. axe-core 4.10.2 — обязательно

Прогон через CI на каждом PR. Pseudo-pipeline:

playwright test --headed
# в playwright тесте:
await page.evaluate(() => axe.run(document, { runOnly: ['wcag2a','wcag2aa','wcag21aa'] }))
# violations.length должен быть 0

12.2. Viewport coverage

Все экраны проверять как минимум на 4 viewports:

  • 1680×1050 — desktop large
  • 1440×900 — desktop стандарт
  • 768×1024 — tablet
  • 375×812 — mobile (iPhone X reference)

12.3. Reduced motion

Включить prefers-reduced-motion: reduce в DevTools → проверить, что декоративные анимации (pulse, slide) выключены, но focus rings и hover transitions работают.

12.4. Keyboard-only навигация

Tab через всю страницу — все интерактивные элементы должны иметь видимый focus ring.


13. Полезные команды для разработчика

13.1. Локальный preview макетов

cd дизайн
python -m http.server 8765
# открыть http://localhost:8765/concepts/v8_dashboard.html

13.2. Прогон axe для проверки

// В DevTools console на любой странице:
const s = document.createElement('script');
s.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
document.head.appendChild(s);
s.onload = async () => {
  const r = await axe.run(document, { runOnly: ['wcag2a','wcag2aa','wcag21aa'] });
  console.log('Violations:', r.violations.length, r.violations);
  console.log('Passes:', r.passes.length);
};

13.3. OKLCH палитра — ещё пары

Если нужно добавить новый статус — использовать palette_14.py с другим start_hue и repulsion-итерацией. min ΔE2000 ≥ 10 обязательно.

cd дизайн
PYTHONIOENCODING=utf-8 python palette_14.py

14. FAQ

Q: Можно ли использовать Tailwind вместо чистого CSS? A: Можно, но токены из §3 должны попасть в tailwind.config.ts. Проще импортировать tokens.css глобально и использовать классы из v8_*.html напрямую (они стабильные).

Q: Что делать, если нужен новый статус (15-й)? A: Перезапустить palette_14.py с N=15, ΔE2000 пересчитается. Все 14 существующих останутся, новый встанет в свободное место hue-кругa. Дизайнер должен утвердить (новый статус = новый workflow в БД).

Q: Можно ли поменять Teal #0F6E56 на синий? A: НЕТ без явного OK заказчика — это брендовый цвет (зафиксирован Диз-2). Если нужно — сначала переоткрыть бренд-решение.

Q: ApexCharts не дружит с opsz axis Inter? A: Использовать font-family fallback: ApexCharts читает font-family: 'Inter' без opsz, выглядит чуть-чуть толще, но не критично.

Q: Как повторить OKLCH-расчёт? A: Python 3.12+ с pip install colour-science numpy. Скрипты в palette_*.py в корне дизайн/.

Q: Где живёт реальный source of truth для брендбука? A: Этот файл (docs/DEVELOPER_HANDOFF.md). Файл дизайн/02_Brand_i_A11y.md устарел (там про v1.1 «Лидпоток»).


15. Контакты

Роль Контакт
Дизайнер kpd9363@gmail.com (Платон)
Поддержка дизайна через данный handoff + concepts/v8_*.html
Заказчик Дмитрий

16. Изменения

Версия Дата Что изменилось
v8 Forest 2026-05-07 Полная переработка: Лидерра, Forest палитра, L-Square лого, 25 макетов
v7 cool-tech 2026-05-07 Деприкейтнут (cool-grey слишком белый, sidebar сливался)
v4-v6 до 2026-05-07 Архивы итераций — не использовать

Конец DEVELOPER_HANDOFF.md v8 Forest.