<?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: FSCSS tutorial</title>
    <description>The latest articles on DEV Community by FSCSS tutorial (@fscss-ttr).</description>
    <link>https://dev.to/fscss-ttr</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%2Fuser%2Fprofile_image%2F2605174%2F8c1f0686-e922-4c08-b66f-08e912d8add1.png</url>
      <title>DEV Community: FSCSS tutorial</title>
      <link>https://dev.to/fscss-ttr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fscss-ttr"/>
    <language>en</language>
    <item>
      <title>Styling Deeply Nested divs with One FSCSS Array Loop (No More Hand-Writing Selectors)</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Fri, 11 Sep 2026 17:08:54 +0000</pubDate>
      <link>https://dev.to/fscss/styling-deeply-nested-divs-with-one-fscss-array-loop-no-more-hand-writing-selectors-3i08</link>
      <guid>https://dev.to/fscss/styling-deeply-nested-divs-with-one-fscss-array-loop-no-more-hand-writing-selectors-3i08</guid>
      <description>&lt;p&gt;Writing nested selectors by hand is painful.&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="nt"&gt;div&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="no"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="nt"&gt;div&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="no"&gt;purple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="nt"&gt;div&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="no"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/* …and it gets worse the deeper you go */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FSCSS gives us a clean way to generate the entire chain in a single loop using &lt;code&gt;@arr&lt;/code&gt; + &lt;code&gt;rpt()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You have a simple nested structure:&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&amp;gt;&lt;/span&gt;1
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;2
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;3&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&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;You want each level to have a different background color (and maybe padding, border-radius, etc.) without writing the selectors manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  The One-Loop Solution
&lt;/h3&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 &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/fscss@1.2.0/runtime.min.js"&lt;/span&gt; &lt;span class="na"&gt;async&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;style&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;@arr&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;purple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;          &lt;span class="c"&gt;/* → 1, 2, 3 */&lt;/span&gt;

