JSON rein,
fertiges SVG raus
chartlet übersetzt eine kleine JSON-Spezifikation beim Build in ein fertiges Diagramm. Im Browser läuft davon nichts: kein Chart-JavaScript, keine Hydration, kein Layout-Shift.
$ cargo install chartlet --version 0.1.0-alpha.3 $ chartlet render monthly-revenue.json -o chart.svg $ chartlet render broken.json -o chart.svg chartlet: series_length_mismatch at /series/0/values: expected 2 values, one per category; use null for a missing value $ echo $? 1
Ein Diagramm ist ein Build-Artefakt, kein Laufzeit-Feature.
Wer auf einer statischen Seite ein Diagramm zeigt, lädt heute meist eine Chart-Bibliothek in den Browser – für Daten, die sich seit dem Build nicht geändert haben. chartlet verschiebt diesen Schritt nach vorn: Die Spezifikation wird beim Build zu SVG oder HTML, das Ergebnis ist eine Datei wie jede andere.
Dokumentation →- 01Deterministisch — dieselbe Spezifikation ergibt dieselben Bytes, prüfbar im Diff und cachebar wie jedes Build-Artefakt
- 02Barrierefrei angelegt — jedes Diagramm trägt Titel und Beschreibung, die HTML-Ausgabe zusätzlich die vollständigen Daten als Tabelle
- 03Ehrlich bei Problemen — ungültige Eingaben werden mit Code, JSON-Pointer und Lösungshinweis abgelehnt
- 04Zwei Wege hinein — Rust-CLI und Crate, dazu eine Astro-Integration, die zur Build-Zeit rendert
Eine Spezifikation, zwei Ausgabeprofile.
{
"schemaVersion": 1,
"type": "bar",
"title": "Monthly revenue",
"description": "Revenue increased in February and remained above January in March.",
"source": "Example data",
"categoryAxis": { "title": "Month" },
"valueAxis": { "title": "Revenue in thousands", "format": "number" },
"data": [
{ "label": "January", "value": 120 },
{ "label": "February", "value": 180 },
{ "label": "March", "value": 150 }
]
}<svg xmlns="http://www.w3.org/2000/svg" width="800" height="450" viewBox="0 0 800 450"
role="img" class="chartlet-root"
aria-labelledby="chartlet-e92f7ad68bc8a2d6-title chartlet-e92f7ad68bc8a2d6-description">
<title id="chartlet-e92f7ad68bc8a2d6-title">Monthly revenue</title>
<desc id="chartlet-e92f7ad68bc8a2d6-description">Revenue increased in February
and remained above January in March.</desc>
<style>.chartlet-root{--chartlet-text:#172033;--chartlet-muted:#344054;
--chartlet-grid:#d9dee8;--chartlet-accent:#2563eb;--chartlet-background:#fff; …}</style>
…
</svg><figure class="chartlet-figure">
<figcaption>Monthly revenue</figcaption>
<svg …>…</svg>
<p class="chartlet-source">Source: Example data</p>
<details class="chartlet-data">
<summary>Show chart data</summary>
<table>
<caption>Data for Monthly revenue</caption>
<thead><tr><th scope="col">Category</th><th scope="col">Value</th></tr></thead>
<tbody>
<tr><th scope="row">January</th><td>120</td></tr>
<tr><th scope="row">February</th><td>180</td></tr>
<tr><th scope="row">March</th><td>150</td></tr>
</tbody>
</table>
</details>
</figure>Balken, Linien, Zeitreihen – und sonst nichts.
Balken, vertikal
"type": "bar" mit data: eine Liste aus label und value. Der Standardfall, 800 × 450 Pixel, wenn nichts anderes angegeben ist.
Balken, horizontal
"orientation": "horizontal" – auch mit negativen Werten; die Nulllinie wird dann eigens gezeichnet.
Gruppierte Balken
categories und series statt data, bis zu vier Serien. Die Legende entsteht automatisch, die Datentabelle bekommt eine Spalte je Serie.
Linien mit Lücken
"type": "line" mit null für eine Beobachtung, die es nicht gibt. chartlet lässt eine sichtbare Lücke, statt eine Linie darüber zu ziehen; die Tabelle zeigt „Missing“.
Zeitreihen
"type": "time" mit panes und layers zeichnet auf einer Kalenderachse. Zeitstempel als Unix-Sekunden oder ISO 8601, Zeitzone als fester UTC-Versatz, UTC als Vorgabe.
Grenzen im Vertrag
Bis zu 100 Kategorien und vier Serien; eine Zeit-Pane trägt vier Layer mit je 2000 Beobachtungen. Labels müssen eindeutig sein. Was darüber liegt, wird abgelehnt statt still gekürzt.
Bedienelemente ohne eine Zeile Chart-JavaScript.
Ein gruppiertes Diagramm bekommt eine Checkbox je Serie. Abwählen blendet Balken und Wertlabels per CSS :has() aus; die Achse skaliert nicht neu. Browser ohne :has() zeigen schlicht alle Serien.
Zwei bis vier zoomSteps erzeugen vorberechnete Ausschnitte desselben Diagramms, umschaltbar über Radio-Buttons. Jede Stufe kostet ein eigenes SVG in der Datei.
Jeder Balken und jeder Punkt trägt ein natives <title>. Als Alternative für assistive Technik gelten aber die Beschreibung und die Datentabelle, nicht diese Hover-Hinweise.
Installieren, Spezifikation schreiben, rendern.
- 1Installieren
CLI von crates.io holen
Rust 1.88 oder neuer. Solange chartlet im Alpha-Stand ist, gehört die Version ausdrücklich dazu.
cargo install chartlet --version 0.1.0-alpha.3 - 2Rendern
Spezifikation übersetzen
Ohne
--formatentsteht ein eigenständiges SVG, mit--format htmleine<figure>samt Quelle und Datentabelle.-liest von der Standardeingabe.chartlet render spec.json --format html -o chart.html - 3Astro
Beim Seitenbau rendern
@casoon/chartletruft im Alpha-Stand die CLI auf – beide werden gebraucht, undchartletmuss imPATHliegen oder überCHARTLET_BINerreichbar sein.--- import Chart from '@casoon/chartlet/astro'; import revenue from '../data/monthly-revenue.json'; --- <Chart id="monthly-revenue" spec={revenue} /> - 4CI
Warnungen zu Fehlern machen
--strictlässt den Lauf bei jeder Warnung scheitern – etwa wenn ein Label gekürzt oder ein Wertlabel weggelassen werden musste.chartlet render spec.json --strict -o chart.svg
Was im Diagramm steckt – und was noch offen ist.
chartlet ist auf Barrierefreiheit hin gebaut, aber noch nicht mit Screenreadern geprüft. Die folgenden Punkte sind im Quelltext umgesetzt; die Prüfung mit VoiceOver und NVDA steht aus.
- 01
Ein Bild mit Namen und Beschreibung
Das SVG trägt
role="img"und verweist überaria-labelledbyauf<title>und<desc>. Fehlt eine eigenedescription, erzeugt chartlet sie aus den Daten – etwa: „Bar chart with 4 categories and 2 series (Budget, Actual). Highest: 160 (Budget in April). Lowest: 120 (Budget in January). 1 value is missing.“ - 02
Die Daten als echte Tabelle
Die HTML-Ausgabe enthält immer ein
<table>mitcaption,th scope="col"undth scope="row"– auch die Werte, deren sichtbares Label keinen Platz hatte. Standardmäßig steckt es in einem nativen<details>;--table visiblezeigt es dauerhaft. - 03
Farben mit Kontrast
Die Serienfarben bleiben bei den verbreiteten Formen der Farbfehlsichtigkeit unterscheidbar und halten mindestens 4,5:1 gegen Weiß; im dunklen Thema erreicht die schwächste Serienfarbe 7,65:1. Die Legende führt die Serien in der Reihenfolge der Balken.
- 04
Kein stilles Weglassen
Gekürzte Labels und ausgelassene Wertlabels erzeugen Warnungen (
text_truncated,value_labels_omitted). Der vollständige Text bleibt in der Spezifikation und in der Datentabelle.
Geprüft, entworfen, geplant.
| Kontext | Status | Anmerkung |
|---|---|---|
| Eigenständiges SVG | Verifiziert | Byte-gleich auf macOS und Linux, durch Golden-File-Tests abgedeckt |
| HTML-Figure mit Datentabelle | Verifiziert | Die Tabelle ist immer vorhanden, standardmäßig in einem `<details>` |
| Astro-Komponente | Verifiziert | Rendert zur Build-Zeit und liefert kein Chart-JavaScript aus |
| Chromium (Chrome, Edge) | Verifiziert | axe-Scan in der CI und manuelle Prüfung während der Entwicklung |
| `<img src="chart.svg">` | Entworfen | Titel und Beschreibung bleiben; Figure und Datentabelle entfallen |
| Serienfilter und Zoom-Stufen | Entworfen | Im HTML-Profil vorhanden; breitere Browser-Prüfung steht aus |
| Firefox und Safari | Geplant | Standard-SVG wird erwartet, der Accessibility-Baum ist ungetestet |
| VoiceOver, NVDA, JAWS | Geplant | Noch nicht getestet |
| Markdown, PDF und Druck, E-Mail | Geplant | Noch nicht bewertet |
| WASM-Build und native Node-Bindings | Nicht unterstützt | Nach dem MVP geplant; die Alpha ruft die CLI auf |
Die vollständige Matrix mit Themes und Zeitreihen steht in der Dokumentation.
Entwicklungsstand · Stand: September 2026
Balken, Linien, Zeitreihen
Balkendiagramme einzeln und gruppiert, vertikal und horizontal, kategoriale Liniendiagramme und Zeitreihen auf einer Kalenderachse. Dazu die HTML-Ausgabe mit Datentabelle, Serienfilter und Zoom-Stufen, das JSON-Schema als Vertrag und Golden-File-Tests für die Ausgabe.
Frühe Alpha, Version 0.1.0-alpha.3
Das erste Release liegt seit dem 11. September 2026 auf crates.io, das npm-Paket unter derselben Version. Die Spezifikation kann sich vor dem ersten stabilen Release noch ändern – schemaVersion ist dafür von Anfang an Pflichtfeld.
Marks, Screenreader-Prüfung, Bindings
Die Mark-Typen area, ohlc, band und annotation werden derzeit mit mark_not_implemented abgelehnt, mehrere Panes mit too_many_panes. Offen sind außerdem die Prüfung mit VoiceOver und NVDA, Forced-Colors-Unterstützung sowie ein WASM-Build und native Node-Bindings anstelle des CLI-Aufrufs.
Drei Beispiele, direkt aus dem Repo.
Die Dateien stammen aus examples/ im Repo, erzeugt mit chartlet 0.1.0-alpha.3. Jede trägt Titel und Beschreibung im SVG.
Wann chartlet das falsche Werkzeug ist.
chartlet veröffentlicht ein fertiges Diagramm. Es ist kein Werkzeug, um Daten zu erkunden.
Interaktive Analyse
Stufenloses Zoomen, Pannen, Cross-Filtering, Live-Updates oder einzeln per Tastatur ansteuerbare Datenpunkte gibt es nicht.
Daten, die sich zur Laufzeit ändern
Gerendert wird beim Build. Wer Werte zeigt, die sich im Minutentakt bewegen, braucht etwas anderes.
Andere Diagrammarten
Karten, Netzwerke, 3D und alles jenseits von Balken, Linien und Zeitreihen sind nicht vorgesehen.
Zwei Installationen
Das npm-Paket ruft im Alpha-Stand die Rust-CLI auf. Auf einem Build-Server müssen also beide vorhanden sein.
Noch nicht mit Screenreadern geprüft
Die Ausgabe ist auf Barrierefreiheit hin entworfen, aber die Prüfung mit VoiceOver, NVDA und JAWS steht aus. Bis dahin gilt sie als geplant, nicht als nachgewiesen.
Häufige Fragen.
Was heißt „deterministisch“ konkret?
Dieselbe Spezifikation ergibt dieselben Bytes. Die Golden-File-Tests des Repositories prüfen das für macOS und Linux. Praktisch heißt das: Ein Diagramm lässt sich reviewen, diffen und cachen wie jedes andere Build-Artefakt.
Wie meldet chartlet einen Fehler in der Spezifikation?
Mit einem Code, der Stelle als JSON-Pointer und einem Hinweis zur Behebung, zum Beispiel: chartlet: series_length_mismatch at /series/0/values: expected 2 values, one per category; use null for a missing value. Der Prozess endet dann mit Exit-Code 1.
Was ist der Unterschied zwischen einem Fehler und einer Warnung?
Ein Fehler stoppt das Rendern. Eine Warnung geht nach stderr, das Diagramm entsteht trotzdem. Es gibt vier Warnungscodes: text_truncated, value_labels_omitted, dense_chart und color_not_supported. Mit --strict wird jede Warnung zum Fehler.
Brauche ich Rust, um chartlet in einem Astro-Projekt zu nutzen?
Im Alpha-Stand ja. Das npm-Paket @casoon/chartlet ruft die Rust-CLI auf, also müssen beide installiert sein und chartlet im PATH liegen oder über CHARTLET_BIN erreichbar sein. Native Node-Bindings sind geplant, aber noch nicht vorhanden.
Wie sieht das Diagramm im Dark Mode aus?
Mit "theme": "dark" setzt chartlet dieselben CSS-Custom-Properties auf eine dunkle Palette und zeichnet einen eigenen Hintergrund. Die schwächste Serienfarbe erreicht dabei 7,65:1 Kontrast gegen #0e131c. Ein Umschalten zur Laufzeit gibt es nicht – das Thema gehört zur Spezifikation.
Kann ich dasselbe Diagramm zweimal auf einer Seite einbinden?
Ja, aber mit --id-prefix. Die IDs für Titel und Beschreibung werden aus der Spezifikation abgeleitet; ohne eigenen Präfix stünden sie zweimal identisch im Dokument.
Diagramme, die vor dem Browser fertig sind.
chartlet ist MIT-lizenziert und liegt vollständig auf GitHub – mit JSON-Schema, Beispielsammlung und einer Galerie, in der jede Spezifikation neben ihrer Ausgabe steht.