Charts (line / bar / area / pie / scatter / graph)
components specs/components/charts.kmd
Canonical chart primitives — line / bar / stacked-bar / area / scatter / pie / donut / sparkline / graph (node-edge). Behavior contract; library binding is impl detail. Replaces ad-hoc chart-lib picks in Koder admin tools (Forge metrics, Hub analytics, Talk stats). Modeled after Ant Design G2/G6 + PatternFly + Material 3.
When this spec applies
Primary triggers
- Pick a chart component for a Koder product surface
All triggers
- Render data visualization in a Koder admin tool
- Replace a third-party chart with the canonical primitive
Specification body
Component — Charts
Status: v0.1.0 — Draft. Behavior contract; library binding (Victory / Recharts / G2 / fl_chart for Flutter) is impl detail chosen per surface.
R1 — Chart-type catalog
| Type | Use when |
|---|---|
| Line | Continuous data over time; 1-5 series |
| Bar | Categorical comparison; vertical default, horizontal on long labels |
| Stacked bar | Part-of-whole over categories |
| Area | Magnitude over time with emphasis on cumulative value |
| Scatter | Correlation between two variables; outlier detection |
| Pie | Part-of-whole, ≤ 5 slices (otherwise use bar) |
| Donut | Pie + central value display |
| Sparkline | Inline mini-chart in tables / cards; no axes |
| Graph | Nodes + edges (topology, dependency, mind-map) |
Other types (treemap, sankey, heatmap, candlestick, 3D, geospatial) out of v0 — open separate ticket if a Koder product needs one.
R2 — Color usage
- Series colors: cycle through Verge accent palette in canonical order
(defined in
specs/themes/color-roles.kmd— for v0 use--kdr-accent→ +30° HCL → +60° → +90° → +120°). NEVER hardcode hex; consume via tokens. - Status colors (success / warning / danger) used ONLY when the data itself is status-typed (uptime chart green/yellow/red); otherwise stick to the accent cycle.
- Color-blind a11y per
specs/themes/a11y-modes.kmd(#062):- Lines and bars MUST also vary in pattern / stroke style when ≥ 3 series share a chart — color alone is insufficient.
R3 — Axes
- X-axis labels rotate to 45° when overlap detected (auto), otherwise horizontal.
- Y-axis: locale-aware number formatting (per
specs/content/grammar- and-mechanics.kmdR4 —1,234.56en-US vs1.234,56pt-BR). - Grid lines: subtle (
--kdr-borderat 50% opacity); never dominate the data.
R4 — Tooltips
On hover (or focus / tap), show a tooltip:
- Series name (per legend) + data point value
- For multi-series at the same X position: stacked tooltip listing all series + values
- Position: above the data point; flips below if no space
- Dismiss: pointer leave / blur / outside-tap / Esc
R5 — Keyboard navigation
| Key | Action |
|---|---|
| Tab | Enter the chart (focus moves to first data point) |
| ← / → | Navigate adjacent data points within a series |
| ↑ / ↓ | Switch active series (multi-series charts) |
| Enter | "Drill" — emit a drill event (consumer handles) |
| Esc | Exit chart focus |
R6 — Screen-reader
- Each chart MUST emit a
<table>data-alternate sibling (role="presentation"if visually hidden), so SR users can read the data values without parsing the SVG. - Chart container
role="img"witharia-label= chart title + brief description (e.g. "Line chart, monthly active users, 6 months"). - On focused data point: live-region announces "{series}: {value} at {x-label}".
R7 — i18n
- Axis labels, legend labels, tooltip series names translatable per
specs/i18n/contract.kmd. - Number formatting per locale (R3).
- Date formatting on time-axis charts: locale-aware short form
(per
specs/components/date-picker.kmdR7).
R8 — OUIA
Per specs/testing/ouia-test-hooks.kmd:
data-ouia-component-type="Chart"(orLineChart,BarChart, … for type-specific)data-ouia-component-id="<chart-slug>"data-ouia-safe="true"only when data loaded + render complete (no animation in progress).
R9 — Empty / loading / error
- Empty data: render
patterns/empty-state.kmdinside chart container. - Loading:
components/skeleton.kmdshape mirror (a placeholder grid). - Error: error pattern with retry button (when data fetch fails).
Não-escopo
- Specific library binding (Victory vs Recharts vs G2 vs fl_chart) — consumer choice; spec ratifies behavior contract only.
- Real-time streaming / WebSocket data updates (separate ticket if pursued).
- 3D, geospatial, financial candlestick, heatmap, treemap, sankey (case-by-case; open ticket per type when a product needs one).
Requirements (testable)
Requirement: Series color cycling and color-blind redundancy {#req-charts-color-encoding}
Chart series SHALL derive their colors from the Verge accent token cycle in canonical order and SHALL NOT hardcode hex values; when three or more series share a chart, each series SHALL additionally vary in pattern or stroke style so that color is never the sole distinguishing encoding.
Scenario: Series colors come from tokens, not hardcoded hex
- GIVEN a line chart with three series
- WHEN the chart renders
- THEN each series stroke resolves to a Verge accent token (
--kdr-accent, then +30° / +60° HCL steps) in canonical order - AND no series stroke is a literal hardcoded hex color
Scenario: Three or more series add pattern redundancy
- GIVEN a bar chart configured with four series
- WHEN the chart renders
- THEN each series is drawn with a distinct pattern or stroke style in addition to its color
- AND two series with adjacent accent hues remain distinguishable when color is removed
Requirement: Hover and focus tooltips {#req-charts-tooltip}
A chart SHALL present a tooltip on hover, focus, or tap that shows the series name and the data-point value, SHALL list all series with their values when multiple series share the same X position, and SHALL dismiss the tooltip on pointer leave, blur, outside-tap, or Esc.
Scenario: Multi-series tooltip lists every series at the X position
- GIVEN a chart with three series sharing an X coordinate
- WHEN the pointer hovers that X position
- THEN the tooltip lists all three series names with their values
Scenario: Esc dismisses the tooltip
- GIVEN a visible tooltip on a focused data point
- WHEN the user presses Esc
- THEN the tooltip is removed from the view
Requirement: Keyboard navigation of data points {#req-charts-keyboard}
A chart SHALL be fully keyboard operable: Tab SHALL move focus to the first data point, Left/Right SHALL navigate adjacent data points within a series, Up/Down SHALL switch the active series on multi-series charts, Enter SHALL emit a drill event, and Esc SHALL exit chart focus.
Scenario: Arrow keys move within and across series
- GIVEN a multi-series chart with focus on the first data point of series 1
- WHEN the user presses Right and then Down
- THEN focus advances to the next data point in series 1
- AND then moves to the corresponding data point in series 2
Scenario: Enter emits a drill event
- GIVEN a focused data point
- WHEN the user presses Enter
- THEN a drill event is emitted for the consumer to handle
Requirement: Screen-reader accessibility {#req-charts-screenreader}
A chart SHALL expose its data to assistive technology: it SHALL emit a data-alternate <table> sibling readable by screen readers, its container SHALL carry role="img" with an aria-label combining the chart title and a brief description, and a focused data point SHALL be announced via a live region as "{series}: {value} at {x-label}".
Scenario: Chart exposes a data-alternate table and img role
- GIVEN a line chart of monthly active users over six months
- WHEN the chart renders
- THEN the container has
role="img"with anaria-labelcontaining the chart title and description - AND a sibling
<table>carrying the same data values is present in the DOM
Scenario: Focused data point is announced
- GIVEN a chart with keyboard focus on a data point
- WHEN focus lands on that point
- THEN a live region announces "{series}: {value} at {x-label}" for that point
Requirement: OUIA test hooks {#req-charts-ouia}
A chart SHALL expose OUIA hooks: data-ouia-component-type set to Chart (or the type-specific name such as LineChart), data-ouia-component-id set to the chart slug, and data-ouia-safe="true" only once data is loaded and rendering is complete with no animation in progress.
Scenario: Safe flag is false during animation and true once settled
- GIVEN a chart whose data has loaded and whose entry animation is running
- WHEN rendering is still animating
- THEN
data-ouia-safeisfalse - AND once the animation completes
data-ouia-safebecomestrue
Requirement: Empty, loading, and error states {#req-charts-states}
A chart SHALL render the canonical empty-state pattern inside its container when there is no data, SHALL render the skeleton placeholder grid while loading, and SHALL render the error pattern with a retry button when a data fetch fails.
Scenario: Empty data renders the empty-state pattern
- GIVEN a chart bound to an empty dataset
- WHEN the chart renders
- THEN the empty-state pattern is shown inside the chart container
- AND no data marks (lines, bars, points) are drawn
Scenario: Fetch failure renders an error with retry
- GIVEN a chart whose data fetch fails
- WHEN the failure is surfaced
- THEN the error pattern is shown with a retry button that re-triggers the fetch
References
specs/themes/verge.kmdspecs/themes/color-roles.kmdspecs/components/data-table.kmdspecs/testing/ouia-test-hooks.kmd