<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ProvChart</title>
    <description>The latest articles on DEV Community by ProvChart (provchart).</description>
    <link>https://dev.to/provchart</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14466%2Fb4fe5b40-3c9d-46ca-9537-1880fc4bea3c.png</url>
      <title>DEV Community: ProvChart</title>
      <link>https://dev.to/provchart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/provchart"/>
    <language>en</language>
    <item>
      <title>Building a Lightweight Portfolio Dashboard with ProvChart &amp; st-core Tokens</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Fri, 11 Sep 2026 15:49:34 +0000</pubDate>
      <link>https://dev.to/provchart/building-a-lightweight-portfolio-dashboard-with-provchart-st-core-tokens-3g0l</link>
      <guid>https://dev.to/provchart/building-a-lightweight-portfolio-dashboard-with-provchart-st-core-tokens-3g0l</guid>
      <description>&lt;p&gt;&lt;strong&gt;Open-source template:&lt;/strong&gt; &lt;a href="https://github.com/Figsh/dashboard-project" rel="noopener noreferrer"&gt;github.com/Figsh/dashboard-project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most dashboard tutorials start the same way: pull in 40–150 KB of JavaScript, wait for hydration, and hope search engines eventually see the chart.&lt;/p&gt;

&lt;p&gt;We wanted the opposite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This&lt;/strong&gt; is a dark portfolio dashboard that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Renders charts with &lt;strong&gt;zero charting JavaScript&lt;/strong&gt; on the page
&lt;/li&gt;
&lt;li&gt;Uses &lt;strong&gt;ProvChart&lt;/strong&gt; to turn data into pure HTML + CSS
&lt;/li&gt;
&lt;li&gt;Keeps the original &lt;strong&gt;&lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;st-core.fscss&lt;/a&gt; design tokens&lt;/strong&gt; for a consistent, high-end look
&lt;/li&gt;
&lt;li&gt;Stays crawlable, fast on first paint, and easy to theme
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article walks through how it was built and how you can reuse the template.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem with typical chart libraries
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;First paint&lt;/th&gt;
&lt;th&gt;SEO / crawl&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JS Libs&lt;/td&gt;
&lt;td&gt;40–150 KB+ JS&lt;/td&gt;
&lt;td&gt;After hydration&lt;/td&gt;
&lt;td&gt;Often missed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hand-written CSS charts&lt;/td&gt;
&lt;td&gt;Fragile, limited points&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ProvChart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0 KB chart runtime&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ProvChart compiles your data into CSS custom properties and returns scoped HTML + CSS. The browser paints the shape with native CSS (&lt;code&gt;clip-path&lt;/code&gt;, gradients, etc.). No chart library ships to the client.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ProvChart API&lt;/strong&gt; — &lt;code&gt;POST /api/v1/generate&lt;/code&gt; → &lt;code&gt;{ html, css }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;st-core tokens&lt;/strong&gt; — the same CSS variables used by &lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;st-core.fscss&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite&lt;/strong&gt; — fast local dev and simple build
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiny Node proxy&lt;/strong&gt; — keeps the API key off the browser
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Design tokens stay exactly as they were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--st-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="m"&gt;#080710&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;#0f0e1c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="m"&gt;#13122a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="m"&gt;#6c47ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-accent-2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#a78bff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="m"&gt;#00e5a0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-red&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="m"&gt;#ff4d6a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="m"&gt;#edeaff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="m"&gt;#5e5a82&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--st-border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.13&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UI (sidebar, KPIs, allocation bars, activity feed, profile dropdown) is pure CSS on top of these tokens. Charts are the only part that talks to an external service.&lt;/p&gt;




&lt;h2&gt;
  
  
  How ProvChart is wired in
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Server-side proxy (key stays private)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// api/proxy-server.js (simplified)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://provchart-api.devtem.org/api/v1/generate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROVCHART_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser only calls &lt;code&gt;/api/chart&lt;/code&gt;. Never expose a live key in frontend bundles.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Client payload (real numbers, not 0–100)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;area&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;midnight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;260&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Wed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Thu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fri&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sun&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Now&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Portfolio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#6c47ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;74200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;76800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;84201&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Benchmark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#00c9b8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;72800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;74500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;81000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ProvChart auto-formats large values (20K, 1.3K, 2M, 1.09B…). You send raw numbers; you do &lt;strong&gt;not&lt;/strong&gt; pre-format with &lt;code&gt;k&lt;/code&gt; / &lt;code&gt;M&lt;/code&gt; / &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Inject HTML + CSS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// each chart gets its own &amp;lt;style&amp;gt; so instances never clobber each other&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each host (&lt;code&gt;#main-chart-host&lt;/code&gt;, &lt;code&gt;#spark-btc-host&lt;/code&gt;, …) keeps a scoped style tag. That avoids the classic “last chart wins and the others go blank” bug.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data shape: 9 points, clear narrative
&lt;/h2&gt;

&lt;p&gt;Instead of 24 noisy points, every series uses &lt;strong&gt;9 points&lt;/strong&gt; with a deliberate arc:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Downfall&lt;/strong&gt; — a clear dip
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High rate&lt;/strong&gt; — a strong recovery into “Now”
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example (1W portfolio):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;portfolio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;74200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;76800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;75500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;72800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;71500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;76200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;79800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;82500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;84201&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;//          Mon     Tue    Wed    Thu    Fri    Sat    Sun    Mon    Now&lt;/span&gt;
&lt;span class="c1"&gt;//                         ↘ dip ─────────↗ strong climb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Labels stay short and aligned (&lt;code&gt;Mon&lt;/code&gt; … &lt;code&gt;Now&lt;/code&gt;). No client-side slicing or thinning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dashboard features in the template
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Period tabs: &lt;strong&gt;1D / 1W / 1M / 3M / 1Y&lt;/strong&gt; (each re-requests ProvChart)
&lt;/li&gt;
&lt;li&gt;KPI row + overview stats strip
&lt;/li&gt;
&lt;li&gt;Main multi-series area chart (Portfolio vs Benchmark)
&lt;/li&gt;
&lt;li&gt;BTC / ETH spark area charts
&lt;/li&gt;
&lt;li&gt;Allocation bars + recent activity feed
&lt;/li&gt;
&lt;li&gt;Sidebar with &lt;strong&gt;profile dropdown&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Live clock, responsive layout, same st-core visual language
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is vanilla JS modules + CSS. No React/Vue required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Figsh/dashboard-project.git
&lt;span class="nb"&gt;cd &lt;/span&gt;dashboard-project
npm &lt;span class="nb"&gt;install

cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# or export PROVCHART_API_KEY="your_api_key"&lt;/span&gt;
&lt;span class="c"&gt;# add PROVCHART_API_KEY from https://chart.devtem.org/dashboard&lt;/span&gt;

