Графики · Column Chart

Column Chart

Вертикальные столбцы для упорядоченных категорий – временных интервалов или рейтингов. Дисциплина data-ink (Тафти): один акцент, без сетки, моноширинные цифры, прямые подписи значений по умолчанию, встроенная строка источника и доступные состояния пустоты / загрузки / ошибки.

Studio · Код, график, настройки

Трёхпанельный верстак. Меняйте любую настройку справа – график в центре обновляется вживую, а код слева перегенерируется, готовый к вставке в ваш проект.

Код
import { ColumnChart } from "@/components/charts/column-chart";

const data = [142, 168, 187, 159, 203, 178, 215];
const labels = ["ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС"];

export function Example() {
  return (
    <ColumnChart
      data={data}
      labels={labels}
      height={240}
      gap={4}
      header={{ title: "Активные пользователи", subtitle: "Последние 7 дней" }}
      trend={0.184}
      source="Brock Analytics, 2026"
      onBarClick={(point, index) => {
        console.log("clicked", index, point);
      }}
      onBarHover={(point, index) => {
        // point is null on mouse leave
        setHoverIndex(index);
      }}
      onBarFocus={(point, index) => {
        // Fires on keyboard navigation
        announce(`Bar ${index + 1}: ${point.value}`);
      }}
      exportable
      exportFileName="active-users-7d"
    />
  );
}
График
Активные пользователи
Последние 7 дней
Source: Brock Analytics, 2026
Column chart with 7 data points. Highest: ВС (215); lowest: ПН (142). Source: Brock Analytics, 2026.
Data table.
LabelValue
ПН142
ВТ168
СР187
ЧТ159
ПТ203
СБ178
ВС215

Установка

npx shadcn@latest add https://brockui.com/r/column-chart

Использование

import { ColumnChart } from "@/components/charts/column-chart";

const data = [142, 168, 187, 159, 203, 178, 215];
const labels = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"];

export function Example() {
  return (
    <ColumnChart
      data={data}
      labels={labels}
      height={220}
      trend={0.184}
      source="Brock Analytics, 2026"
    />
  );
}

// Opinionated defaults — but every sub-component is yours to replace via the
// `slots` prop. The data is passed in; you control the rendering.
export function WithCustomTooltip() {
  return (
    <ColumnChart
      data={data}
      labels={labels}
      slots={{
        tooltip: ({ label, value }) => (
          <div className="border-2 border-brock-accent p-2">
            <span className="font-mono tabular-nums">{value}</span> · {label}
          </div>
        ),
      }}
    />
  );
}

Пропы

