Задача: 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, открытый вопрос).
Выбор 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 доступность включена по умолчанию.
Ключевой довод под нашу задачу — нативный funnel. У Recharts есть first-class
FunnelChart / Funnel — воронку доставок можно собрать без кастомного графика, покраска
стадий через <Cell>. У главной альтернативы (Tremor) нативного funnel нет.
Tremor — не альтернатива, а обёртка над тем же Recharts (+ Radix UI). Выбрать Tremor = выбрать Recharts под капотом, но с меньшей кастомизируемостью и без funnel. Плюс — после покупки Vercel (янв 2025) проект переориентирован на экосистему Vercel/v0 (риск-фактор для «самостоятельной поддержки»). → берём сырой Recharts, а полезные идеи Tremor (Spark-графики для KPI) воспроизводим на Recharts сами.
Расплата — бандл не маленький (Recharts тянет d3-субмодули; один график уже подтягивает бóльшую часть библиотеки, tree-shaking выигрыш ограничен). Для базовых line/bar/funnel/ sparkline это приемлемо; для будущих тяжёлых сценариев (тысячи точек, real-time) — переоценить.
Канон дашборда рассылок устойчив: ряд KPI-плиток (Processed/Delivered, Bounce, Opens) + воронка sent → delivered → opened → clicked → converted. Подтверждено первичным источником SendGrid и рядом vendor-материалов. Нюанс: отраслевая воронка обычно не включает bounce как стадию — bounce это ветвь оттока/отдельная KPI, а не узел воронки (расходится с формулировкой «sent/delivered/open/click/bounce» из карточки, см. §5.2).
Массовость: live-данные npm registry (неделя 2026-06-29→07-05) — recharts ~51M/нед
против @nivo/core ~1.5M и @tremor/react ~0.3M. Порядок recharts >> nivo >> tremor
устойчив. (Блоговые цифры 2.4M/3.6M — занижены/устарели; направление «лидер с отрывом» верно.)
D3 без монолита: тянет не весь d3, а отдельные d3-субмодули через victory-vendor
(проверено по package.json [email protected]).
Дизайн-токены: shadcn/ui Chart построен прямо на Recharts и темизируется через
CSS-переменные Tailwind (var(--chart-1..5)) — первичный источник, не маркетинг. Прямой путь
завести нашу brand-палитру как chart-токены.
SSR/RSC: SVG-рендер даёт сравнительное преимущество перед canvas (Chart.js, ECharts). Косвенно подтверждено даже хендбуком ECharts (canvas-first библиотека признаёт приоритет SVG для server-side).
Оговорка (важно для реализации): это преимущество только сравнительное. Recharts всё равно требует
'use client'и имеет задокументированные SSR-квирки (ResponsiveContainerизмеряет DOM на клиенте, issue #4336). «Сильной SSR-истории» сам по себе Recharts не даёт — графики в нашем App Router будут client-компонентами.
Источники: pkgpulse recharts-v3-vs-tremor-vs-nivo, shadcn/ui Chart, logrocket best-react-chart-libraries-2026, npm registry API.
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.
visx — набор 30+ модульных пакетов; минимальный бандл достигается только при selective-импорте
отдельных модулей и написании кастомного low-level кода (полный @visx/visx — крупный).
Оправдан, когда нужен полный контроль над рендером/интеракциями; для базовых line/bar/funnel/
sparkline — избыточная инженерная стоимость.
Источники: visx, pkgpulse visx-vs-recharts.
Canvas-рендер хуже ложится на SSR/RSC-историю Next 15 App Router (см. §2.1). Не выбираем.
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).
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.
Официальная docs Twilio/SendGrid (Deliverability Insights → Overview) выводит четыре первичные метрики:
Это прямой референс набора KPI-плиток для нашего дашборда. Sparkline-тренд логично навесить на каждую плитку (§3).
Источник: SendGrid Deliverability Insights.
Три независимых источника согласованно описывают одну прогрессию: 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.
Под макеты (шаг 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).
Голосование верификаторов (нужно 2/3 «refute») отклонило конкретику из блогов:
→ Вывод: абсолютные размеры бандлов и сравнительные a11y/SSR-«оценки» из листиклов в ADR не переносить; приводить порядок величин и мерить бандл эмпирически.
npm run build / bundle-analyzer, не полагаясь
на противоречивые блоги.SparkAreaChart (иначе дублирование зависимостей).--chart-1..N под #02CBEF/#0F133A,
покрыть light/dark.Библиотеки (первичные): 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
На основе этого ресёрча собрать интерактивный макет (оболочка дашборда + базовые графики line/bar/funnel/sparkline на brand-токенах), отправить М. Терентьеву на согласование (без апрува макетов в разработку не берём). Заготовка ADR-решения по charts-стеку: recharts v3, сырой (не Tremor), chart-палитра на CSS-переменных под brand, бандл замерить эмпирически.