Marble UI
← Components

Chart Container / Tooltip / Legend

Preview: Frame

Title, legend row, plot and a "Show data" table toggle. A static plot is role="img" with the summary; an interactive one labels its focus target instead.

Import

import { ChartContainer, ChartLegend, ChartTable } from '@marbleui/ui'

Props

<ChartContainer>alias of ChartFrame

PropTypeDefaultDescription
children *requiredReactNode—
summary *requiredstring—Accessible summary of what the chart shows.
actionsReactNode—
classNamestring—
descriptionReactNode—
interactiveboolean—The plot holds its own focusable, labelled element (`useCrosshair` / `useChartNav`), so it isn't wrapped in role="img".
legendReactNode—
table{ columns: string[]; rows: (string | number)[][]; }—
titleReactNode—

<ChartLegend>

PropTypeDefaultDescription
items *required{ label: string; color: string; }[]—
classNamestring—
shape'square' | 'dot' | 'line''square'

<ChartTable>

PropTypeDefaultDescription
caption *requiredstring—
columns *requiredstring[]—
rows *required(string | number)[][]—

Usage notes

The frame every chart renders in: title and description, a legend row, the plot, and a “Show data” toggle that swaps the plot for a table. Wrap a custom visual in ChartContainer to give it the same structure as BarChart and the other charts.

<ChartContainer
  title="Plan mix"
  summary="Pro 58%, Team 27%, Starter 15% of active subscriptions."
  legend={<ChartLegend items={plans.map((p, i) => ({ label: p.name, color: `var(--chart-${i + 1})` }))} />}
  table={{ columns: ['Plan', 'Share'], rows: plans.map((p) => [p.name, `${p.share}%`]) }}
>
  <PlanMixBar plans={plans} />
</ChartContainer>
  • children sit inside a role="img" element named by summary, so screen readers hear the summary instead of the marks. Put filters and menus in actions, not in the plot.
  • table adds the “Show data” button (aria-pressed). The table’s first column becomes row headers and summary its caption. The header row renders when title, description, actions or table is set.
  • ChartLegend renders nothing for fewer than two items; shape is square (default), line or dot. Color custom marks with --chart-1…8 in fixed order, like the built-in charts.
Example source · show
import { ChartContainer, ChartLegend } from './chart-container'

export const styles = {
  Frame: () => (
    <div className="w-96 rounded-xl border border-border bg-surface p-4">
      <ChartContainer
        title="Plan mix"
        description="Share of active subscriptions"
        summary="Plan mix: Pro 58%, Team 27%, Starter 15%."
        legend={<ChartLegend items={[{ label: 'Pro', color: 'var(--chart-1)' }, { label: 'Team', color: 'var(--chart-2)' }, { label: 'Starter', color: 'var(--chart-3)' }]} />}
        table={{ columns: ['Plan', 'Share'], rows: [['Pro', '58%'], ['Team', '27%'], ['Starter', '15%']] }}
      >
        <div className="flex h-3 gap-0.5 overflow-hidden rounded-full">
          <span className="rounded-l-full" style={{ width: '58%', background: 'var(--chart-1)' }} />
          <span style={{ width: '27%', background: 'var(--chart-2)' }} />
          <span className="rounded-r-full" style={{ width: '15%', background: 'var(--chart-3)' }} />
        </div>
      </ChartContainer>
    </div>
  ),
}

Source: packages/ui/src/chart-container/chart-container.tsx

Catalog

Category
charts
Priority
Pro
Primitive
custom