ИмяТипПо умолчаниюОписание
datanumber[] | DataPoint[]Значения столбцов. Две формы: number[] (с пропом labels) или { key?, label?, value, meta?, pattern?, color?, highlight?, note? }[] (объектная форма). key – стабильный адрес для аннотаций и focusBar (по умолчанию label); meta – ваши данные, возвращаются нетронутыми в каждом коллбэке; отрицательные значения рендерятся ниже нулевой базы. Синтетический столбец «Other» несёт isOther + items[] (только на выходе)
sort'none' | 'asc' | 'desc''none'Сортировка столбцов по значению (стабильная). 'none' сохраняет исходный порядок – честный дефолт для временных интервалов; asc/desc превращает график в рейтинг
topNnumber | { n, label?, pinned?, distinct? }undefinedОставить N крупнейших столбцов, свернув хвост в один агрегат «Other» (сумма). По умолчанию: закреплён последним независимо от sort, приглушённая заливка --brock-other. Коллбэки получают isOther + свёрнутые items[]. Число = все дефолты
labelsstring[]undefinedПодписи оси X (Hack mono под столбцами + в тултипе). Используются только при data: number[]
heightnumber200Высота графика в пикселях (ось Y + область столбцов)
gapnumber4Зазор между столбцами в пикселях. Сужается автоматически на плотных данных (60+ столбцов)
accentstringvar(--brock-accent)Переопределение цвета заливки (любой CSS-цвет или var). По умолчанию – оранжевый Brock
barRadiusnumber0Радиус верхних углов в px. Типичные значения: 0 (острые), 2 (едва заметный), 6 (скруглённые)
header{ title?, subtitle? }undefinedБлок заголовка и подзаголовка над графиком
xAxis{ title?, hideTicks? }undefinedНастройка оси X (заголовок под подписями, скрытие подписей делений)
yAxis{ title?, max?, hideTicks? }undefinedНастройка оси Y. min намеренно отсутствует – база столбчатого графика всегда ноль (обрезанные столбцы лгут). max только расширяет шкалу: значения ниже максимума данных игнорируются с предупреждением в dev
numberFormat{ prefix?, suffix?, decimals?, locale?, notation?, style?, currency? }undefinedФорматирование чисел для оси Y, тултипа и подписей значений. Поддерживает локаль BCP-47, notation Intl.NumberFormat ('compact' → 1,2 тыс.), style ('currency' / 'percent') и валюту ISO 4217. Явные formatValue/yAxisFormat сильнее
dataLabels{ show?: boolean | 'auto', format? }{ show: 'auto' }Прямые подписи значений у внешнего конца каждого столбца (Hack mono; у отрицательных – зеркально снизу). 'auto' (дефолт) показывает подписи И скрывает ось Y при ≤ 8 столбцах – ось избыточна, когда каждое значение напечатано. Явный yAxis.hideTicks сильнее. format(value, datum) переопределяет numberFormat
pattern'solid' | 'hatched''solid'Заливка всех столбцов по умолчанию. Per-bar pattern на точке данных сильнее. Штриховка кодирует «историческое/оценка/в работе» без второго цвета (Тафти)
hatchUntilIndexnumberundefinedШорткат: столбцы с ИСХОДНЫМ индексом < N штрихуются (применяется до sort/topN – паттерн едет со своим датумом). Классическое «факт vs прогноз»
hatchFromIndexnumberundefinedЗеркало hatchUntilIndex: штрихуются столбцы с индексом >= N. Удобно для прогнозных хвостов. Совмещается с hatchUntilIndex (объединение)
patternStyle'diagonal' | 'diagonal-reverse' | 'dots' | 'vertical' | 'horizontal''diagonal'Вид штриховки (на уровне графика). Per-bar pattern решает, штрихуется ли столбец; этот проп – как выглядит штриховка. 'dots' – для печати и оттенков серого
scroll'none' | 'auto''none'Поведение при нехватке ширины. 'auto' включает горизонтальный скролл; ось Y прижата слева, столбцы и ось X скроллятся вместе
minBarWidthnumber4Минимальная ширина столбца в px. Вместе со scroll='auto' задаёт min-width графика: N*minBarWidth + (N−1)*gap. При scroll='none' игнорируется
bands{ from, to, label?, color? }[]undefinedПлот-бенды – подсвеченные вертикальные зоны по ЭКРАННЫМ позициям. Редакционный паттерн («Q3», «окно деплоя»). Рисуются за столбцами с низкой непрозрачностью; индексы ограничиваются диапазоном данных. Бенды предполагают исходный порядок – сочетание с sort даёт предупреждение в dev (зона «Q3» после пересортировки бессмысленна)
trendnumberundefinedДесятичный индикатор тренда: 0.184 → ↗ +18.4%. Оранжевый при росте, приглушённый при падении
referenceLine{ value: number | { stat: 'mean' | 'median' }, label? }undefinedПунктирная референсная линия – фиксированный порог («цель Q3») или статистика по ИСХОДНЫМ данным (sort/topN не должны двигать статистику). Статистики подписываются Mean/Median автоматически. Участвует в шкале с обеих сторон – отрицательные и нулевые (break-even) референсы остаются видимыми
sourcestringundefinedСтрока атрибуции под графиком (паттерн FT)
animation{ enabled?, duration? }{ enabled: true, duration: 400 }Каскадный подъём столбцов при монтировании. Автоматически отключается при prefers-reduced-motion
loadingbooleanfalseСостояние загрузки. Без данных → полный скелетон (сплошные столбцы-плейсхолдеры + доступная подпись загрузки, ARIA role=status). С данными → полупрозрачный оверлей со спиннером для фонового обновления. Уважает prefers-reduced-motion
errorError | string | nullnullТерминальная ошибка. Замещает график даже при наличии данных (устаревшие данные рядом с ошибкой вводят в заблуждение). Принимает Error, строку или null. ARIA role=alert
onRetry() => voidundefinedКоллбэк кнопки повтора в дефолтном состоянии ошибки. Кнопка рендерится только когда проп передан
loadingLabelstring'Loading…'Доступная подпись состояния загрузки (скелетона), используется как его ARIA-метка. Переопределите для локализации
errorLabelstring'Error'Подпись над сообщением об ошибке и ARIA-метка. Переопределите для локализации
retryLabelstring'Retry'Надпись на кнопке повтора в дефолтном состоянии ошибки. Переопределите для локализации
loadingFallbackReactNodeundefinedПолная замена дефолтного скелетона и оверлея. Для брендированного состояния загрузки
errorFallbackReactNode | (error: Error) => ReactNodeundefinedПолная замена дефолтного UI ошибки. React-нода или функция, получающая нормализованный Error
exportableboolean | { png?, svg?, csv?, copy? }falseПоказать тулбар экспорта (справа сверху). true – все 4 действия; объектная форма – выборочно. Императивные методы ref работают в любом случае
exportFileNamestring | (format) => string'chart'Базовое имя файла выгрузки. Строка – фиксированное, функция – по формату. Расширение (.png/.svg/.csv) добавляется автоматически
onExport(format, artifact) => voidundefinedСрабатывает после завершения экспорта. Получает формат ('png'|'svg'|'csv'|'copy') и артефакт (Blob для png/copy, строка для svg/csv). Для аналитики и своих share-сценариев
refRef<ColumnChartHandle>Императивный API: { exportSVG, exportPNG, exportCSV, copyImage, focusBar, getSelection }. Экспорт работает даже в loading/error/empty. focusBar(target) принимает экранный индекс (с ограничением) ИЛИ стабильный key (неизвестный key → -1). getSelection() возвращает { index (экранный), key (стабильный), point } или null
onBarClick(point, index, event) => voidundefinedСрабатывает на клик, тап или Enter/Space на сфокусированном столбце. Событие – MouseEvent или KeyboardEvent. При переданном пропе столбцы получают cursor-pointer
onBarHover(point | null, index | null) => voidundefinedСрабатывает при наведении (point + index) и при уходе из области столбцов (null, null). Синхронизируйте свои легенды и панели деталей
onBarFocus(point, index) => voidundefinedСрабатывает при смене клавиатурного фокуса между столбцами (стрелки, Home/End, Tab, программный focusBar()). Отслеживает позицию roving tabindex
slotsColumnChartSlots{}Словарь headless-слотов. Каждый слот заменяет дефолтный саб-компонент: tooltip (per-bar), empty (нет данных), loading (скелетон), error (терминальная), toolbar (чипы экспорта), caption (под источником), watermark (оверлей фигуры). Каждый слот получает типизированные пропы. Слоты сильнее шорткатов loadingFallback / errorFallback
captionstringundefinedКороткая редакционная подпись-примечание – курсив с левой границей, под строкой источника. Паттерн полей FT/писем Stripe. slots.caption сильнее
watermarkstringundefinedДиагональная вотермарка – едва заметный пиксельный текст (≈6% непрозрачности) поверх графика. Маркер жизненного цикла документа (DRAFT, CONFIDENTIAL) – не брендинг (для атрибуции есть source). Намеренно ПЕЧАТАЕТСЯ: бумажный конфиденциальный отчёт обязан нести маркировку. slots.watermark сильнее
annotationsColumnChartAnnotation[]undefinedСвободные редакционные аннотации в точке (x, y) пространства данных. x: число = ИСХОДНЫЙ индекс (едет со своим датумом сквозь sort/topN; если датум свернулся в «Other» – пропускается с предупреждением в dev) или строка = совпадение по key/label. y может быть отрицательным. { x, y, text, anchor?, arrow?, color? }; пунктирная стрелка опциональна. Воспроизводится в SVG/PNG-экспорте
chartTypestring'column'Машиночитаемый идентификатор, проставляется на фигуре как data-chart-type. Входит в toJSON(). AI/LLM-инструменты и аналитика используют его, чтобы понимать тип графика
dataDescriptionstringundefinedОписание данных на естественном языке («Дневная аудитория, последние 7 дней»). Проставляется как data-description. Для AI-промптов и редакционной провенанс. Не путать с `description` (авто-метка для скринридера)
data-testidstringundefinedQA-селектор, пробрасывается на фигуру. Стабилен при рефакторинге классов – конвенция Testing Library / Playwright
descriptionstringauto-generatedДоступное описание для скринридеров (figcaption + подпись таблицы). По умолчанию: 'Column chart with N data points. Source: ...'
formatValue(value, datum?) => stringtoLocaleString()Свой форматтер значений для тултипов, подписей и sr-таблицы. Вторым аргументом получает датум (key, meta, isOther…) для контекстного форматирования. Сильнее numberFormat
yAxisFormat(v: number) => stringtoLocaleStringФункция форматирования подписей делений оси Y. Сильнее numberFormat