&lt;span class="c"&gt;# Terminal 1 — proxy&lt;/span&gt;
npm run proxy

&lt;span class="c"&gt;# Terminal 2 — Vite&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:5173&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free tier note:&lt;/strong&gt; ProvChart free accounts get a small number of generations per month.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this approach works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First paint&lt;/strong&gt; — charts appear with the CSS; no wait for a chart library.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle size&lt;/strong&gt; — zero charting JS on the page.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crawlability&lt;/strong&gt; — the chart structure is real HTML.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme control&lt;/strong&gt; — st-core tokens + ProvChart &lt;code&gt;theme: "midnight"&lt;/code&gt; keep the look consistent.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; — API key only lives on the proxy / edge function.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You still get dynamic period switching and multi-series visuals; you just don’t pay the usual JavaScript tax.&lt;/p&gt;




&lt;h2&gt;
  
  
  Repo &amp;amp; next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template:&lt;/strong&gt; &lt;a href="https://github.com/Figsh/dashboard-project" rel="noopener noreferrer"&gt;github.com/Figsh/dashboard-project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ProvChart:&lt;/strong&gt; &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;st-core.fscss&lt;/a&gt;:&lt;/strong&gt; underlying token/chart primitives used by the design system
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fork it, swap the datasets, point the proxy at your own backend, or pre-generate SVGs with the ProvChart GitHub Action for static docs.&lt;/p&gt;

&lt;p&gt;Lightweight dashboards don’t have to look lightweight. With ProvChart and a solid token set, they can look like a product — and still load like a static page.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>provchart</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Generate pure CSS charts from Claude (or any MCP client)</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Mon, 31 Aug 2026 08:18:10 +0000</pubDate>
      <link>https://dev.to/provchart/generate-pure-css-charts-from-claude-or-any-mcp-client-2nei</link>
      <guid>https://dev.to/provchart/generate-pure-css-charts-from-claude-or-any-mcp-client-2nei</guid>
      <description>&lt;p&gt;Ask an agent for a chart today and it'll usually hand you a Chart.js config, or some SVG with hand-typed coordinates. Fine to look at in a chat window, but the moment you actually want it somewhere real, a README, a docs page, a landing page, you're back to pasting code into a sandbox, while the real UI still needed JS libs (or a screenshot).&lt;/p&gt;

&lt;p&gt;So ProvChart now has an MCP server: the agent calls a tool and gets back &lt;strong&gt;real HTML/CSS or SVG&lt;/strong&gt;, not a sketch of one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Package
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/provchart-mcp" rel="noopener noreferrer"&gt;provchart-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Figsh/provchart-mcp" rel="noopener noreferrer"&gt;Figsh/provchart-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Overview: &lt;a href="https://chart.devtem.org/agents" rel="noopener noreferrer"&gt;chart.devtem.org/agents&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;provchart_generate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;html&lt;/code&gt; + &lt;code&gt;css&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;provchart_generate_svg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;svg&lt;/code&gt; + &lt;code&gt;dataUri&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;provchart_explain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short notes (no API call)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Paint doesn't need a client chart runtime. The optional &lt;a href="https://www.npmjs.com/package/provchart-runtime" rel="noopener noreferrer"&gt;provchart-runtime&lt;/a&gt; only adds hover/tooltips on HTML charts, nothing required for the chart to just render.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Desktop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"provchart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"provchart-mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"PROVCHART_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pc_live_xxxxxxxx"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key from &lt;a href="https://chart.devtem.org/dashboard" rel="noopener noreferrer"&gt;chart.devtem.org/dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt that works
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Monthly signups: Jan 120, Feb 450, Mar 3200, Apr 8900. SVG for the README.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Values can be real numbers, not squeezed onto a 0–100 scale. Optional &lt;code&gt;min&lt;/code&gt; / &lt;code&gt;max&lt;/code&gt; if you want a fixed axis instead of auto-scaling.&lt;/p&gt;

&lt;h3&gt;
  
  
  After the tool returns
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;HTML:&lt;/strong&gt; inject &lt;code&gt;css&lt;/code&gt; into a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag, &lt;code&gt;html&lt;/code&gt; into a container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SVG:&lt;/strong&gt; write it to a file and use &lt;code&gt;![alt](./charts/name.svg)&lt;/code&gt; in Markdown. Better than a huge data URI sitting in your README source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Related automation
&lt;/h3&gt;

&lt;p&gt;If you want charts without chatting to an agent for each one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/fscss-ttr/provchart-readme-action" rel="noopener noreferrer"&gt;provchart-readme-action&lt;/a&gt; — JSON config → SVG in CI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fscss-ttr/provchart-repo-stats" rel="noopener noreferrer"&gt;provchart-repo-stats&lt;/a&gt; — stars / languages → SVG for the README, pulled straight from GitHub's API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Site: &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt; · Docs: &lt;a href="https://chart.devtem.org/docs" rel="noopener noreferrer"&gt;chart.devtem.org/docs&lt;/a&gt;&lt;/p&gt;




</description>
      <category>mcp</category>
      <category>opensource</category>
      <category>provchart</category>
      <category>ai</category>
    </item>
    <item>
      <title>Automated GitHub repo stats as SVG in the README</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Sun, 30 Aug 2026 16:51:53 +0000</pubDate>
      <link>https://dev.to/provchart/automated-github-repo-stats-as-svg-in-the-readme-253i</link>
      <guid>https://dev.to/provchart/automated-github-repo-stats-as-svg-in-the-readme-253i</guid>
      <description>&lt;p&gt;Most README “stats” badges are either fixed images or another third-party widget. I wanted something simpler: &lt;strong&gt;read the repo from GitHub’s API, turn the numbers into SVG files in the repo, embed them like any image.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s what &lt;a href="https://github.com/fscss-ttr/provchart-repo-stats" rel="noopener noreferrer"&gt;ProvChart Repo Stats&lt;/a&gt; does.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it writes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Default chart&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docs/charts/repo-overview.svg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stars, forks, watchers, open issues&lt;/td&gt;
