Темы
Тема — это набор дизайн-токенов, из которого чарт берёт всё оформление: палитру серий, цвета фона и текста, базовый кегль, толщину линий, скругление и прозрачность марок, семантические цвета роста и падения, вид осей. В theme передаётся имя встроенной темы или объект ThemeOptions.
Собирать тему руками необязательно — в конструкторе тем на каждый токен есть контрол, живые превью и экспорт в JSON (страница на английском).
Встроенные темы
В библиотеке семь пресетов. Их имена экспортируются как THEME_NAMES — селекту не нужен захардкоженный список:
import { Charts, THEME_NAMES, type ThemeName } from 'grafit-charts';
for (const name of THEME_NAMES) select.append(new Option(name, name));
void chart.updateDelta({ theme: select.value as ThemeName });| Имя | Фон | Что это |
|---|---|---|
'default' | светлый | нейтральная светлая тема, используется без theme |
'dark' | тёмный | нейтральная тёмная тема |
'vibrant' | светлый | насыщенные цвета в порядке, при котором соседние не сливаются у дальтоников |
'muted' | светлый | приглушённая палитра на тёплом фоне |
'mono' | светлый | один тон от светлого к тёмному — для этапов и уровней, не для разных категорий |
'contrast' | светлый | все цвета серий держат 3:1, включены тики, сплошная сетка, толстые линии |
'midnight' | тёмный | тёмная тема с синим отливом, палитра подобрана под более тёмный фон |
'default' — светлая (используется без theme):
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Light theme (default)' },
series: [
{ type: 'bar', xField: 'month', yField: 'desktop', name: 'Desktop' },
{ type: 'line', xField: 'month', yField: 'mobile', name: 'Mobile' },
],
theme: 'default',
};
}export function getData() {
return [
{ month: 'Jan', desktop: 42, mobile: 28 },
{ month: 'Feb', desktop: 49, mobile: 34 },
{ month: 'Mar', desktop: 46, mobile: 41 },
{ month: 'Apr', desktop: 58, mobile: 47 },
{ month: 'May', desktop: 63, mobile: 55 },
{ month: 'Jun', desktop: 60, mobile: 62 },
];
}'dark' — тёмная:
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Dark theme (dark)' },
series: [
{ type: 'bar', xField: 'month', yField: 'desktop', name: 'Desktop' },
{ type: 'line', xField: 'month', yField: 'mobile', name: 'Mobile' },
],
theme: 'dark',
};
}export function getData() {
return [
{ month: 'Jan', desktop: 42, mobile: 28 },
{ month: 'Feb', desktop: 49, mobile: 34 },
{ month: 'Mar', desktop: 46, mobile: 41 },
{ month: 'Apr', desktop: 58, mobile: 47 },
{ month: 'May', desktop: 63, mobile: 55 },
{ month: 'Jun', desktop: 60, mobile: 62 },
];
}'contrast' — тема с упором на доступность: меняет не только цвета, но и вид осей:
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'High-contrast preset' },
series: [
{ type: 'bar', xField: 'quarter', yField: 'product', name: 'Product' },
{ type: 'line', xField: 'quarter', yField: 'service', name: 'Service' },
],
// the preset inks up the chrome on its own: ticks on, solid grid, thicker lines
theme: 'contrast',
};
}export function getData() {
return [
{ quarter: 'Q1', product: 46, service: 28 },
{ quarter: 'Q2', product: 52, service: 34 },
{ quarter: 'Q3', product: 49, service: 45 },
{ quarter: 'Q4', product: 61, service: 52 },
];
}Тему можно менять на лету — chart.updateDelta({ theme: 'dark' }) перерисует чарт с анимацией. Демки на этом сайте так и переключаются вместе с темой страницы (если пример не задаёт тему явно).
Палитры и цветовое зрение
'vibrant', 'contrast' и 'midnight' проверены симуляцией цветового зрения: соседние цвета серий не сливаются при протанопии и дейтеранопии. 'muted' ближе к границе — подходит для четырёх серий или вместе с подписями значений. Палитра 'default' и 'dark' появилась раньше этой проверки и сохраняет опубликованные цвета ради совместимости.
Кастомная тема
Объект темы: baseTheme (основа) + palette (цвета серий по кругу) + params (дизайн-токены) + axis (оформление осей):
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Sales funnel' },
series: [{ type: 'bar', xField: 'stage', yField: 'count', name: 'Deals', cornerRadius: 6 }],
legend: { enabled: false },
// custom theme: palette + design tokens on top of a base theme
theme: {
baseTheme: 'dark',
palette: { fills: ['#27c08d'] },
params: {
backgroundColor: '#0d1f1a',
foregroundColor: '#d8f3e9',
fontFamily: 'Georgia, serif',
},
},
};
}export function getData() {
return [
{ stage: 'Leads', count: 1840 },
{ stage: 'Qualified', count: 1120 },
{ stage: 'Demo', count: 640 },
{ stage: 'Contract', count: 310 },
{ stage: 'Payment', count: 245 },
];
}Цвет, заданный в серии (fill, stroke), имеет приоритет над палитрой темы.
Дизайн-токены
В params по одному значению на токен — оно применяется сразу ко всем типам серий:
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Design tokens' },
subtitle: { text: 'one theme, every mark follows' },
series: [
{ type: 'bar', xField: 'month', yField: 'north', name: 'North' },
{ type: 'line', xField: 'month', yField: 'south', name: 'South', marker: { enabled: true } },
],
theme: {
baseTheme: 'vibrant',
params: {
// one value each, applied across every series type at once
fontSize: 12,
strokeWidth: 3,
cornerRadius: 6,
fillOpacity: 0.5,
},
axis: { tick: true, gridDash: [] },
},
};
}export function getData() {
return [
{ month: 'Jan', north: 34, south: 22 },
{ month: 'Feb', north: 41, south: 27 },
{ month: 'Mar', north: 38, south: 35 },
{ month: 'Apr', north: 52, south: 39 },
{ month: 'May', north: 57, south: 48 },
{ month: 'Jun', north: 54, south: 56 },
];
}Три токена ведут себя иначе остальных. cornerRadius и fillOpacity по умолчанию не заданы: встроенные значения различаются намеренно — столбец прямоугольный, а range-bar скруглённый, area заливается с 0.35, а маркер с 0.85. Не задавайте их — каждая марка сохранит своё значение; задайте — они перекроют все сразу.
fontSize — базовый кегль, по умолчанию 11. Остальные подписи отсчитываются от него фиксированным смещением: подписи осей — на базе, легенда и заголовки осей на шаг выше, заголовок чарта — на шесть. Смена базы двигает всю шкалу, сохраняя иерархию.
Веб-шрифты
Текст на canvas сам по себе загрузку шрифта не запускает: ещё не скачанный @font-face нарисуется — и измерится — запасным начертанием. Поэтому чарт сам запрашивает у браузера все семейства, упомянутые в опциях, и, когда настоящие начертания приходят, заново считает раскладку и перерисовывается: подписи, оси и легенда получают размеры того шрифта, который вы указали.
Шрифты, объявленные страницей позже — ленивый CSS-чанк, ваш собственный вызов document.fonts.add(), — тоже учитываются: чарт слушает их загрузку и перерисовывается, когда приходит одно из его семейств.
Поэтому смена fontFamily на ещё не загруженный шрифт даёт два кадра. chart.waitForUpdate() резолвится после второго — дождитесь его перед getImageDataURL(), если экспортируете картинку.
Если нужен ровно один кадр, поведение отключается:
{
fonts: { autoReload: false },
}Тогда чарт рисует тем начертанием, которое у браузера уже есть, и недостающие не запрашивает: с ещё не загруженным семейством он так и останется на запасном — текст на canvas сам загрузку шрифта не инициирует.
Оформление осей
ChartOptions.axes — массив, и overrides до него не дотягивается: для этого есть блок axis. В нём сразу переключатели, размеры и цвета всех осей:
theme: {
baseTheme: 'default',
axis: {
tick: true,
tickSize: 4,
gridDash: [],
gridColor: '#eceff3',
labelSize: 12,
titleColor: '#1f2733',
},
}Три переключателя работают как общий выключатель: выключенный гасит элемент везде, включённый оставляет обычное правило (сетка на оси значений, линия на оси категорий). Чтобы включить сетку там, где правило её убрало, задайте её на самой оси.
Цвета — необязательное уточнение. Не трогаете color, gridColor и tickColor — все трое идут за params.axisColor; не трогаете labelColor — он идёт за params.mutedColor, titleColor за params.foregroundColor. Задали один — изменится только он.
Легенда и тултип
Это обычные блоки ChartOptions, поэтому тема достаёт до них через overrides.common — отдельных токенов для них нет, потому что два пути к одному пикселю хуже одного:
theme: {
baseTheme: 'dark',
overrides: {
common: {
legend: { position: 'right', item: { label: { fontSize: 13 } }, background: { fill: '#1b1f27', cornerRadius: 8 } },
tooltip: { background: '#11151c', borderColor: '#2b313b', borderRadius: 10 },
},
},
}Так доступно всё из LegendOptions и TooltipOptions, а чарт, задавший ту же опцию сам, по-прежнему выигрывает.
Overrides
overrides — частичные options, вклеиваемые под пользовательские: common — chart-блоки для всех чартов, <seriesType>.series — дефолты серий данного типа. Это способ дотянуться до всего, что токены выразить не могут: стилей отдельного типа серий и нестилевых опций вроде legend.position.
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Overrides: defaults per series type' },
series: [
{ type: 'bar', xField: 'month', yField: 'desktop', name: 'Desktop' },
{ type: 'line', xField: 'month', yField: 'mobile', name: 'Mobile' },
],
theme: {
overrides: {
common: { legend: { position: 'right' } },
bar: { series: { cornerRadius: 8, fillOpacity: 0.8 } },
line: { series: { strokeWidth: 3.5, lineDash: [12, 6], marker: { enabled: false } } },
},
},
};
}export function getData() {
return [
{ month: 'Jan', desktop: 42, mobile: 28 },
{ month: 'Feb', desktop: 49, mobile: 34 },
{ month: 'Mar', desktop: 46, mobile: 41 },
{ month: 'Apr', desktop: 58, mobile: 47 },
{ month: 'May', desktop: 63, mobile: 55 },
{ month: 'Jun', desktop: 60, mobile: 62 },
];
}Приоритет: дефолты библиотеки < токены темы < overrides.common < overrides[type].series < явные options. Токены ниже overrides, потому что overrides вмерживаются в options ещё до того, как рендер обратится к теме.
Опции ThemeOptions
| Опция | Тип | Описание |
|---|---|---|
baseTheme | ThemeName | базовая тема-основа |
palette.fills | ColorValue[] | цвета заливки серий, по индексу серии |
palette.strokes | ColorValue[] | цвета обводки (по умолчанию = fills) |
palette.sequential | ColorValue[] | шкала для серий с colorField и градиентной легенды |
params.backgroundColor | ColorValue | фон чарта |
params.foregroundColor | ColorValue | основной цвет текста |
params.mutedColor | ColorValue | вторичный текст: подписи осей, подзаголовок, значения легенды |
params.axisColor | ColorValue | линии осей, тики и сетка |
params.fontFamily | string | шрифт всех надписей |
params.fontSize | Pixels | базовый кегль (11), остальные размеры двигаются вместе с ним |
params.strokeWidth | Pixels | толщина линий данных — line, area и radar |
params.lineDash | Pixels[] | штрих линий данных; [] — сплошные |
params.markStrokeWidth | Pixels | толщина обводки заливок — столбцов, секторов, боксов |
params.cornerRadius | Pixels | скругление всех прямоугольных марок; не задан — у каждой своё |
params.fillOpacity | Fraction | прозрачность всех заливок; не задан — у каждой марки своя |
params.positiveColor | ColorValue | рост: candlestick, ohlc |
params.negativeColor | ColorValue | падение: candlestick, ohlc, отрицательные столбцы waterfall |
axis.line | boolean | линия оси |
axis.tick | boolean | засечки |
axis.gridLine | boolean | линии сетки (и полярная паутина) |
axis.strokeWidth | Pixels | толщина линии оси, засечек и сетки |
axis.gridDash | Pixels[] | штрих сетки; [] — сплошная линия |
axis.lineDash | Pixels[] | штрих самой линии оси; по умолчанию сплошная |
axis.color | ColorValue | только линия оси; по умолчанию params.axisColor |
axis.gridColor | ColorValue | только сетка; по умолчанию params.axisColor |
axis.tickColor | ColorValue | только засечки; по умолчанию params.axisColor |
axis.tickSize | Pixels | длина засечки (6) |
axis.labelColor | ColorValue | подписи делений; по умолчанию params.mutedColor |
axis.labelSize | Pixels | кегль подписей; по умолчанию params.fontSize |
axis.labelSpacing | Pixels | зазор между линией оси и подписями (8) |
axis.titleColor | ColorValue | заголовок оси; по умолчанию params.foregroundColor |
axis.titleSize | Pixels | кегль заголовка оси; на шаг выше params.fontSize |
overrides.common | Record<string, unknown> | chart-блоки для всех чартов |
overrides.<seriesType>.series | Record<string, unknown> | дефолты серий конкретного типа |