Ресёрч: charts-библиотека + UI дашбордов аналитики (MLF-289, шаг 1)

Задача: MLF-289 — «Аналитика: фундамент — charts-стек (recharts) + workspace-изоляция дашборда». Это шаг 1 карточки (ресёрч); результат ложится в шаг 2 (макеты базовых графиков + оболочки дашборда на дизайн-токенах), затем — согласование с М. Терентьевым (без апрува в разработку НЕ берём) и ADR-решение по charts-стеку.

Дата: 2026-07-07 Метод: deep-research harness (6 углов → 18 источников → 72 факта → adversarial-верификация 25 ключевых: 17 подтверждено / 8 опровергнуто голосованием) + сверка с live-данными npm registry. Скоуп: только фундамент — выбор charts-библиотеки под наш стек и канон дашборда рассылок (KPI-плитки, воронка доставок, sparkline). Конкретные метрики CDP, drill-down, сегменты — вне ресёрча (это последующие карточки).

Достоверность. Первичные источники (release-notes Recharts v3, docs Recharts, tremor.so, блог Vercel, docs Twilio/SendGrid, shadcn/ui, npm API) — верифицированы. Блоговые сравнения (pkgpulse, logrocket, querio, chartts, chenguangliang) давали противоречивые абсолютные цифры бандлов и a11y-рейтинги — 8 таких claim-ов опровергнуты голосованием верификаторов (см. §6). Поэтому ниже размеры бандлов приводятся как порядок величины, а не точные KB — точную цифру для нашего проекта надо мерить эмпирически (§7, открытый вопрос).


1. TL;DR — рекомендация

  1. Выбор recharts из карточки — обоснован. Для стека Next.js 15 (App Router/RSC) + React 19 + Tailwind 3.4 это дефолт под базовые дашборд-графики: самая массовая React- charts-библиотека (лидер npm с большим отрывом), SVG-рендер даёт сравнительно лучшую SSR/RSC-историю, чем canvas-библиотеки (Chart.js, ECharts), нативно ложится на дизайн- токены в стиле shadcn/ui (var(--chart-N)), а в v3 доступность включена по умолчанию.

  2. Ключевой довод под нашу задачу — нативный funnel. У Recharts есть first-class FunnelChart / Funnel — воронку доставок можно собрать без кастомного графика, покраска стадий через <Cell>. У главной альтернативы (Tremor) нативного funnel нет.

  3. Tremor — не альтернатива, а обёртка над тем же Recharts (+ Radix UI). Выбрать Tremor = выбрать Recharts под капотом, но с меньшей кастомизируемостью и без funnel. Плюс — после покупки Vercel (янв 2025) проект переориентирован на экосистему Vercel/v0 (риск-фактор для «самостоятельной поддержки»). → берём сырой Recharts, а полезные идеи Tremor (Spark-графики для KPI) воспроизводим на Recharts сами.

  4. Расплата — бандл не маленький (Recharts тянет d3-субмодули; один график уже подтягивает бóльшую часть библиотеки, tree-shaking выигрыш ограничен). Для базовых line/bar/funnel/ sparkline это приемлемо; для будущих тяжёлых сценариев (тысячи точек, real-time) — переоценить.

  5. Канон дашборда рассылок устойчив: ряд KPI-плиток (Processed/Delivered, Bounce, Opens) + воронка sent → delivered → opened → clicked → converted. Подтверждено первичным источником SendGrid и рядом vendor-материалов. Нюанс: отраслевая воронка обычно не включает bounce как стадию — bounce это ветвь оттока/отдельная KPI, а не узел воронки (расходится с формулировкой «sent/delivered/open/click/bounce» из карточки, см. §5.2).


2. Сравнение библиотек (подтверждено)

2.1 Recharts — рекомендуемый дефолт

Источники: pkgpulse recharts-v3-vs-tremor-vs-nivo, shadcn/ui Chart, logrocket best-react-chart-libraries-2026, npm registry API.

2.2 Tremor — обёртка, а не конкурент

tremor.so дословно: «Built on Recharts and Radix UI», «35+ fully open-source, accessible components for dashboards and charts» (Spark Charts, Bar Lists, Data Bars, Tracker, Progress Circles; встроенный dark-mode). Из этого следует:

Вывод для ADR: не тащить Tremor как зависимость. Spark-варианты (sparkline для KPI) — воспроизвести на Recharts (мини LineChart/AreaChart без осей), не добавляя второй charts-стек.

Источники: tremor.so, vercel.com/blog/vercel-acquires-tremor, tremor/issues/140.

2.3 visx / D3 — меньший бандл ценой ручного кода

visx — набор 30+ модульных пакетов; минимальный бандл достигается только при selective-импорте отдельных модулей и написании кастомного low-level кода (полный @visx/visx — крупный). Оправдан, когда нужен полный контроль над рендером/интеракциями; для базовых line/bar/funnel/ sparkline — избыточная инженерная стоимость.

Источники: visx, pkgpulse visx-vs-recharts.

2.4 Chart.js / ECharts — canvas-first

Canvas-рендер хуже ложится на SSR/RSC-историю Next 15 App Router (см. §2.1). Не выбираем.


3. Funnel + sparkline на Recharts (подтверждено)

Funnel (воронка доставок). Официальная docs Recharts документирует FunnelChart (контейнер) и Funnel. Паттерн: import { FunnelChart, Funnel, Tooltip, Cell } from 'recharts'; data — массив объектов, dataKey выбирает поле значения, per-stage цвета через <Cell>. Этого достаточно для воронки sent/delivered/open/click с покраской стадий из brand-палитры — без кастомной реализации. Это ключевой довод против Tremor.