&lt;span class="n"&gt;rpt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="s1"&gt;"div "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;      &lt;span class="c"&gt;/* builds "div ", "div div ", "div div div " */&lt;/span&gt;
  &lt;span class="nt"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;@arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;[]];&lt;/span&gt;
  &lt;span class="nt"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;border-radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#fff&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;8&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Gets Generated
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;div&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="no"&gt;orange&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;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&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;#fff&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;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="nt"&gt;div&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="no"&gt;purple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* same properties */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="nt"&gt;div&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="no"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* same properties */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;@arr levels[count(3, 1)]&lt;/code&gt; creates the numbers &lt;code&gt;1, 2, 3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rpt(@arr.levels[], "div ")&lt;/code&gt; repeats the string &lt;code&gt;"div "&lt;/code&gt; according to the current level.&lt;/li&gt;
&lt;li&gt;Inside the rule, &lt;code&gt;@arr.colors[@arr.levels[]]&lt;/code&gt; picks the matching color (arrays are 1-indexed).&lt;/li&gt;
&lt;li&gt;FSCSS expands the whole block once for every item in the array — one clean loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Easy Variations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Change depth&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Just update the &lt;code&gt;count()&lt;/code&gt; and the colors array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@arr colors[tomato, coral, salmon, crimson]
@arr levels[count(4, 1)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use child combinator instead of descendant&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rpt(@arr.levels[], "div &amp;gt; ") {
  /* now generates div &amp;gt;, div &amp;gt; div &amp;gt;, etc. */
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add more properties or even different values per level&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can reference the level index for anything — font-size, opacity, animation-delay, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Feels Good
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Zero repetitive typing
&lt;/li&gt;
&lt;li&gt;Depth and colors stay in sync
&lt;/li&gt;
&lt;li&gt;Still pure CSS after compilation
&lt;/li&gt;
&lt;li&gt;Works with the browser runtime or the CLI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hand-writing nested selectors used to be a small but annoying chore. With FSCSS arrays + &lt;code&gt;rpt()&lt;/code&gt;, it becomes a one-liner pattern you can reuse anytime.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Full:&lt;/em&gt; &lt;a href="https://fscss.devtem.org/nested-loop" rel="noopener noreferrer"&gt;https://fscss.devtem.org/nested-loop&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <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>FSCSS Array Loops: Generating Repeated CSS Rules with @arr</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Tue, 08 Sep 2026 15:00:09 +0000</pubDate>
      <link>https://dev.to/fscss/fscss-array-loops-generating-repeated-css-rules-with-arr-3c32</link>
      <guid>https://dev.to/fscss/fscss-array-loops-generating-repeated-css-rules-with-arr-3c32</guid>
      <description>&lt;p&gt;One of the most useful tools in FSCSS for cutting down repetitive CSS is the array system. Arrays let you store ordered lists of values and expand them into repeated rules automatically, instead of writing five or six nearly identical blocks by hand.&lt;/p&gt;

&lt;p&gt;This post walks through how &lt;code&gt;@arr&lt;/code&gt; works, how the loop trigger behaves, and the pattern used in real FSCSS modules like &lt;code&gt;siri-wave.fscss&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declaring Arrays
&lt;/h3&gt;

&lt;p&gt;Arrays in FSCSS are 1-indexed and string-native. The first item is &lt;code&gt;[1]&lt;/code&gt;, not &lt;code&gt;[0]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@arr colors[magenta, cyan, green, purple, orange, blue]
@arr delays[0.1s, 0.25s, 0.45s, 0.15s, 0.35s, 0.55s]
@arr indexes[count(6,1)]   /* generates 1,2,3,4,5,6 */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;count(n, start)&lt;/code&gt; is the standard way to generate a numeric index array. You can also declare literal lists of any CSS value type: colors, sizes, times, strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessing Individual Items
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;background: @arr.colors[1];      /* magenta */
animation-delay: @arr.delays[3]; /* 0.45s */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Loop Trigger: Empty Brackets
&lt;/h3&gt;

&lt;p&gt;The core feature is the empty bracket syntax, &lt;code&gt;@arr.name[]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When FSCSS sees &lt;code&gt;@arr.name[]&lt;/code&gt; inside a selector that's followed by a block, it expands the rule once for every item in the array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@arr delays[0.1s, 0.3s, 0.5s]
@arr colors[#ef4444, #f59e0b, #10b981]
@arr indexes[count(3,1)]

.loading-dot:nth-child(@arr.indexes[]) {
  animation-delay: @arr.delays[@arr.indexes[]];
  background: @arr.colors[@arr.indexes[]];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compiles to:&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="nc"&gt;.loading-dot&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1s&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;#ef4444&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.loading-dot&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.3s&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;#f59e0b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.loading-dot&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5s&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;#10b981&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;h3&gt;
  
  
  A Real Example: siri-wave.fscss
&lt;/h3&gt;

&lt;p&gt;Here's the pattern as it's actually used in &lt;a href="https://github.com/Koolkidkonrad/siri-wave.fscss" rel="noopener noreferrer"&gt;siri-wave.fscss&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@define siri-blob-colors(st:.blob){`
  @arr siri-colors[magenta, cyan, green, purple, orange, blue]
  @arr siri-colors-i[count(6,1)] 

  @siri-blob-variant(@use(st).@arr.siri-colors[@arr.siri-colors-i[]], @arr.siri-colors[@arr.siri-colors-i[]])

  @use(st){
    /* Need a block to trigger the loop */
    --siri-loop-index: @arr.siri-colors-i[];
  }
`}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking down what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Two parallel arrays are declared: one for values, one for indexes.&lt;/li&gt;
&lt;li&gt;The empty &lt;code&gt;[]&lt;/code&gt; sits inside a selector/argument that's followed by a block.&lt;/li&gt;
&lt;li&gt;FSCSS expands the whole statement six times, once per index.&lt;/li&gt;
&lt;li&gt;Each expansion calls &lt;code&gt;@siri-blob-variant&lt;/code&gt; with the matching color name, producing &lt;code&gt;.blob.magenta&lt;/code&gt;, &lt;code&gt;.blob.cyan&lt;/code&gt;, and so on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This index-array-plus-parallel-values pattern is the recommended way to generate multiple variants cleanly, without writing each variant out by hand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rules for Reliable Looping
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@arr.name[]&lt;/code&gt; must appear in a &lt;strong&gt;selector position followed by a &lt;code&gt;{ }&lt;/code&gt; block&lt;/strong&gt;. A bare reference inside a mixin argument alone won't trigger the loop reliably.&lt;/li&gt;
&lt;li&gt;Prefer an explicit index array (&lt;code&gt;count(n,1)&lt;/code&gt;) whenever you need to index into other arrays.&lt;/li&gt;
&lt;li&gt;Arrays are expanded entirely at compile time. The output is plain CSS, with no runtime array logic left behind.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Array Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.length&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of items&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.length&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.first&lt;/code&gt; / &lt;code&gt;.last&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;First / last item&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.first&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Comma-separated list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.join(sep)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Join with custom separator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.join(+)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.reverse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reversed list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.reverse&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.shuffle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Random order&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.shuffle&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.indices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1-based index list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.indices&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.randint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One random value for the compile&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@arr.colors!.randint&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Short list&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Mental Model
&lt;/h3&gt;

&lt;p&gt;Think of &lt;code&gt;@arr.name[]&lt;/code&gt; as "for each item in this array, generate a rule." That's the entire mechanism behind the six colored blobs in siri-wave, generated without writing six almost-identical blocks by hand.&lt;/p&gt;




&lt;p&gt;FSCSS isn't just a different syntax for writing CSS. It's growing into an ecosystem of reusable libraries, tools, and resources meant to make modern web development faster and less repetitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The siri-wave repo: &lt;a href="https://github.com/Koolkidkonrad/siri-wave.fscss" rel="noopener noreferrer"&gt;siri-wave.fscss on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>fscss</category>
      <category>css</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>st-core.fscss: a chart and dashboard UI kit built entirely in CSS mixins</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Sat, 05 Sep 2026 23:19:55 +0000</pubDate>
      <link>https://dev.to/fscss/st-corefscss-a-chart-and-dashboard-ui-kit-built-entirely-in-css-mixins-2mb3</link>
      <guid>https://dev.to/fscss/st-corefscss-a-chart-and-dashboard-ui-kit-built-entirely-in-css-mixins-2mb3</guid>
      <description>&lt;p&gt;Most chart libraries hand you a JavaScript API and a bundle size to worry about. st-core.fscss takes a different route: it's a set of FSCSS mixins that compile straight to CSS, no JavaScript required to render a chart, a stat card, or a progress bar.&lt;/p&gt;

&lt;p&gt;It's open source, and the whole thing lives in one readable source file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can do right now
&lt;/h2&gt;

&lt;p&gt;st-core.fscss covers the pieces you'd actually reach for on a dashboard or analytics screen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Line and area charts&lt;/strong&gt;, drawn with &lt;code&gt;clip-path: polygon()&lt;/code&gt; against eight data points&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data dots&lt;/strong&gt; placed along the line, generated automatically from an array so you don't write eight separate rules by hand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A background grid and axis labels&lt;/strong&gt;, sized to match your data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stat cards&lt;/strong&gt;, with label, value, and an up/down delta built in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progress bars&lt;/strong&gt;, driven by a single custom property so the fill can be updated later without touching the markup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A device-frame container&lt;/strong&gt;, if you want the whole thing to look like it's sitting inside a phone mockup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything reads from one shared design-token set, colors, radii, spacing, chart defaults, so a single &lt;code&gt;st-root()&lt;/code&gt; call at the top of your stylesheet themes the whole kit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's built this way
&lt;/h2&gt;

&lt;p&gt;Every piece is a plain &lt;code&gt;@define&lt;/code&gt; mixin. Call &lt;code&gt;st-chart-points(68, 59, 70, 35, 58, 22, 42, 55)&lt;/code&gt; with your real numbers, call &lt;code&gt;st-chart-line()&lt;/code&gt; and &lt;code&gt;st-chart-fill()&lt;/code&gt;, and you have a working chart, no chart.js, no canvas, no re-render logic. Update the data later by calling &lt;code&gt;st-chart-points()&lt;/code&gt; again with new numbers, and every dot, line, and fill shifts to match.&lt;/p&gt;

&lt;p&gt;That's the appeal for a dev workflow: you're writing CSS-shaped code the whole time. Nothing to import beyond FSCSS itself, nothing to configure beyond the parameters you pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax highlighting
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;@define&lt;/code&gt;, &lt;code&gt;@use()&lt;/code&gt;, and &lt;code&gt;@arr&lt;/code&gt; look unfamiliar in an editor with no idea what FSCSS is, the FSCSS &lt;a href="https://marketplace.visualstudio.com/items?itemName=Figsh.fscss" rel="noopener noreferrer"&gt;VS Code extension&lt;/a&gt; fixes that. It adds proper syntax highlighting and snippets for FSCSS files, so reading or writing st-core.fscss mixins looks like reading CSS, not a wall of unhighlighted text. Worth installing before you open the source for the first time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;The repo itself is short enough to read start to finish in one sitting, and there's a companion explainer walking through what every mixin does and why, mixin by mixin, if you'd rather understand the mechanics before you start calling them. Both beginners picking up FSCSS for the first time and developers who already know the compiler should be able to get a chart on screen within a few minutes either way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repo: &lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;github.com/fscss-ttr/st-core.fscss&lt;/a&gt;
&lt;/h3&gt;

</description>
      <category>css</category>
      <category>fscss</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>FSCSS v1.2.0 is out: two clean entry points, full backward compatibility, and inline styles that actually compile</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Fri, 04 Sep 2026 23:55:28 +0000</pubDate>
      <link>https://dev.to/fscss/fscss-v120-is-out-two-clean-entry-points-full-backward-compatibility-and-inline-styles-that-34ff</link>
      <guid>https://dev.to/fscss/fscss-v120-is-out-two-clean-entry-points-full-backward-compatibility-and-inline-styles-that-34ff</guid>
      <description>&lt;p&gt;FSCSS is open source, built in the open, and free for anyone to use, extend, or pick apart. That's the whole point of it. Every release is meant to make it easier for other developers to build with, not harder. v1.2.0 is the clearest example of that yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What shipped
&lt;/h2&gt;

&lt;p&gt;Previous versions of FSCSS scattered their browser-facing files across the package: &lt;code&gt;/e/exec.js&lt;/code&gt;, &lt;code&gt;/exec.js&lt;/code&gt;, &lt;code&gt;/xfscss.js&lt;/code&gt;, &lt;code&gt;/xfscss.min.js&lt;/code&gt;, and more. Depending on what you needed, you'd end up guessing which file was the right one to load.&lt;/p&gt;

&lt;p&gt;v1.2.0 consolidates all of that into two public entry points:&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="c"&gt;&amp;lt;!-- Auto-run, for plain HTML pages --&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/fscss@1.2.0/runtime.min.js"&lt;/span&gt; &lt;span class="na"&gt;async&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Manual control, for tooling and modules&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;xfscss&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;https://cdn.jsdelivr.net/npm/fscss@1.2.0/esm.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;runtime.js&lt;/code&gt; auto-scans the page and compiles everything on load, &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks, &lt;code&gt;&amp;lt;link type="fscss"&amp;gt;&lt;/code&gt; sources, and now inline &lt;code&gt;style&lt;/code&gt; attributes too. &lt;code&gt;esm.js&lt;/code&gt; never auto-runs and never touches page loading, since it's meant for using FSCSS as a tool: an FSCSS-to-CSS converter, a build step, or any pipeline where you decide what gets processed and when.&lt;/p&gt;

&lt;p&gt;Use one or the other. Never both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing breaks
&lt;/h2&gt;

&lt;p&gt;This is the part worth repeating: the npm package and CLI are completely unchanged, and every method that worked in the 1.1.x line still works exactly the same way in 1.2.0. &lt;code&gt;@define&lt;/code&gt;, &lt;code&gt;@fun&lt;/code&gt;, &lt;code&gt;@event&lt;/code&gt;, &lt;code&gt;@arr&lt;/code&gt;, &lt;code&gt;pattern()&lt;/code&gt;, all of it. The consolidation only touches the browser entry-point files. If you're compiling ahead of time with the CLI, this release changes nothing for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inline styles now go through the full pipeline
&lt;/h2&gt;

&lt;p&gt;This is the headline feature. From v1.2.0, &lt;code&gt;style="..."&lt;/code&gt; attributes are compiled on boot the same way &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks are:&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;style&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;@define&lt;/span&gt; &lt;span class="n"&gt;btn-style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#636EE7&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;10px&lt;/span&gt; &lt;span class="m"&gt;20px&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="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;#dddddd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bg&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="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&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;button&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"@btn-style(#1a2a4f)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Search&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"@btn-style(#f00f00)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Search&lt;span class="nt"&gt;&amp;lt;/button&amp;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%2F1ce8h199ai90jatcs9u4.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%2F1ce8h199ai90jatcs9u4.jpg" alt="FSCSS output" width="399" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Combined with &lt;code&gt;xfscss.process()&lt;/code&gt;, that means an inline mixin call can be recompiled at runtime and swapped straight back into the element, no rewritten CSS, no framework state:&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;update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;newStyle&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;xfscss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@btn-style(@arr.colors!.randint)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;newStyle&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 full walkthrough of this: including a detail (method declared inline stay resolvable even after their declaration is cleaned out of the attribute), if you want to go deeper: &lt;a href="https://dev.to/figsh/fscss-v120-lets-you-write-mixins-directly-in-style-heres-why-thats-a-bigger-deal-than-2a31"&gt;FSCSS v1.2.0 lets you write mixins directly in style="..."&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for an open-source tool
&lt;/h2&gt;

&lt;p&gt;None of this is about adding complexity for its own sake. FSCSS exists to cut down on repetitive CSS and give developers capabilities plain CSS doesn't have natively, and every release should make that easier to reach for, not harder to keep up with. Two entry points instead of a pile of internal files. Full compatibility instead of a migration guide. A feature that makes the JS API and the language work together instead of sitting side by side.&lt;/p&gt;

&lt;p&gt;Open source only stays useful if the people using it can trust an upgrade won't cost them a rewrite. That's the bar this release was held to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;fscss@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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 &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/fscss@1.2.0/runtime.min.js"&lt;/span&gt; &lt;span class="na"&gt;async&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;ul&gt;
&lt;li&gt;Docs: &lt;a href="https://fscss.devtem.org/docs" rel="noopener noreferrer"&gt;fscss.devtem.org/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;API reference: &lt;a href="https://fscss.devtem.org/api" rel="noopener noreferrer"&gt;fscss.devtem.org/api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Inline styles: &lt;a href="https://fscss.devtem.org/inline" rel="noopener noreferrer"&gt;fscss.devtem.org/inline&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Figsh/xfscss" rel="noopener noreferrer"&gt;github.com/Figsh/xfscss&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NPM: &lt;a href="https://www.npmjs.com/package/fscss" rel="noopener noreferrer"&gt;npmjs.com/package/fscss&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bug reports, feature requests, and pull requests are all welcome on &lt;a href="https://github.com/Figsh/xfscss" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;/p&gt;

</description>
      <category>coding</category>
      <category>fscss</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>FSCSS Component Architecture: A Modular, Composition-First Approach to CSS</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Thu, 03 Sep 2026 15:58:02 +0000</pubDate>
      <link>https://dev.to/fscss/fscss-component-architecture-a-modular-composition-first-approach-to-css-38n6</link>
      <guid>https://dev.to/fscss/fscss-component-architecture-a-modular-composition-first-approach-to-css-38n6</guid>
      <description>&lt;p&gt;FSCSS component architecture is built around a modular, composition-first model that compiles to plain CSS. It emphasizes reusable style units, design tokens, conditional logic, and selective imports—with almost no runtime JavaScript required for the final output. Components in FSCSS are treated as pure style definitions rather than framework-specific widgets, keeping stylesheets readable, highly reusable, and free of classic “mega-stylesheet” problems while still producing standard CSS that any browser understands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Building Blocks
&lt;/h3&gt;

&lt;p&gt;FSCSS provides a focused set of primitives for defining and composing styles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Introduced / Key version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;str(name, "…")&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Named blocks of CSS declarations&lt;/td&gt;
&lt;td&gt;Simple reusable style snippets&lt;/td&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@fun(name){…}&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key-value stores (design tokens)&lt;/td&gt;
&lt;td&gt;Spacing scales, color palettes, property groups&lt;/td&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@define name(params)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Parameterized mixins&lt;/td&gt;
&lt;td&gt;Themed components, variants, full structures&lt;/td&gt;
&lt;td&gt;1.1.15+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;pattern(threshold: "desc", "…")&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Semantic / fuzzy matching&lt;/td&gt;
&lt;td&gt;Natural-language style injection&lt;/td&gt;
&lt;td&gt;1.1.25+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@event name(param)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Conditional value functions&lt;/td&gt;
&lt;td&gt;Themes, states, calculations&lt;/td&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@arr(name[…])&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Arrays + iteration&lt;/td&gt;
&lt;td&gt;Generated classes, loops, scales&lt;/td&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@import&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Selective / wildcard module loading&lt;/td&gt;
&lt;td&gt;Modular architecture &amp;amp; ecosystem modules&lt;/td&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How Components Are Structured
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Atomic / Token Layer (&lt;code&gt;@fun&lt;/code&gt; + variables)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Design tokens sit at the foundation so every component draws from a single source of truth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@fun(tokens) {
  primary: #2563eb;
  radius-md: 8px;
  space-4: 1rem;
  shadow-sm: 0 1px 3px rgba(0,0,0,.1);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Base Style Blocks (&lt;code&gt;str()&lt;/code&gt; or &lt;code&gt;@fun&lt;/code&gt; full-block)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Related declarations are grouped into reusable blocks that can be dropped into any selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;str(card-base, "
  padding: @fun.tokens.space-4.value;
  border-radius: @fun.tokens.radius-md.value;
  box-shadow: @fun.tokens.shadow-sm.value;
  background: white;
")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Parameterized Components (&lt;code&gt;@define&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
True mixins accept arguments and can be composed freely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@define button(bg: #2563eb, fg: white, pad: 0.75rem 1.5rem) {
  background: @use(bg);
  color: @use(fg);
  padding: @use(pad);
  border: none;
  border-radius: @fun.tokens.radius-md.value;
  cursor: pointer;
}

@define button-hover(bg) {`
  &amp;amp;:hover { background: @use(bg); }
`}

.btn-primary {
  @button()
  @button-hover(#1d4ed8)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Semantic / Intent-based Styles (&lt;code&gt;pattern()&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Styles can be described in plain language; FSCSS matches by similarity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pattern(0.6: "elevated card with soft shadow", `
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 10px 25px rgba(0,0,0,.08);
  background: white;
`)

.product-card {
  elevated card with soft shadow
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Conditional &amp;amp; Dynamic Behavior (&lt;code&gt;@event&lt;/code&gt; + &lt;code&gt;@arr&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Themes, states, and generated variants are handled at compile time without external logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@event theme(mode) {
  if mode: dark { return: #0f172a; }
  el { return: #f8fafc; }
}

body {
  background: @event.theme(dark);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Modular Architecture &amp;amp; Ecosystem
&lt;/h3&gt;

&lt;p&gt;FSCSS encourages splitting styles into focused modules and importing only what is needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import((page-root, page-hero) from customizable)
@import((*) from form)
@import((st-chart-line, st-stat-card) from st-core)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modules that follow this pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;st-core&lt;/a&gt;&lt;/strong&gt; — pure-CSS charts &amp;amp; stat cards (data via CSS custom properties)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/fscss-ttr/form.fscss" rel="noopener noreferrer"&gt;form&lt;/a&gt;&lt;/strong&gt; — floating labels, checkboxes, switches, validation (checkbox-hack style)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/Figsh/Circle-progress.fscss" rel="noopener noreferrer"&gt;circle-progress&lt;/a&gt;&lt;/strong&gt; — progress rings with color logic driven by &lt;code&gt;@event&lt;/code&gt; + &lt;code&gt;@arr&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/fscss-ttr/customizable.fscss" rel="noopener noreferrer"&gt;customizable&lt;/a&gt;&lt;/strong&gt; — page shells (nav, hero, CTA, footer, typography)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/fscss-ttr/cube.fscss" rel="noopener noreferrer"&gt;cube&lt;/a&gt;&lt;/strong&gt; — pure CSS 3D cube faces with micro-effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each module is self-contained, token-driven, and designed so multiple modules can coexist on one page without conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended Project Layout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;styles/
  tokens.fscss          ← @fun + $variables
  components/
    button.fscss        ← @define + str()
    card.fscss
    form.fscss
  layouts/
    page.fscss
  main.fscss            ← selective @imports + composition
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compile with the CLI for production (&lt;code&gt;fscss main.fscss dist/styles.css&lt;/code&gt;) so the shipped CSS has zero runtime dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Design Principles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composition over inheritance&lt;/strong&gt; — stack small &lt;code&gt;@define&lt;/code&gt;s and &lt;code&gt;str()&lt;/code&gt; blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens first&lt;/strong&gt; — everything reads from &lt;code&gt;@fun&lt;/code&gt; or &lt;code&gt;$variables&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selective imports&lt;/strong&gt; — pull only the mixins a page needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compile-time resolution&lt;/strong&gt; — almost all logic runs at build time and disappears from the final CSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive enhancement&lt;/strong&gt; — start with simple &lt;code&gt;str()&lt;/code&gt; / &lt;code&gt;@fun&lt;/code&gt;, then graduate to &lt;code&gt;@define&lt;/code&gt; and &lt;code&gt;pattern()&lt;/code&gt; as complexity grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture keeps stylesheets readable, highly reusable, and free of the classic “mega-stylesheet” problems while still producing standard CSS that any browser understands.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>fscss</category>
      <category>sharepoint</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How pattern() Feels a Few Weeks In</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Tue, 01 Sep 2026 15:06:19 +0000</pubDate>
      <link>https://dev.to/fscss/how-pattern-feels-a-few-weeks-in-3fm5</link>
      <guid>https://dev.to/fscss/how-pattern-feels-a-few-weeks-in-3fm5</guid>
      <description>&lt;p&gt;I've written three posts now about &lt;code&gt;pattern()&lt;/code&gt;: what it is, why it might matter for beginners and AI-generated CSS, and a demo module built on top of it. This one is different. It's less a tutorial and more a check-in, and a reason to keep teaching this in the open rather than writing it once and moving on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that surprised me
&lt;/h2&gt;

&lt;p&gt;I expected questions about syntax. I got questions about the matching itself instead. One reader asked directly how two patterns avoid matching the same phrase. Another pointed at the ambiguity tradeoff as the thing worth watching as the idea scales. Neither question was about how to write &lt;code&gt;pattern()&lt;/code&gt;. Both were about whether it holds up.&lt;/p&gt;

&lt;p&gt;That's the right question to be asking, and it's not fully answered yet. I know it holds up in a small module. I don't know yet how it holds up in a library with hundreds of patterns written by more than one person. That's genuinely still open, and I'd rather say that plainly than pretend the demo module settles it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short recap, for anyone catching up
&lt;/h2&gt;

&lt;p&gt;If this is the first post of the series you've landed on:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pattern()&lt;/code&gt; lets you describe a piece of CSS in plain English instead of calling it by an exact name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pattern(0.6: "rounded primary button", `
  border: none;
  border-radius: 8px;
  padding: 10px 20px;
  background: #764ba2;
  color: white;
`)

.btn {
  rounded primary button
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The phrase inside &lt;code&gt;.btn&lt;/code&gt; doesn't need to match the description exactly. It needs to score above the threshold, &lt;code&gt;0.6&lt;/code&gt; here, on word similarity. Write something close enough, and it resolves. Write something unrelated, and it falls through as harmless literal text instead of breaking your build.&lt;/p&gt;

&lt;p&gt;The earlier posts go deeper: &lt;a href="https://dev.to/fscss/fscss-define-pattern-and-import-explained-in-plain-english-5el0"&gt;the mechanics of &lt;code&gt;pattern()&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.to/fscss/why-readable-css-matters-more-as-ai-writes-more-of-it-3jg0"&gt;why this matters for beginners and AI-generated CSS&lt;/a&gt;, and &lt;a href="https://dev.to/fscss/patternsfscss-a-demo-module-for-styling-css-in-plain-english-oc"&gt;patterns.fscss&lt;/a&gt;, a small open module you can fork.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell a beginner trying this for the first time
&lt;/h2&gt;

&lt;p&gt;Don't start by writing ten patterns. Start by writing one, for something you personally rewrite often, a card style, a button variant, whatever shows up in your own projects repeatedly. Give it a description in the words you'd actually use out loud. Then try triggering it with a slightly different phrase than the one you wrote, not the exact words, something close. If it matches, you've felt the whole idea in about two minutes. If it doesn't, lower the threshold slightly and try again. That's the entire learning curve.&lt;/p&gt;

&lt;p&gt;The mistake to avoid is writing two patterns that are almost the same idea in almost the same words. I made this mistake myself in the first draft of &lt;code&gt;patterns.fscss&lt;/code&gt;, two button patterns worded closely enough that matching between them was genuinely unclear. It's an easy trap, and the only real fix is noticing it and rewording, not a cleverer algorithm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes next
&lt;/h2&gt;

&lt;p&gt;I don't have a roadmap to announce. What I have is an open module and an open question about how well description-based matching holds up at scale. If you try &lt;code&gt;pattern()&lt;/code&gt; on something real, whether it works cleanly or breaks in an interesting way, I'd rather hear about the break. That's more useful to the next person reading this series than another polished example would be.&lt;/p&gt;

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

&lt;p&gt;Docs: &lt;a href="https://fscss.devtem.org/pattern" rel="noopener noreferrer"&gt;fscss.devtem.org/pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Extension &lt;a href="https://marketplace.visualstudio.com/items?itemName=Figsh.fscss" rel="noopener noreferrer"&gt;visualstudio&lt;/a&gt; &lt;/p&gt;




</description>
      <category>fscss</category>
      <category>css</category>
      <category>webdev</category>
      <category>beginners</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>I got tired of static README badges, so I made my repo stats real charts</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Sun, 30 Aug 2026 17:18:57 +0000</pubDate>
      <link>https://dev.to/fscss-ttr/i-got-tired-of-static-readme-badges-so-i-made-my-repo-stats-real-charts-114c</link>
      <guid>https://dev.to/fscss-ttr/i-got-tired-of-static-readme-badges-so-i-made-my-repo-stats-real-charts-114c</guid>
      <description>&lt;p&gt;Every README I've ever maintained has had that one section near the top: badges for stars, forks may be build status icon. They work, but they're just... images. Static, generic, the same little badge everyone else's repo has too.&lt;/p&gt;

&lt;p&gt;What I actually wanted was simpler to describe than it turned out to be to build: &lt;strong&gt;pull the real numbers from a repo, turn them into an actual chart, commit it as an SVG, embed it like any other image.&lt;/strong&gt; No third-party badge service holding my stats hostage, no client-side JS running in a README that can't run JS anyway.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/fscss-ttr/provchart-repo-stats" rel="noopener noreferrer"&gt;ProvChart Repo Stats&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually writes
&lt;/h2&gt;

&lt;p&gt;It's a GitHub Action. Point it at a repo, give it an API key, and on schedule (or on demand) it writes two SVGs straight into your repo:&lt;/p&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&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;horizontal bar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's it. No hand-maintained JSON describing what the chart should look like, no dashboard to babysit. The numbers come from GitHub's API, the chart comes from &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;ProvChart&lt;/a&gt;, the action wires the two together and commits the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Add a repo secret.&lt;/strong&gt; Grab an API key from the &lt;a href="https://chart.devtem.org/dashboard" rel="noopener noreferrer"&gt;ProvChart dashboard&lt;/a&gt; and save it as &lt;code&gt;PROVCHART_API_KEY&lt;/code&gt; under your repo's Settings → Secrets → Actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Drop in the workflow.&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="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;p&gt;&lt;strong&gt;3. Embed the SVGs in your README like any other image.&lt;/strong&gt;&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;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 manually, or just wait for the Sunday morning cron. The action commits the SVGs, your README stays plain Markdown, nothing else changes about how your repo works.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the charts actually look like
&lt;/h2&gt;

&lt;p&gt;The overview chart is an area chart plotting your real counts, not squeezed onto a 0-to-100 scale:&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%2Fh02vhwwr0ipnoo2km7f0.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%2Fh02vhwwr0ipnoo2km7f0.jpg" alt="Overview area chart showing stars, forks, watchers, and issues as real counts" width="720" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the language breakdown renders as a clean horizontal bar, share by percentage:&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%2Fh3yfrz7srgxfjquhzr0o.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%2Fh3yfrz7srgxfjquhzr0o.jpg" alt="Language share horizontal bar chart: JavaScript, TypeScript, Python, Shell, Others" width="720" height="395"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A couple of defaults worth knowing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overview&lt;/strong&gt; uses real counts. If your repo has 3,200 stars, the chart says 3,200, it's not forced into a 0-100 box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages&lt;/strong&gt; render as share percentages, which is usually the more useful way to look at a language breakdown anyway.&lt;/li&gt;
&lt;li&gt;Both are configurable if the defaults don't fit: &lt;code&gt;overview-type&lt;/code&gt; and &lt;code&gt;languages-type&lt;/code&gt; let you switch to bars if that's more your style.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the hood it's all generated through ProvChart's &lt;code&gt;generate-svg&lt;/code&gt; endpoint, so there's no image hosting involved and nothing rendered client-side. The SVG is just... a file in your repo, same as any other asset.&lt;/p&gt;

&lt;p&gt;If you already maintain a &lt;code&gt;.provchart/charts.json&lt;/code&gt; and want fixed sample charts instead of live repo metrics, there's a sibling action for that: &lt;a href="https://github.com/fscss-ttr/provchart-readme-action" rel="noopener noreferrer"&gt;provchart-readme-action&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Repo, issues, and feedback: &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;

&lt;p&gt;If you end up using it on something, I'd genuinely like to see what your README looks like with it. And if something breaks or a default doesn't make sense for your use case, open an issue, I'm actively maintaining this one.&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-repo-stats" rel="noopener noreferrer"&gt;
        provchart-repo-stats
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Generate ProvChart SVG charts from GitHub repo stats (stars, forks, watchers, issues, languages)
    &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 Repo Stats&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;GitHub Action that reads &lt;strong&gt;live repo health&lt;/strong&gt; from the GitHub API and writes &lt;strong&gt;ProvChart SVG&lt;/strong&gt; charts for your README.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;v1.3 defaults&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;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;Chart&lt;/th&gt;

&lt;th&gt;File&lt;/th&gt;

&lt;th&gt;Type&lt;/th&gt;

&lt;th&gt;Values&lt;/th&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;Overview&lt;/td&gt;

&lt;td&gt;&lt;code&gt;docs/charts/repo-overview.svg&lt;/code&gt;&lt;/td&gt;

&lt;td&gt;&lt;strong&gt;area&lt;/strong&gt;&lt;/td&gt;

&lt;td&gt;Real stars, forks, watchers, open issues&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Languages&lt;/td&gt;

&lt;td&gt;&lt;code&gt;docs/charts/languages.svg&lt;/code&gt;&lt;/td&gt;

&lt;td&gt;&lt;strong&gt;hbar&lt;/strong&gt;&lt;/td&gt;

&lt;td&gt;Language share %&lt;/td&gt;

&lt;/tr&gt;

&lt;/tbody&gt;

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

&lt;p&gt;No hand-written chart JSON for basic stats — only a workflow and an API key.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start&lt;/h2&gt;
&lt;/div&gt;

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

&lt;p&gt;Repo &lt;strong&gt;Settings → Secrets and variables → Actions&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;Key from &lt;a href="https://chart.devtem.org/dashboard" rel="nofollow noopener noreferrer"&gt;chart.devtem.org/dashboard&lt;/a&gt;&lt;br&gt;
&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. Workflow&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.github/workflows/repo-stats.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;Repo stats charts&lt;/span&gt;
&lt;span class="pl-ent"&gt;on&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;schedule&lt;/span&gt;:
    - &lt;span class="pl-ent"&gt;cron&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;0 8 * * 0&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
  &lt;span class="pl-ent"&gt;workflow_dispatch&lt;/span&gt;:

&lt;span class="pl-ent"&gt;permissions&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;contents&lt;/span&gt;: &lt;span class="pl-s"&gt;write&lt;/span&gt;

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

      - &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;Check secret&lt;/span&gt;
        &lt;span class="pl-ent"&gt;env&lt;/span&gt;:
          &lt;span class="pl-ent"&gt;KEY&lt;/span&gt;: &lt;span class="pl-s"&gt;${{ secrets.PROVCHART_API_KEY }}&lt;/span&gt;
        &lt;span class="pl-ent"&gt;run&lt;/span&gt;: &lt;span class="pl-s"&gt;|&lt;/span&gt;
&lt;span class="pl-s"&gt;          if [ -z "$KEY" ]; then&lt;/span&gt;
&lt;span class="pl-s"&gt;            echo "::error::PROVCHART_API_KEY is empty"&lt;/span&gt;
&lt;span class="pl-s"&gt;            exit 1&lt;/span&gt;
&lt;span class="pl-s"&gt;          fi&lt;/span&gt;
&lt;span class="pl-s"&gt;          echo "Secret OK&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-repo-stats" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>github</category>
      <category>provchart</category>
      <category>automation</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>From st-core.fscss to ProvChart: Same CSS-Paint Idea, Built for Real Data</title>
      <dc:creator>FSCSS tutorial</dc:creator>
      <pubDate>Wed, 26 Aug 2026 15:56:32 +0000</pubDate>
      <link>https://dev.to/fscss/from-st-corefscss-to-provchart-same-css-paint-idea-built-for-real-data-2mp2</link>
      <guid>https://dev.to/fscss/from-st-corefscss-to-provchart-same-css-paint-idea-built-for-real-data-2mp2</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;st-core.fscss&lt;/a&gt; started as an FSCSS module with a clear bet: &lt;strong&gt;charts can be browser paint, not a JavaScript chart runtime.&lt;/strong&gt; No canvas. No SVG dependency for the line. Data lives in CSS custom properties; shapes are &lt;code&gt;clip-path: polygon()&lt;/code&gt;; optional JS only writes numbers. Compiled, it’s on the order of &lt;strong&gt;~0.5 kb&lt;/strong&gt; of CSS—not a 40–150 kb chart library.&lt;/p&gt;

&lt;p&gt;That idea spread—writeups, demos, dashboard experiments—because it matched what performance-minded frontends already wanted: &lt;strong&gt;first paint is the chart&lt;/strong&gt;, crawlers see markup, updates can be variable writes with free CSS transitions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ProvChart&lt;/strong&gt; is not a rejection of that model. It’s what you build when the same philosophy has to survive &lt;strong&gt;arbitrary series lengths, multi-series layouts, and a server-side compile step&lt;/strong&gt;—without asking every team to hand-author mixins per dataset.&lt;/p&gt;




&lt;h2&gt;
  
  
  What st-core actually nailed
&lt;/h2&gt;

&lt;p&gt;st-core’s contract is intentionally tight and easy to teach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eight points: &lt;code&gt;--st-p1&lt;/code&gt; … &lt;code&gt;--st-p8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fixed X stops: &lt;code&gt;0% 14% 28% 42% 57% 71% 85% 100%&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mixins like &lt;code&gt;@st-chart-points&lt;/code&gt;, &lt;code&gt;@st-chart-line&lt;/code&gt;, &lt;code&gt;@st-chart-fill&lt;/code&gt;, dots, stats&lt;/li&gt;
&lt;li&gt;Optional JS: &lt;code&gt;element.style.setProperty('--st-p3', '42%')&lt;/code&gt; → browser repaints the polygon
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.chart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* FSCSS: @st-chart-points(20, 35, 33, 30, 48, 35, 66, 37) */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That is still a &lt;strong&gt;strong starter library&lt;/strong&gt; for hand-built cards, marketing strips, and demos where eight samples are enough. It maximizes what the browser already does well. Nothing about ProvChart throws that away.&lt;/p&gt;


&lt;h2&gt;
  
  
  Where a fixed eight-point contract stops fitting
&lt;/h2&gt;

&lt;p&gt;Real product data rarely arrives as “exactly eight numbers”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Days in a month, weeks in a year, irregular sensor samples
&lt;/li&gt;
&lt;li&gt;Several series on one frame (revenue vs cost, traffic vs signups)
&lt;/li&gt;
&lt;li&gt;Automation: JSON from an API, not a mixin edited in a stylesheet
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;st-core’s elegance &lt;strong&gt;is&lt;/strong&gt; that compile-time contract. ProvChart’s job is to keep &lt;strong&gt;data → variables → CSS geometry → paint&lt;/strong&gt; when the point count and series count are decided &lt;strong&gt;per request&lt;/strong&gt;, not per mixin signature.&lt;/p&gt;


&lt;h2&gt;
  
  
  What ProvChart changes (and what it doesn’t)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;st-core.fscss&lt;/th&gt;
&lt;th&gt;ProvChart&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Paint model&lt;/td&gt;
&lt;td&gt;CSS variables + &lt;code&gt;clip-path&lt;/code&gt; / native CSS&lt;/td&gt;
&lt;td&gt;Same family of ideas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Point count&lt;/td&gt;
&lt;td&gt;Eight fixed slots&lt;/td&gt;
&lt;td&gt;Length of &lt;code&gt;series[].points&lt;/code&gt; (no eight-slot ceiling)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Series&lt;/td&gt;
&lt;td&gt;Hand composition / multi-line patterns in FSCSS&lt;/td&gt;
&lt;td&gt;First-class multi-series, combo, bar, hbar, gauge, …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authoring&lt;/td&gt;
&lt;td&gt;Mixins in source / FSCSS compile&lt;/td&gt;
&lt;td&gt;JSON → builder or &lt;strong&gt;Developer API&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client chart library&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None for paint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docs embeds&lt;/td&gt;
&lt;td&gt;Manual / CSS&lt;/td&gt;
&lt;td&gt;Optional &lt;code&gt;generate-svg&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interaction&lt;/td&gt;
&lt;td&gt;Hand dots / &lt;code&gt;attr()&lt;/code&gt; patterns&lt;/td&gt;
&lt;td&gt;Optional &lt;strong&gt;provchart-runtime&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Unchanged belief:&lt;/strong&gt; the chart is CSS. JavaScript should not be required to &lt;em&gt;draw&lt;/em&gt; the shape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changed machinery:&lt;/strong&gt; compilation can run on a server; geometry is generated for the payload you sent; roots are instance-scoped (classes like &lt;code&gt;pc-…&lt;/code&gt; and variables such as &lt;code&gt;--pc-{id}-s{series}-p{index}&lt;/code&gt;) so many charts can share a page without colliding.&lt;/p&gt;

&lt;p&gt;There is &lt;strong&gt;no marketed “must be ≤ 200 points” product rule&lt;/strong&gt; in this writeup. Practical limits are the usual ones: payload size, CSS complexity, and what stays smooth in real browsers—not an eight-argument mixin.&lt;/p&gt;


&lt;h2&gt;
  
  
  Server compile instead of only a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; mixin
&lt;/h2&gt;

&lt;p&gt;st-core path: FSCSS CLI or in-browser compile → you ship CSS you authored.&lt;/p&gt;

&lt;p&gt;ProvChart path: send JSON, get markup:&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;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;45&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;50&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;May&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; first paint is the chart&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Five points or fifty—it’s the same call shape. For README and Markdown surfaces, &lt;code&gt;POST /api/v1/generate-svg&lt;/code&gt; returns SVG / data URI without requiring a chart runtime on the doc page.&lt;/p&gt;


&lt;h2&gt;
  
  
  provchart-runtime: interaction without owning paint
&lt;/h2&gt;

&lt;p&gt;st-core already showed the interaction seed: a dot, a tooltip, data on the element, CSS doing the rest.&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; (MIT, npm &lt;code&gt;provchart-runtime&lt;/code&gt;) generalizes that for &lt;strong&gt;generated&lt;/strong&gt; HTML charts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finds &lt;code&gt;[data-provchart]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Skips pure SVG exports when configured
&lt;/li&gt;
&lt;li&gt;Scroll reveal, legend focus, per-point tooltips (reading generated custom properties where present)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MutationObserver&lt;/code&gt; for charts injected after load
&lt;/li&gt;
&lt;/ul&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.0.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;Additive on purpose: generate output does not depend on the runtime; the runtime does not replace the engine.&lt;/p&gt;


&lt;h2&gt;
  
  
  The stack, honestly drawn
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;st-core.fscss (open source, viral pure-CSS chart idea)
  --st-p1…p8 · fixed X-stops · mixins · ~0.5kb compiled CSS
  JS optional: write variables, CSS transitions animate polygons
        │
        ▼
ProvChart (chart.devtem.org)
  Same paint philosophy · variable-length series · multi-series types
  Builder + Developer API → static HTML/CSS (and SVG for docs)
        │
        ▼
provchart-runtime (optional)
  Tooltips / motion / legend UX on HTML charts only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Open core: &lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;github.com/fscss-ttr/st-core.fscss&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Product: &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Runtime: &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;/ul&gt;


&lt;h2&gt;
  
  
  When to use which
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use st-core when…&lt;/th&gt;
&lt;th&gt;Use ProvChart when…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You want full control in FSCSS/CSS source&lt;/td&gt;
&lt;td&gt;Data is JSON from backends, CI, or agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eight points (or st-core’s patterns) fit the story&lt;/td&gt;
&lt;td&gt;Series length and count change per view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You’re deep in the FSCSS module ecosystem&lt;/td&gt;
&lt;td&gt;You want API keys, quotas, SVG export, dashboard builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maximum clarity for teaching pure CSS charts&lt;/td&gt;
&lt;td&gt;Production multi-series, combo, gauge, automation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Many teams will use &lt;strong&gt;both&lt;/strong&gt;: st-core for crafted marketing cards; ProvChart when the dataset refuses to stay eight samples long.&lt;/p&gt;



&lt;p&gt;st-core.fscss proved a viral, high-performance idea—&lt;strong&gt;browser-native paint, minimal JS, no chart-library tax.&lt;/strong&gt; ProvChart keeps that idea and removes the fixed-slot ceiling so the same belief works when the numbers come from a database, not a mixin call.&lt;/p&gt;




&lt;div class="ltag__user ltag__user__id__14466"&gt;
  &lt;a href="/provchart" class="ltag__user__link profile-image-link"&gt;
    &lt;div class="ltag__user__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14466%2Fb4fe5b40-3c9d-46ca-9537-1880fc4bea3c.png" alt="provchart image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
      &lt;a href="/provchart" class="ltag__user__link"&gt;ProvChart&lt;/a&gt;
      Follow
    &lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a href="/provchart" class="ltag__user__link"&gt;
        Charts that load with the first paint and stay crawlable
ProvChart generates pure HTML + CSS charts from your data
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>product</category>
      <category>fscss</category>
      <category>css</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>
  </channel>
</rss>
