io / charts
Server-rendered SVG charts for Io: bar and line charts with a data table, legend and accessible markup, tooltips as a progressive enhancement.
Requires
- php: >=8.5
- io/framework: ^0.1.0
Requires (Dev)
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13.3
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Bar and line charts for Io, rendered as SVG on the
server. They work without JavaScript, print, go into PDFs and e-mails, and ship their data as a real
table. io.js adds tooltips, a crosshair and keyboard navigation on top.
Setup
$app->plugin(new Io\Charts\ChartsPlugin()); // before attach()
The layout renders the stylesheet and script with $this->pluginAssetTags().
Usage
The sales page of Sonnenkorn, the bakery in the Io example app,
draws two charts from its sales report (from src/Controllers/BackofficeController.php):
use Io\Charts\Chart; $revenue = Chart::fromRows($this->sales->revenueByDay(28, $locale), x: 'day', y: 'revenue') ->title('Revenue per day, last four weeks') ->unit('€'); $products = Chart::fromRows($this->sales->topProducts(28, $locale), x: 'product', y: 'quantity') ->title('Sold in the last four weeks') ->size(640, 280);
- In a view:
{{ $this->chart($model->revenue) }}renders a<figure>with caption, the SVG, a legend (two or more series) and the data table in a<details>. - From a route:
return $chart;answers withimage/svg+xml. Use it as<img src="/backoffice/sales/revenue.svg">or inside a PDF; the SVG carries its own dark-mode styles. - From a query:
Chart::fromRows($rows, x: 'day', y: 'quantity', series: 'product')pivots rows (arrays or objects) into labels and series (duplicates add up). - By hand:
Chart::bar()->labels(['Mon', 'Tue'])->series('Croissants', [84, 76])->series('Pretzels', [60, 58]).
Builder options: line() or bar(), stacked() (bars), markers(false) (lines), unit('€', decimals: 2),
description('...') for the screen-reader sentence (generated from the data when omitted),
size(width, height), withTable(false). At most eight series: fold the rest into "Other" or draw a
second chart; never two value axes on one chart.
What you get by design
- Thin marks: bars at most 24px wide with a rounded data end and a square baseline, 2px lines, 8px markers with a surface ring, 2px surface gaps in stacks. Gridlines are hairlines.
- Colors are assigned to series in a fixed, validated colorblind-safe order and follow the series, never its rank. Text never wears a series color.
- A legend for two or more series; the maximum of a single-series bar chart and the end value of each line are labelled directly. Everything else is in the tooltip and the table.
role="img"with<title>and a generated<desc>; every x position is a focusable hit area with the values in itsaria-label; arrow keys walk the positions once the script is loaded.- Light and dark mode from the same eight hues, stepped for each surface.
Options
| Setting | Default | Description |
|---|---|---|
width, height |
640, 320 |
Drawing size; the SVG scales with its container. |
maxBarWidth |
24 |
Bars never get thicker than this. |
format |
null |
Closure(float $value, int $decimals): string; default number_format with . and ,. |
table |
true |
Render the data table under every chart. |
tableLabel |
'Show data as table' |
Text of the <summary>. |
palette, paletteDark |
validated defaults | Eight categorical colors per surface, in slot order. Validate a replacement before shipping it. |
Development
composer install && composer test && composer stan