noeka / svgraph
JavaScript-free SVG chart rendering for PHP. Sparkline, line/area, bar, pie/donut, and progress charts as static markup.
Requires
- php: ^8.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- infection/infection: ^0.29
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.0
- rector/rector: ^2.0
- roave/security-advisories: dev-latest
- spatie/phpunit-snapshot-assertions: ^5.0
This package is auto-updated.
Last update: 2026-05-15 18:43:57 UTC
README
JavaScript-free SVG chart rendering for PHP. Sparkline, line/area, bar, pie/donut, and progress charts as static markup — no canvas, no JS, no build step.
- Server-side: charts render to plain SVG strings, ready to echo into a template.
- Zero JavaScript: hover tooltips, keyboard focus, and animations are pure CSS.
- Themeable: built-in light/dark themes plus a full set of CSS custom properties.
- Accessible:
role="img"with labelled<title>/<desc>, screen-reader data table fallback, focusable points, link safety, reduced-motion aware. - Tiny: PSR-4, no runtime dependencies.
Requirements
- PHP 8.3+
Installation
composer require noeka/svgraph
Quick start
use Noeka\Svgraph\Chart; echo Chart::line([ ['Mon', 12], ['Tue', 27], ['Wed', 18], ['Thu', 41], ])->axes()->grid()->smooth()->stroke('#3b82f6');
Every chart is Stringable — cast with (string) or drop directly
into a template with <?= ?>.
Chart gallery
| Sparkline |
Line / area |
Bar |
| Pie |
Donut |
Progress |
Click any chart for its full options reference and worked examples.
Theming
use Noeka\Svgraph\Theme; // Built-in dark theme Chart::line($data)->theme(Theme::dark()); // Custom palette on top of the default theme Chart::pie($data)->theme( Theme::default()->withPalette('#6366f1', '#f43f5e', '#0ea5e9', '#84cc16'), );
Full reference: docs/theming.md.
Multi-series
use Noeka\Svgraph\Data\Series; Chart::line(['Jan' => 12, 'Feb' => 27, 'Mar' => 18]) ->addSeries(Series::of('Costs', ['Jan' => 6, 'Feb' => 14, 'Mar' => 9], '#ef4444')) ->axes()->grid();
For bar charts, ->grouped() and ->stacked() pick how series share
each x-tick. See bar chart docs.
Legend (toggle series visibility)
Chart::line(['Jan' => 12, 'Feb' => 27, 'Mar' => 18]) ->addSeries(Series::of('Costs', ['Jan' => 6, 'Feb' => 14, 'Mar' => 9])) ->legend();
Available on line and bar charts. Each legend entry is a <label> bound
to a hidden checkbox; clicking it hides that series and dims the entry.
Pure CSS — no JavaScript.
Caveats:
- State is page-local. Refreshing the page resets every toggle; without JS there is nowhere to persist it.
- Axes do not rescale. Hiding a tall series leaves the value axis at the original combined min/max, so the remaining series stay at their original positions on the canvas.
- Multiple charts on the same page get unique IDs automatically, so toggling one chart never affects another.
- Keyboard-accessible. The
<label>+ checkbox combo is natively focusable; pressing Space toggles the series.
Animations
Chart::pie($data)->legend()->animate();
All entrance animations sit inside @media (prefers-reduced-motion: no-preference),
so users who request reduced motion always see a static chart. Details:
docs/animations.md.
Documentation
- Getting started
- Data formats — every input shape (lists, tuples, maps,
Point/Series/Slice/Link) - Theming — themes, palettes, full token reference
- Annotations — reference lines, threshold bands, target zones, callouts
- Animations
- Accessibility
- CSS customization —
.series-{N}hooks,--svgraph-*properties - Recipes — Blade, Twig, email, caching
- docs/index.md — full table of contents
- Contributing — developer setup, tooling, workflow
Regenerating example images
The SVGs in docs/images/ are generated from the
runnable PHP scripts in examples/. Rebuild them after a
code change:
composer docs:images
License
MIT — see LICENSE.