noeka/svgraph

JavaScript-free SVG chart rendering for PHP. Sparkline, line/area, bar, pie/donut, and progress charts as static markup.

Maintainers

Package info

github.com/noeka/svgraph

pkg:composer/noeka/svgraph

Statistics

Installs: 13

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 3

0.3.0 2026-05-14 19:50 UTC

README

CI

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.

Hero example

  • 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
sparkline
Line / area
line
Bar
bar
Pie
pie
Donut
donut
Progress
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

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.