Источники: Funnel API, FunnelChart API.

Sparkline (KPI-плитки). Отдельного компонента нет — делается мини-LineChart/AreaChart без осей/сетки/тултипа в маленьком ResponsiveContainer. Это ровно то, что Tremor оборачивает в SparkAreaChart поверх того же Recharts — воспроизводим сами, чтобы не плодить зависимости (открытый вопрос §7).


4. Доступность (a11y) (подтверждено)

Recharts v3 включает accessibilityLayer по умолчанию на всех cartesian/polar графиках (клавиатурная навигация стрелками, aria-роли/лейблы); отключается accessibilityLayer={false}. Первичный источник — release-notes v3.0.0 (23 июня 2025): «accessibilityLayer is now on on all polar and cartesian charts by default», «Accessibility by default» вынесено в заголовок релиза. В v2.x дефолт был false — то есть берём v3.

Опровергнуто ресёрчем: блоговый рейтинг «Nivo Excellent / Recharts Partial» по a11y (0-3) — не опираться на него; сравнительные a11y-«оценки» из листиклов недостоверны.

Источники: release v3.0.0, logrocket.


5. UI дашбордов рассылок — канон под макеты (подтверждено)

5.1 KPI-плитки — референс SendGrid

Официальная docs Twilio/SendGrid (Deliverability Insights → Overview) выводит четыре первичные метрики:

Это прямой референс набора KPI-плиток для нашего дашборда. Sparkline-тренд логично навесить на каждую плитку (§3).

Источник: SendGrid Deliverability Insights.

5.2 Воронка доставок — прогрессия и нюанс с bounce

Три независимых источника согласованно описывают одну прогрессию: sent → delivered → opened → clicked → converted/purchases (с процентами drop-off между стадиями). Mailchimp Marketing dashboard имеет funnel-секцию «how campaign sends convert into opens, clicks, and orders».

Нюанс для макетов и ADR: отраслевой канон воронки завершается на converted/purchases и НЕ включает bounce как стадию. Bounce — это ветвь оттока между sent и delivered (и/или отдельная KPI-плитка), а не хвостовой узел funnel. Формулировка карточки «sent/delivered/open/click/bounce» это смешивает. → развилка на согласование (§8, вопрос 1): классический funnel до converted + bounce/complaint отдельными KPI, либо funnel с явной веткой оттока.

Источники: coupler funnel-analysis, coupler email-marketing-dashboard, mailtrap best-email-marketing-dashboards.

5.3 Привязка к нашим дизайн-токенам

Под макеты (шаг 2) chart-палитру завести на brand-токенах из tailwind.config.ts: brand.accent #02CBEF (акцент/основная серия), brand.dark #0F133A (тёмный фон/оси/текст), rounded-card 16px (оболочка карточек графиков), шрифты Unbounded (заголовки) / Golos Text (UI). Механизм — CSS-переменные графиков в духе shadcn/ui (--chart-1..N), покрыть light/dark (§7).


6. Опровергнуто ресёрчем (не тащить в ADR)

Голосование верификаторов (нужно 2/3 «refute») отклонило конкретику из блогов:

Вывод: абсолютные размеры бандлов и сравнительные a11y/SSR-«оценки» из листиклов в ADR не переносить; приводить порядок величин и мерить бандл эмпирически.


7. Открытые вопросы (эмпирика перед/во время реализации)

  1. Реальный бандл Recharts v3 в нашем проекте (Next 15 tree-shaking, только line/bar/funnel/sparkline) — измерить через npm run build / bundle-analyzer, не полагаясь на противоречивые блоги.
  2. Sparkline: нативный Recharts (мини-LineChart без осей) — подтвердить, что не тянем Tremor ради SparkAreaChart (иначе дублирование зависимостей).
  3. Brand-палитра ↔ chart-токены: определить --chart-1..N под #02CBEF/#0F133A, покрыть light/dark.

8. Вопросы для согласования с М. Терентьевым (перед макетами)

  1. Bounce в воронке (§5.2): классический funnel до converted + bounce/complaint отдельными KPI-плитками, или funnel с явной веткой оттока sent→bounce?
  2. Набор KPI-плиток: берём набор SendGrid (Processed / Delivered / Bounce & Blocked / Unique Opens) как базовый, или свой?
  3. Sparkline в плитках: нужен ли тренд-sparkline на каждой KPI-плитке в MVP, или только числа + воронка + основной line/bar?
  4. Стадии воронки: sent → delivered → opened → clicked → converted — все пять в MVP, или без converted (пока нет revenue-события)?

9. Ограничения ресёрча

10. Источники

Библиотеки (первичные): Recharts Funnel API · FunnelChart API · Recharts release v3.0.0 · shadcn/ui Chart · tremor.so · Vercel acquires Tremor · tremor/issues/140 · visx · npm registry API Библиотеки (сравнения, блоги): pkgpulse recharts-v3-vs-tremor-vs-nivo · pkgpulse recharts-vs-chartjs-vs-nivo-vs-visx · logrocket · querio · chartts · chenguangliang Дашборды рассылок: SendGrid Deliverability Insights (первичный) · coupler funnel-analysis · coupler dashboard · mailtrap · improvado · thoughtspot


11. Что дальше (шаг 2 карточки)

На основе этого ресёрча собрать интерактивный макет (оболочка дашборда + базовые графики line/bar/funnel/sparkline на brand-токенах), отправить М. Терентьеву на согласование (без апрува макетов в разработку не берём). Заготовка ADR-решения по charts-стеку: recharts v3, сырой (не Tremor), chart-палитра на CSS-переменных под brand, бандл замерить эмпирически.