&lt;td&gt;area (real counts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docs/charts/languages.svg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Language share&lt;/td&gt;
&lt;td&gt;hbar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No hand-maintained chart JSON for those two.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Add a repo secret &lt;code&gt;PROVCHART_API_KEY&lt;/code&gt; (key from &lt;a href="https://chart.devtem.org/dashboard" rel="noopener noreferrer"&gt;chart.devtem.org/dashboard&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Workflow:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Repo stats charts&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;8&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0"&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;charts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fscss-ttr/provchart-repo-stats@v1.3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PROVCHART_API_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;PROVCHART_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PROVCHART_API_KEY }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stefanzweifel/git-auto-commit-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;commit_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chore:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;update&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;repo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stats&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;charts"&lt;/span&gt;
          &lt;span class="na"&gt;file_pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs/charts/*.svg"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;README:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Overview&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./docs/charts/repo-overview.svg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Languages&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./docs/charts/languages.svg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the workflow once (or wait for the schedule). The SVGs commit back; the README stays normal Markdown.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defaults worth knowing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overview&lt;/strong&gt; uses real counts (not forced to 0–100).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages&lt;/strong&gt; use share %.&lt;/li&gt;
&lt;li&gt;You can switch types (&lt;code&gt;overview-type&lt;/code&gt;, &lt;code&gt;languages-type&lt;/code&gt;) if you prefer bars.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Charts are generated with &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;ProvChart&lt;/a&gt; (&lt;code&gt;generate-svg&lt;/code&gt;). Related: &lt;a href="https://github.com/fscss-ttr/provchart-readme-action" rel="noopener noreferrer"&gt;provchart-readme-action&lt;/a&gt; if you already have a &lt;code&gt;.provchart/charts.json&lt;/code&gt; and want fixed sample charts instead of live repo metrics.&lt;/p&gt;

&lt;p&gt;Feedback and issues: &lt;a href="https://github.com/fscss-ttr/provchart-repo-stats" rel="noopener noreferrer"&gt;github.com/fscss-ttr/provchart-repo-stats&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>github</category>
      <category>devops</category>
      <category>opensource</category>
      <category>provchart</category>
    </item>
    <item>
      <title>Keep README charts in sync with a small GitHub Action</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Fri, 28 Aug 2026 13:32:25 +0000</pubDate>
      <link>https://dev.to/provchart/keep-readme-charts-in-sync-with-a-small-github-action-5akc</link>
      <guid>https://dev.to/provchart/keep-readme-charts-in-sync-with-a-small-github-action-5akc</guid>
      <description>&lt;p&gt;I got tired of updating chart screenshots in READMEs by hand.&lt;/p&gt;

&lt;p&gt;Either the numbers were stale, or I’d regenerate an image, commit it, and forget the next week. For docs and GitHub READMEs I don’t need a full chart library on the page — I need a &lt;strong&gt;vector file&lt;/strong&gt; that still looks sharp.&lt;/p&gt;

&lt;p&gt;So I wired ProvChart’s SVG API into a GitHub Action.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You describe charts in &lt;code&gt;.provchart/charts.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On schedule or &lt;code&gt;workflow_dispatch&lt;/code&gt;, the Action calls the API
&lt;/li&gt;
&lt;li&gt;It writes &lt;code&gt;.svg&lt;/code&gt; files into the repo (e.g. &lt;code&gt;docs/charts/&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;A commit step pushes them if anything changed
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;README stays simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;API traffic&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./docs/charts/demo.svg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7a5twtx9rokqhh1fmu3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7a5twtx9rokqhh1fmu3.jpg" alt="API traffic: ProvChart svg" width="720" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No Chart.js in the README. No screenshot pipeline.&lt;/p&gt;
&lt;h3&gt;
  
  
  Minimal setup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Secret:&lt;/strong&gt; &lt;code&gt;PROVCHART_API_KEY&lt;/code&gt; (from the ProvChart dashboard)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Config&lt;/strong&gt; (array of payloads):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"area"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"midnight"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"axisX"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Mon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fri"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sun"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"series"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Requests"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#8b7bff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"points"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Workflow sketch:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fscss-ttr/provchart-readme-action@v1.0.4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PROVCHART_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.provchart/charts.json&lt;/span&gt;
    &lt;span class="na"&gt;output-dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs/charts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then auto-commit the &lt;code&gt;docs/charts/*.svg&lt;/code&gt; files if you want.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why this shape
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SVG&lt;/strong&gt; fits Markdown and docs better than a heavy client chart stack
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI&lt;/strong&gt; owns refresh; the README only references files
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same JSON model&lt;/strong&gt; as the rest of ProvChart (line, area, bar, gauge, …)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gauges at about &lt;strong&gt;220×220&lt;/strong&gt; work well in a README without taking over the page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Try without starting from zero
&lt;/h3&gt;

&lt;p&gt;Sample repo (fork → add the secret → run the workflow):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Figsh/provchart-charts" rel="noopener noreferrer"&gt;github.com/Figsh/provchart-charts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/fscss-ttr/provchart-readme-action" rel="noopener noreferrer"&gt;github.com/fscss-ttr/provchart-readme-action&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Not a replacement for interactive dashboards. Just a practical way to keep &lt;strong&gt;docs charts&lt;/strong&gt; honest without babysitting PNGs.&lt;/p&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/fscss-ttr" rel="noopener noreferrer"&gt;
        fscss-ttr
      &lt;/a&gt; / &lt;a href="https://github.com/fscss-ttr/provchart-readme-action" rel="noopener noreferrer"&gt;
        provchart-readme-action
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      CI generates SVG charts and commits them for README/docs
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ProvChart README Action&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Generate vector &lt;strong&gt;SVG&lt;/strong&gt; charts with the &lt;a href="https://chart.devtem.org" rel="nofollow noopener noreferrer"&gt;ProvChart&lt;/a&gt; API inside &lt;strong&gt;GitHub Actions&lt;/strong&gt;, then commit them for live README and docs embeds.&lt;/p&gt;

&lt;p&gt;No Chart.js. No screenshot bots. Pure SVG from JSON.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;table&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;&lt;strong&gt;Action&lt;/strong&gt;&lt;/td&gt;

&lt;td&gt;&lt;code&gt;fscss-ttr/provchart-readme-action&lt;/code&gt;&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;&lt;strong&gt;Sample repo (fork &amp;amp; test)&lt;/strong&gt;&lt;/td&gt;

&lt;td&gt;&lt;a href="https://github.com/Figsh/provchart-charts/" rel="noopener noreferrer"&gt;Figsh/provchart-charts&lt;/a&gt;&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;&lt;strong&gt;API&lt;/strong&gt;&lt;/td&gt;

&lt;td&gt;&lt;a href="https://chart.devtem.org" rel="nofollow noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt;&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;

&lt;td&gt;MIT&lt;/td&gt;

&lt;/tr&gt;

&lt;/tbody&gt;

&lt;/table&gt;&lt;/div&gt;&lt;br&gt;
&lt;p&gt;&lt;/p&gt;


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Try in 2 minutes (recommended)&lt;/h2&gt;
&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fork&lt;/strong&gt; &lt;a href="https://github.com/Figsh/provchart-charts/" rel="noopener noreferrer"&gt;github.com/Figsh/provchart-charts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add a repository secret: &lt;strong&gt;&lt;code&gt;PROVCHART_API_KEY&lt;/code&gt;&lt;/strong&gt;
(Dashboard → &lt;a href="https://chart.devtem.org/dashboard" rel="nofollow noopener noreferrer"&gt;Developer API&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actions&lt;/strong&gt; → run &lt;strong&gt;Test ProvChart Action&lt;/strong&gt; (workflow_dispatch)&lt;/li&gt;
&lt;li&gt;SVGs land under &lt;code&gt;docs/charts/&lt;/code&gt; and are committed automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That repo already has &lt;code&gt;.provchart/charts.json&lt;/code&gt;, the workflow, and README embeds.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start (your own repo)&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. API key secret&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Settings → Secrets and variables → Actions → New repository secret&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;table&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;th&gt;Name&lt;/th&gt;

&lt;th&gt;Value&lt;/th&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;&lt;code&gt;PROVCHART_API_KEY&lt;/code&gt;&lt;/td&gt;

&lt;td&gt;Your ProvChart API key&lt;/td&gt;

&lt;/tr&gt;

&lt;/tbody&gt;

&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;2. Config — &lt;code&gt;.provchart/charts.json&lt;/code&gt;
&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;[
  {
    &lt;span class="pl-ent"&gt;"file"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;demo.svg&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
    &lt;span class="pl-ent"&gt;"type"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;area&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-ent"&gt;"theme"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;midnight&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-ent"&gt;"width"&lt;/span&gt;: &lt;span class="pl-c1"&gt;640&lt;/span&gt;,
    &lt;span class="pl-ent"&gt;"height"&lt;/span&gt;: &lt;span class="pl-c1"&gt;320&lt;/span&gt;,
    &lt;span class="pl-ent"&gt;"axisX"&lt;/span&gt;: [&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/fscss-ttr/provchart-readme-action" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>devops</category>
      <category>github</category>
      <category>productivity</category>
      <category>provchart</category>
    </item>
    <item>
      <title>How to ship interactive charts and still aim for 100 performance</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Wed, 26 Aug 2026 11:57:55 +0000</pubDate>
      <link>https://dev.to/provchart/how-to-ship-interactive-charts-and-still-aim-for-100-performance-1mbp</link>
      <guid>https://dev.to/provchart/how-to-ship-interactive-charts-and-still-aim-for-100-performance-1mbp</guid>
      <description>&lt;p&gt;Interactive charts and a perfect Lighthouse run often feel incompatible. The usual path is a 50–150 KB chart library, hydration, and a blank box until JavaScript finishes. You can still offer &lt;strong&gt;real visuals and light interaction&lt;/strong&gt; without paying that tax on every page load.&lt;/p&gt;

&lt;p&gt;This article is about a &lt;strong&gt;paint-first&lt;/strong&gt; pipeline: compile data to HTML/CSS, let the browser draw on first paint, then add interaction as progressive enhancement.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real performance problem
&lt;/h2&gt;

&lt;p&gt;Core Web Vitals care about what blocks the main thread and what delays useful pixels.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Typical JS chart stack&lt;/th&gt;
&lt;th&gt;Paint-first CSS charts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Download / parse&lt;/td&gt;
&lt;td&gt;Chart library + adapters&lt;/td&gt;
&lt;td&gt;No chart library for paint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First paint&lt;/td&gt;
&lt;td&gt;Often after JS runs&lt;/td&gt;
&lt;td&gt;Chart HTML/CSS in the first response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SEO / first crawl&lt;/td&gt;
&lt;td&gt;Depends on hydration&lt;/td&gt;
&lt;td&gt;Markup present immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interaction&lt;/td&gt;
&lt;td&gt;Built into the library&lt;/td&gt;
&lt;td&gt;Optional small runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;“100 performance” is never a guarantee—fonts, images, and third parties still matter—but &lt;strong&gt;removing chart-runtime JS from the critical path&lt;/strong&gt; is one of the highest-leverage wins on dashboard-style pages.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pipeline: compile → paint → enhance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Compile
&lt;/h3&gt;

&lt;p&gt;Send JSON to a generator (build step or API). Example with &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;ProvChart&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://provchart-api.devtem.org/api/v1/generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROVCHART_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;area&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Traffic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#4fd8c4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Signups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#f0a860&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fri&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sun&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get &lt;strong&gt;scoped HTML + CSS&lt;/strong&gt; (custom properties, &lt;code&gt;clip-path&lt;/code&gt;, bars, gauges)—not a client chart framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Paint
&lt;/h3&gt;

&lt;p&gt;Inject on the server or at the start of the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;beforeend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;style&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/style&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On first paint the chart is already geometry in CSS. No &lt;code&gt;new Chart(ctx, …)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Enhance (optional)
&lt;/h3&gt;

&lt;p&gt;Interaction does not have to ride in the same bundle as rendering.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/fscss-ttr/provchart-runtime" rel="noopener noreferrer"&gt;ProvChart Runtime&lt;/a&gt; is a separate MIT script: tooltips, scroll reveal, legend focus. Charts work without it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProvChartRuntimeConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tooltips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;perPointTooltips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;excludeSvg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/provchart-runtime@1.2.0/dist/provchart-runtime.min.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pattern: &lt;strong&gt;render is free of chart-library JS; UX is progressive.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What “interactive” can mean without a heavy library
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Lightweight approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hover values&lt;/td&gt;
&lt;td&gt;Runtime tooltips / CSS hits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legend focus&lt;/td&gt;
&lt;td&gt;Dim other series on legend hover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live data&lt;/td&gt;
&lt;td&gt;Rewrite CSS variables or re-call generate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;README / docs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;generate-svg&lt;/code&gt; → commit &lt;code&gt;.svg&lt;/code&gt; (no client runtime)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full brush/zoom/analytics&lt;/td&gt;
&lt;td&gt;Reserve a JS library for &lt;em&gt;that&lt;/em&gt; route only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most marketing pages, pricing metrics, and ops KPI strips need the first column—not a full analytics suite.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checklist toward a top performance score
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No chart library on the critical path&lt;/strong&gt; — generate HTML/CSS ahead of time or via API; inline or ship only scoped CSS.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defer enhancement&lt;/strong&gt; — &lt;code&gt;defer&lt;/code&gt; / &lt;code&gt;type="module"&lt;/code&gt; for runtime; charts remain visible if it fails.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect &lt;code&gt;prefers-reduced-motion&lt;/code&gt;&lt;/strong&gt; — skip transform theatre when users ask.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep images honest&lt;/strong&gt; — LCP is often a hero image, not the chart; optimize that separately.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don’t block on fonts&lt;/strong&gt; — &lt;code&gt;font-display: swap&lt;/code&gt; for display type.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split the product&lt;/strong&gt; — paint-first charts on public pages; heavy JS charts only behind “Analytics” where interaction earns the KB.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt; — short text summary + optional table (&lt;a href="https://chart.devtem.org/guides/accessible-charts" rel="noopener noreferrer"&gt;accessible charts&lt;/a&gt;).
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Static sites and agents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSG:&lt;/strong&gt; generate charts at build time; commit HTML/CSS or call the API in CI.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;README:&lt;/strong&gt; prefer SVG file embeds over giant data URIs (&lt;a href="https://chart.devtem.org/guides/charts-in-markdown" rel="noopener noreferrer"&gt;Markdown guide&lt;/a&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents:&lt;/strong&gt; stable JSON in → markup out is easy for ChatGPT/Claude tool calls—no browser chart API required.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When you should still use a JS chart library
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brushing, zooming, streaming ticks
&lt;/li&gt;
&lt;li&gt;Dozens of coordinated views
&lt;/li&gt;
&lt;li&gt;Exotic types your CSS engine doesn’t cover
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use them &lt;strong&gt;deliberately&lt;/strong&gt;, on routes that justify the cost—not as the default for every sparkline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Build or API-generate a chart → inject &lt;code&gt;html&lt;/code&gt; + &lt;code&gt;css&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Confirm the chart shows with JS disabled (enhancement only).
&lt;/li&gt;
&lt;li&gt;Add runtime only if you want tooltips/motion.
&lt;/li&gt;
&lt;li&gt;Re-run Lighthouse on mobile; watch JS execution time and LCP.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://chart.devtem.org/docs" rel="noopener noreferrer"&gt;chart.devtem.org/docs&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Gallery:&lt;/strong&gt; &lt;a href="https://chart.devtem.org/gallery" rel="noopener noreferrer"&gt;chart.devtem.org/gallery&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;a href="https://github.com/fscss-ttr/provchart-runtime" rel="noopener noreferrer"&gt;github.com/fscss-ttr/provchart-runtime&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Maximize the visual by maximizing what the &lt;strong&gt;browser already paints&lt;/strong&gt;. Compile data to CSS, ship interaction as an optional layer, and keep heavy chart runtimes off pages that don’t need them. That’s how interactive-looking charts stay compatible with serious performance budgets.&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>backend</category>
      <category>performance</category>
      <category>provchart</category>
    </item>
    <item>
      <title>Pure CSS Charts + Drop-in Interactivity: ProvChart API &amp; Runtime</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Tue, 25 Aug 2026 16:34:50 +0000</pubDate>
      <link>https://dev.to/provchart/pure-css-charts-drop-in-interactivity-provchart-api-runtime-529b</link>
      <guid>https://dev.to/provchart/pure-css-charts-drop-in-interactivity-provchart-api-runtime-529b</guid>
      <description>&lt;p&gt;Most chart libraries tax every page: 40–150 KB of JS, hydration delay, and markup crawlers never see. &lt;strong&gt;ProvChart&lt;/strong&gt; takes another path—data becomes HTML and CSS (custom properties + &lt;code&gt;clip-path&lt;/code&gt;). &lt;strong&gt;ProvChart Runtime&lt;/strong&gt; is an optional script that adds tooltips, motion, and legend focus without changing how charts are generated.&lt;/p&gt;

&lt;p&gt;This post walks through a minimal setup: generate an area chart from the API, inject it, and let the runtime enhance it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ProvChart API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;POST /api/v1/generate&lt;/code&gt; → &lt;code&gt;{ html, css }&lt;/code&gt; — zero chart-library runtime on the page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ProvChart Runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scans &lt;code&gt;[data-provchart]&lt;/code&gt;, adds hover tooltips, scroll reveal, legend dimming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;SVG API&lt;/strong&gt; (optional)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;POST /api/v1/generate-svg&lt;/code&gt; for README/docs — runtime &lt;strong&gt;skips&lt;/strong&gt; pure SVG by design&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Repo / install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/fscss-ttr/provchart-runtime" rel="noopener noreferrer"&gt;github.com/fscss-ttr/provchart-runtime&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm i provchart-runtime&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CDN: jsDelivr or unpkg
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Product: &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Get an API key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sign in at &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Dashboard → Developer API&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Create a key
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Signed-up free accounts get a monthly test quota. HTML and SVG generations share the same pool.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Minimal page
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;ProvChart + Runtime&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0c0a16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system-ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;#chart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;640px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;92vw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;.err&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ff5e7d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProvChartRuntimeConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;revealOnScroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tooltips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;perPointTooltips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-provchart]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;excludeSvg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/provchart-runtime@1.0.0/dist/provchart-runtime.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"chart"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://provchart-api.devtem.org/api/v1/generate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;area&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Traffic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#4fd8c4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Signups&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f0a860&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Wed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Thu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fri&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sun&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Generate failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;provchart-style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;provchart-style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Optional: only if your build exposes it&lt;/span&gt;
      &lt;span class="c1"&gt;// window.ProvChartRuntime?.scan?.();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p class="err"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_API_KEY&lt;/code&gt;. After load you should see a two-series area chart; hover legend/points if the runtime attached correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. How the runtime finds charts
&lt;/h2&gt;

&lt;p&gt;Generated roots look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pc-chart-…"&lt;/span&gt; &lt;span class="na"&gt;data-provchart=&lt;/span&gt;&lt;span class="s"&gt;"area"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;…&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtime:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Queries &lt;code&gt;[data-provchart]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Skips pure SVG embeds when &lt;code&gt;excludeSvg: true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uses a &lt;code&gt;MutationObserver&lt;/code&gt; so charts injected via &lt;code&gt;fetch&lt;/code&gt; still get enhanced
&lt;/li&gt;
&lt;li&gt;Reads series points from CSS variables like &lt;code&gt;--pc-{id}-s1-p1&lt;/code&gt; for per-point tooltips
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You don’t call into ProvChart’s core from the client for paint—the browser already painted CSS.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Config worth knowing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProvChartRuntimeConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;revealOnScroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tooltips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;perPointTooltips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;countUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// gauge / stat numbers&lt;/span&gt;
  &lt;span class="na"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// watch DOM for new charts&lt;/span&gt;
  &lt;span class="na"&gt;excludeSvg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// leave README SVGs alone&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define this &lt;strong&gt;before&lt;/strong&gt; the runtime script tag.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. HTML/CSS vs SVG
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;App UI, dashboard, marketing page&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/v1/generate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub README, static docs asset&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/v1/generate-svg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No (commit &lt;code&gt;.svg&lt;/code&gt; or data URI)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same payload shape (&lt;code&gt;type&lt;/code&gt;, &lt;code&gt;series&lt;/code&gt;, &lt;code&gt;axisX&lt;/code&gt;); different output.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Why this split matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; no Chart.js-sized dependency to parse before first paint
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEO:&lt;/strong&gt; chart structure is in HTML/CSS on first response
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive enhancement:&lt;/strong&gt; runtime is optional; charts work without it
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs vs apps:&lt;/strong&gt; SVG for Markdown hosts; HTML/CSS + runtime for product UI
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;ProvChart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chart.devtem.org/docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chart.devtem.org/gallery" rel="noopener noreferrer"&gt;Gallery&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fscss-ttr/provchart-runtime" rel="noopener noreferrer"&gt;Runtime on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/provchart-runtime" rel="noopener noreferrer"&gt;npm: provchart-runtime&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;generate charts as HTML and CSS, ship them with almost no client chart code, and add interactivity only when you want it—with a single script tag&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>node</category>
      <category>provchart</category>
    </item>
    <item>
      <title>Embed Charts in Markdown Without Chart.js: SVG, Data URIs, and What Actually Works</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:08:20 +0000</pubDate>
      <link>https://dev.to/provchart/embed-charts-in-markdown-without-chartjs-svg-data-uris-and-what-actually-works-39pi</link>
      <guid>https://dev.to/provchart/embed-charts-in-markdown-without-chartjs-svg-data-uris-and-what-actually-works-39pi</guid>
      <description>&lt;p&gt;Markdown is great until you want a &lt;strong&gt;real chart&lt;/strong&gt; in a README or docs site. Most people reach for a screenshot, a hosted PNG, or a heavy client library. ProvChart takes another path: &lt;strong&gt;&lt;code&gt;POST /api/v1/generate-svg&lt;/code&gt;&lt;/strong&gt; returns a self-contained SVG (and optional data URI) from your data—no js on the page, no image CDN required.&lt;/p&gt;

&lt;p&gt;This article covers &lt;strong&gt;what different Markdown platforms allow&lt;/strong&gt;, practical embed patterns, and when pure SVG is the better default.&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Send series data to ProvChart’s SVG endpoint.
&lt;/li&gt;
&lt;li&gt;Get back:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;svg&lt;/code&gt; — full &lt;code&gt;&amp;lt;svg xmlns="..."&amp;gt;...&amp;lt;/svg&amp;gt;&lt;/code&gt; string
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dataUri&lt;/code&gt; — &lt;code&gt;data:image/svg+xml;base64,...&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Embed with whatever your platform supports.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Platform reality: not every Markdown engine is equal
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;GitHub README&lt;/th&gt;
&lt;th&gt;Many static docs&lt;/th&gt;
&lt;th&gt;Personal / controlled MD&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Data URI&lt;/strong&gt; &lt;code&gt;![](data:image/svg+xml;...)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Often fragile&lt;/td&gt;
&lt;td&gt;Sometimes OK&lt;/td&gt;
&lt;td&gt;Often OK&lt;/td&gt;
&lt;td&gt;Long base64 can break or get sanitized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;&amp;lt;img src="data:..."&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Varies&lt;/td&gt;
&lt;td&gt;Often OK&lt;/td&gt;
&lt;td&gt;Same length / sanitize issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;File&lt;/strong&gt; &lt;code&gt;![](./chart.svg)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Reliable&lt;/td&gt;
&lt;td&gt;✅ Reliable&lt;/td&gt;
&lt;td&gt;✅ Best default&lt;/td&gt;
&lt;td&gt;Commit the SVG; no key in the repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inline &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Usually stripped&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;td&gt;✅ If HTML allowed&lt;/td&gt;
&lt;td&gt;Great for MDX / some SSGs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Raster PNG/WebP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Use only if the host blocks SVG&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Prefer a &lt;strong&gt;committed &lt;code&gt;.svg&lt;/code&gt; file&lt;/strong&gt; for GitHub and public docs. Use &lt;strong&gt;data URIs&lt;/strong&gt; for quick demos. Use &lt;strong&gt;inline SVG&lt;/strong&gt; only where the engine allows raw HTML (MDX, some wikis, your own site).&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 1 — Generate SVG (API)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://provchart-api.devtem.org/api/v1/generate-svg"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "type": "line",
    "series": [
      { "name": "Stars", "color": "#8b7bff", "points": [12, 18, 25, 40, 55] }
    ],
    "axisX": ["Jan", "Feb", "Mar", "Apr", "May"],
    "width": 640,
    "height": 280
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"svg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;svg xmlns=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;http://www.w3.org/2000/svg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ...&amp;gt;...&amp;lt;/svg&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dataUri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data:image/svg+xml;base64,..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the key in CI secrets.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 2 — Embed options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A. Image from file (recommended for README)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Stars over time&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./docs/charts/stars.svg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the &lt;code&gt;svg&lt;/code&gt; field from the API into that path and commit it.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Data URI (quick, not always portable)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Stars over time&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;data:image/svg+xml;base64,AAAA...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the preview is blank, the host likely truncated or blocked the URI—switch to a file.&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Inline SVG (when HTML is allowed)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- MDX / some static generators --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- paste the svg string from the API --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub README will generally &lt;strong&gt;not&lt;/strong&gt; render arbitrary inline SVG.&lt;/p&gt;

&lt;h3&gt;
  
  
  D. Raster only if you must
&lt;/h3&gt;

&lt;p&gt;If a platform blocks SVG entirely, convert once in your pipeline (e.g. local tool) and commit PNG. That’s a fallback—not the default for &lt;em&gt;your&lt;/em&gt; docs site, where SVG is usually better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Advantages of the SVG path
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No chart runtime in the doc UI&lt;/strong&gt; — readers don’t download Chart.js to see a trend.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No third-party image host&lt;/strong&gt; — nothing uploaded to a random CDN for a badge.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharp at any zoom&lt;/strong&gt; — useful for docs and retina displays.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same data model as HTML charts&lt;/strong&gt; — &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;series&lt;/code&gt;, &lt;code&gt;axisX&lt;/code&gt; match ProvChart’s generate API.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI-friendly&lt;/strong&gt; — regenerate &lt;code&gt;docs/charts/*.svg&lt;/code&gt; when metrics change; commit the artifact.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fits the “pipeline” story&lt;/strong&gt; — compile data → paint geometry; for Markdown the paint target is SVG instead of CSS-in-page.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  When pure SVG is the better choice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Personal knowledge bases, Notion-export-style vaults, and &lt;strong&gt;static&lt;/strong&gt; doc sites you control
&lt;/li&gt;
&lt;li&gt;GitHub/GitLab &lt;strong&gt;asset&lt;/strong&gt; charts (versioned next to the repo)
&lt;/li&gt;
&lt;li&gt;Design systems / architecture READMEs that should stay dependency-light
&lt;/li&gt;
&lt;li&gt;Agent or cron jobs that refresh charts without opening a browser
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;strong&gt;HTML + CSS&lt;/strong&gt; (&lt;code&gt;/api/v1/generate&lt;/code&gt;) when the chart lives &lt;strong&gt;inside a web app&lt;/strong&gt; where theme tokens and layout already use CSS.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minimal Node helper (write a file)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://provchart-api.devtem.org/api/v1/generate-svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROVCHART_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;area&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Views&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#4fd8c4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fri&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;docs/charts/views.svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point Markdown at &lt;code&gt;./docs/charts/views.svg&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Broken image in README&lt;/td&gt;
&lt;td&gt;Data URI too long / blocked&lt;/td&gt;
&lt;td&gt;Commit &lt;code&gt;.svg&lt;/code&gt; + relative path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;401&lt;/td&gt;
&lt;td&gt;Bad or revoked key&lt;/td&gt;
&lt;td&gt;New key in Dashboard → Developer API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;429&lt;/td&gt;
&lt;td&gt;Monthly limit&lt;/td&gt;
&lt;td&gt;Upgrade plan or wait for reset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty graphic&lt;/td&gt;
&lt;td&gt;Bad payload&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;series[].points&lt;/code&gt; and &lt;code&gt;axisX&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SVG API: &lt;a href="https://chart.devtem.org/docs#dev-api-svg" rel="noopener noreferrer"&gt;ProvChart docs – generate-svg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guide: &lt;a href="https://chart.devtem.org/guides/charts-in-markdown" rel="noopener noreferrer"&gt;Charts in Markdown &amp;amp; docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pricing (quota): &lt;a href="https://chart.devtem.org/pricing" rel="noopener noreferrer"&gt;chart.devtem.org/pricing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Markdown support for &lt;code&gt;data:&lt;/code&gt; URIs is inconsistent. For public README and most docs, &lt;strong&gt;generate SVG - save file - &lt;code&gt;![](./chart.svg)&lt;/code&gt;&lt;/strong&gt;. Use data URIs for experiments; use inline SVG only where your engine allows HTML. That’s how you get charts in docs.&lt;/p&gt;
&lt;/blockquote&gt;




</description>
      <category>markdown</category>
      <category>documentation</category>
      <category>showdev</category>
      <category>provchat</category>
    </item>
    <item>
      <title>Email Charts with the ProvChart API</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Sun, 23 Aug 2026 10:41:23 +0000</pubDate>
      <link>https://dev.to/provchart/email-charts-with-the-provchart-api-3gni</link>
      <guid>https://dev.to/provchart/email-charts-with-the-provchart-api-3gni</guid>
      <description>&lt;p&gt;Most chart libraries assume a browser that runs JavaScript. Email clients don't give you that. If you want a chart in a weekly digest, you need something that renders without a script tag, or a step that turns a chart into an image before it ever reaches an inbox.&lt;/p&gt;

&lt;p&gt;That's the problem ProvChart's API is built for. It returns HTML and CSS, generated server-side, and you decide from there whether to rasterize it into a PNG or embed a tightly controlled snippet. This post walks through the flow, the chart types that actually work in a digest, and a handful of practical tips for running this in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why email is different
&lt;/h2&gt;

&lt;p&gt;The inbox never needs to see your API key or your chart library. It only needs a static image and a link back to the live dashboard. Everything else, the data pull, the payload mapping, the API call, happens on your server or in a worker, before the send.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pull analytics for the period, from your API, warehouse, or database&lt;/li&gt;
&lt;li&gt;Map the rows into a ProvChart payload (&lt;code&gt;type&lt;/code&gt;, &lt;code&gt;series&lt;/code&gt;, &lt;code&gt;axisX&lt;/code&gt;, &lt;code&gt;theme&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/generate&lt;/code&gt; with your API key&lt;/li&gt;
&lt;li&gt;Turn the returned &lt;code&gt;html&lt;/code&gt; and &lt;code&gt;css&lt;/code&gt; into a PNG (or store the snippet)&lt;/li&gt;
&lt;li&gt;Embed the result in your email template with a CTA back to the dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Mapping analytics to a payload
&lt;/h2&gt;

&lt;p&gt;Keep a single mapper function so every report type shares the same shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;analyticsToPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Revenue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8b7bff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;revenueByDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#4fd8c4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ordersByDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;revenueByDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#0c0a16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1628&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#eae7f5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#837da0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8b7bff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap the data source underneath it, but keep the function stable. Weekly, monthly, and alert emails all stay consistent this way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chart types that actually work in a digest
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trend over 7–30 days&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;line&lt;/code&gt; / &lt;code&gt;area&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Readable as a simple story&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Channel or category mix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Goal or conversion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gauge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One number, high impact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Share of total&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stackedbar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Good for mix breakdowns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Skip dense scatter plots or multi-combo charts in email. Save those for the product UI, where someone can actually zoom in and interact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flveliy5zlw748gamt9ym.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flveliy5zlw748gamt9ym.jpg" alt="ProvChart bar chart example" width="720" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ten things worth doing
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generate server-side.&lt;/strong&gt; The send job or worker owns the API key, the payload, and the output. Never call the API from the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer images for email clients.&lt;/strong&gt; The reliable path is: analytics → API → html/css → headless render (Playwright or Puppeteer) → PNG → CDN → &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; in the template. This works across Gmail, Outlook, and Apple Mail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for one glance.&lt;/strong&gt; Each chart should answer one question: did revenue rise, which channel led. Cap it at one or two series and 7–14 data points. Put the headline number in the subject line or preheader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache by payload hash.&lt;/strong&gt; Identical type, series, labels, and theme should produce identical output. Cache the html/css or the PNG so repeat sends don't burn quota.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check usage before bulk sends.&lt;/strong&gt; Hit &lt;code&gt;GET /api/v1/usage&lt;/code&gt; first. If &lt;code&gt;remaining&lt;/code&gt; is low, drop the optional charts or fall back to number-only blocks rather than hitting &lt;code&gt;MONTHLY_LIMIT_REACHED&lt;/code&gt; mid-campaign.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme for email, not the admin UI.&lt;/strong&gt; Use higher contrast and a background that matches your template. A dedicated &lt;code&gt;theme&lt;/code&gt; object for digests versus in-app charts goes a long way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always add a CTA.&lt;/strong&gt; A simple "Full dashboard →" link under the chart. The email is the snapshot; the app is where people explore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep one layout per digest type.&lt;/strong&gt; Hero metric, one trend chart, one breakdown, a few bullets, then the CTA. Change the data, not the structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate before you generate.&lt;/strong&gt; Empty series, non-numeric points, or oversized arrays waste generations. Enforce max points and required fields in the mapper itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy the key.&lt;/strong&gt; Production frontends shouldn't hold the secret key at all. Route browser requests through your own backend before they reach ProvChart.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A minimal server-side example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyticsToPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weeklyReport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://provchart-api.devtem.org/api/v1/generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROVCHART_API_KEY&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Option A: rasterize data.html + data.css into a PNG, upload it, then &amp;lt;img src="..."&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// Option B: store the snippet for a web version of the same report&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Do / Don't
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate server-side&lt;/li&gt;
&lt;li&gt;Map analytics to a fixed payload shape&lt;/li&gt;
&lt;li&gt;Cache by hash&lt;/li&gt;
&lt;li&gt;Prefer line, bar, or gauge charts&lt;/li&gt;
&lt;li&gt;Link back to the live dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call the API from the inbox&lt;/li&gt;
&lt;li&gt;Build ad-hoc JSON for every send&lt;/li&gt;
&lt;li&gt;Regenerate identical charts&lt;/li&gt;
&lt;li&gt;Pack five dense series into a digest&lt;/li&gt;
&lt;li&gt;Expect full interactivity in email&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/dashboard" rel="noopener noreferrer"&gt;Create an API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/docs#dev-api-overview" rel="noopener noreferrer"&gt;Developer API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/guides/build-generator" rel="noopener noreferrer"&gt;Build your own generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/pricing" rel="noopener noreferrer"&gt;Plans and monthly limits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>provchart</category>
      <category>api</category>
      <category>email</category>
      <category>css</category>
    </item>
    <item>
      <title>Introduction to ProvChart — Charts Without the JavaScript Tax</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Sat, 22 Aug 2026 08:28:32 +0000</pubDate>
      <link>https://dev.to/provchart/introduction-to-provchart-charts-without-the-javascript-tax-4l38</link>
      <guid>https://dev.to/provchart/introduction-to-provchart-charts-without-the-javascript-tax-4l38</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to ProvChart
&lt;/h2&gt;

&lt;p&gt;Most charting libraries ask you to ship &lt;strong&gt;40–150 kb of JavaScript&lt;/strong&gt;, wait for hydration, and hope crawlers eventually see the chart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ProvChart&lt;/strong&gt; takes a different path.&lt;/p&gt;

&lt;p&gt;It turns your data into &lt;strong&gt;CSS custom properties&lt;/strong&gt;. The browser paints the chart with native CSS (&lt;code&gt;clip-path&lt;/code&gt;, gradients, and a few carefully chosen rules). The result is a chart that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loads with the &lt;strong&gt;first paint&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Ships &lt;strong&gt;zero chart library&lt;/strong&gt; to the page&lt;/li&gt;
&lt;li&gt;Is &lt;strong&gt;fully visible to search engines&lt;/strong&gt; on the first crawl&lt;/li&gt;
&lt;li&gt;Can still be updated by changing CSS variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No canvas. No SVG charting runtime. No “wait for JS.”&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea in one sentence
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Data → CSS variables → browser paints the shape.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You (or the API) compile points into variables like &lt;code&gt;--p1&lt;/code&gt;, &lt;code&gt;--p2&lt;/code&gt;, ….&lt;br&gt;&lt;br&gt;
A pure CSS structure uses those variables to draw lines, areas, bars, or gauges.&lt;br&gt;&lt;br&gt;
After that, the page doesn’t need a charting library at all.&lt;/p&gt;


&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Self-contained HTML + CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime on the page&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None for the chart itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Line, area, bar, stacked bar, horizontal bar, scatter, combo, gauge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Themes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dark, light, midnight, or fully custom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SEO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chart markup is in the first HTML response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Change CSS variables (or regenerate) — no virtual DOM chart layer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is also a &lt;strong&gt;Developer API&lt;/strong&gt;: send JSON, get back &lt;code&gt;{ html, css }&lt;/code&gt;, inject into any page or pipeline.&lt;/p&gt;


&lt;h2&gt;
  
  
  Who it’s for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Teams that care about &lt;strong&gt;LCP, TTI, and bundle size&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Marketing and docs sites that need &lt;strong&gt;crawlable&lt;/strong&gt; charts&lt;/li&gt;
&lt;li&gt;Static or mostly-static pages that still need real data viz&lt;/li&gt;
&lt;li&gt;Backends that want to generate charts for &lt;strong&gt;emails, reports, or static builds&lt;/strong&gt; without shipping a client-side chart library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need heavy interaction, streaming of thousands of points, or complex JS-driven animations, a traditional library may still fit better. ProvChart optimizes for &lt;strong&gt;performance, simplicity, and SEO&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick taste (API)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://provchart-api.devtem.org/api/v1/generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Revenue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8b7bff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#4fd8c4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Feb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Apr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// data.html + data.css → inject into the page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe2knt0kx1gh4ozzdrwjc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe2knt0kx1gh4ozzdrwjc.jpg" alt="ProvChart chart example" width="720" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free accounts get a few &lt;strong&gt;test generations&lt;/strong&gt; so you can try the full flow.&lt;br&gt;&lt;br&gt;
Pro and Business unlock higher monthly limits for production use.&lt;/p&gt;


&lt;h2&gt;
  
  
  Open source core + hosted product
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rendering core:&lt;/strong&gt; &lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;st-core.fscss&lt;/a&gt; (MIT) — pure CSS chart primitives for the FSCSS ecosystem
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted product:&lt;/strong&gt; &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;ProvChart&lt;/a&gt; — visual builder, dashboard, Developer API, billing (by &lt;a href="https://devtem.org" rel="noopener noreferrer"&gt;DevTemple&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use the open-source core on its own, or the hosted API when you want keys, limits, and multi-series generation without maintaining the engine yourself.&lt;/p&gt;


&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Product home&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org/get-started" rel="noopener noreferrer"&gt;Get started&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free visual builder (no signup for single-series)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org/docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Core + Developer API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org/pricing" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free / Pro / Business&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  What’s next from this org
&lt;/h2&gt;

&lt;p&gt;We’ll publish short, practical posts on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building a small chart generator with the API
&lt;/li&gt;
&lt;li&gt;Using ProvChart in &lt;strong&gt;email digests&lt;/strong&gt; (server-side → image/snapshot)
&lt;/li&gt;
&lt;li&gt;Performance and SEO notes vs typical JS chart stacks
&lt;/li&gt;
&lt;li&gt;Themes, multi-series patterns, and real dashboard layouts
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you care about charts that stay fast and crawlable, you’re in the right place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ProvChart — charts without the JavaScript tax.&lt;/strong&gt;&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://chart.devtem.org" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;chart.devtem.org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>sharepoint</category>
      <category>seo</category>
      <category>performance</category>
      <category>provchart</category>
    </item>
  </channel>
</rss>