Доступность

Построен по WCAG 2.2 AA. Клавиатурная навигация, поддержка скринридеров, уважает prefers-reduced-motion.

Честное ограничение: автогенерируемые ARIA-описания компонента («Column chart with 7 data points. Highest: …») – на английском. Если ваш продукт для русскоязычных пользователей, передайте собственный description и локализуйте loadingLabel/errorLabel/retryLabel – locale-pack компонента в планах.

Клавиатура
TabФокус внутрь графика (одна точка останова)
← → ↑ ↓Навигация между столбцами (roving tabindex)
HomeК первому столбцу
EndК последнему столбцу
Разметка для скринридера
  • · <figure role="figure"> оборачивает график; aria-labelledby указывает на figcaption
  • · Каждый столбец – role="graphics-symbol" с aria-label="LABEL: value"
  • · Скрытая <table class="sr-only"> даёт табличную сводку данных (подпись + строка на каждую точку)
  • · Индикатор тренда получает человекочитаемую метку («Trend up 18.4 percent») – стрелки скрыты от AT

Дизайн-ходы

  1. 1. Моноширинные числа оси Y + tabular-nums (шрифт Hack) – значения выравниваются по ширине цифры.
  2. 2. Один --brock-accent (оранжевый) для всех столбцов – по обе стороны нуля. Без градиентов, свечения и палитр; per-bar color зарезервирован под единичные редакционные исключения (аномалия, текущий период).
  3. 3. Без сетки. Одна линия 1px на нулевой базе (data-ink, Тафти).
  4. 4. Тултип при наведении: подпись периода в Geist sans + значение в Hack mono, в единой приподнятой карточке.
  5. 5. Каскадная анимация появления (30 мс на столбец, scale-Y от базы). Отключается при prefers-reduced-motion.
  6. 6. Встроенный проп source рендерит строку атрибуции в стиле FT/Bloomberg под графиком.
  7. 7. Понятное пустое состояние (иконка + сообщение No data) при пустом наборе; полная машина состояний через loading / error (сплошной скелетон, оверлей обновления со спиннером, повтор) в том же визуальном языке.

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

Column Chart подходит для упорядоченных категорий, где каждый столбец – дискретный интервал: временной (часы, дни, недели, вызовы агента в минуту) или ранжированный (трафик по каналам через sort + topN, выручка по регионам, прибыль/убыток по месяцам – отрицательные значения полноправны). Лучше всего показывает объём, количество и ритм активности с первого взгляда – разреженные оси не спорят с данными.

Когда не использовать

Для непрерывных трендов – Line Chart. Для крошечных графиков внутри метрик-карточек и текста – Sparkline. Для рейтингов с длинными подписями (горизонтальные полосы) – Bar Chart (скоро). Для состава во времени – Stacked Column Chart (скоро).

Источники

  • · Financial Times Visual Journalism – разреженные оси, строка источника, одноцветные столбцы
  • · Годовые письма Stripe – числа внутри редакционного текста
  • · The Pudding – интерактивный сторителлинг со сдержанностью
  • · Эдвард Тафти, The Visual Display of Quantitative Information – дисциплина data-ink