Repository navigation
markvis 2.2.0
Draw a chart block anywhere Markdown is shown: one call from JavaScript, React with streaming, one script tag in any Markdown page, rehype for MDX hosts, and a GitHub Action. Four more chart types, the graphite theme, and a measured instruction for models. The language stays markvis: 2. A 2.1 block still parses unless it hits one of the stricter checks listed under Breaking.
Breaking
- The ES module
markvis.mjsno longer draws on import. A page that loads it with<script type="module" src=".../markvis@2/dist/markvis.mjs">and relied on charts drawing by themselves now draws nothing. Import it and callrun(), or load the classicmarkvis.min.js, which still draws once the page is ready. - The drop-in's global (
window.markvis) andmarkvis.mjsexportrun,init,initialize,render, andparseBlock, and nothing else; the classicmarkvis.min.jsglobal also carriesstartsOnLoad, which reads a script tag'sdata-start-on-load.replaceLanguageBlocks,replaceInDocument,autoReplace,wrapFence,chartBlockHtml,resultToHtml,htmlTable,escapeHtml,parseMarkdown,renderSvg,isChartBlock,enhanceChartSvg, andtipTextForMarkare gone from it. Themarkvisnpm package still exports the parser and renderer functions. - Stricter input checks. These blocks drew in 2.1 and now fail with a stable code, keeping their table: a non-numeric measure cell such as
N/A(E_BAD_NUMBER); an empty cell on pie, funnel, sankey, treemap, waterfall, gauge, or a stacked/percent layout (E_MISSING_VALUE); a repeated category/series key, including a gauge with more than one row (E_DUP_KEY); a sankey cycle (E_SANKEY_CYCLE); andmarkvis:set to anything but2(E_BAD_VERSION). A gauge withoutmaxnow draws against 0–100. Seedocs/release-2.2.md(Compatibility, Migration).
Added
- Charts follow the host page through CSS variables, with fixed-color fallbacks. A chart drawn in a page writes each color as a fixed attribute plus a
stylethat reads--markvis-ink,--markvis-muted,--markvis-grid,--markvis-paper,--markvis-series-1…--markvis-series-8, and the rest of 18 names (and an opt-in--markvis-font), so light, dark, and brand colors carry into the chart. The list ships asCSS_VARIABLESandPAINT_NAMESfrommarkvis/themes.colors: "host" | "static"onrender(),renderSvg(),initialize(), and<Markvis>;--colors host|staticonmarkvis renderandbake. Page: Theming. parseBlock(text, { filename? })parses one chart block: the inside a Markdown renderer hands a code-block plugin, or the whole block.render(text, options?)returns{ ok, svg, html, chart }or{ ok: false, error, html, table }in one call and never throws.htmlis the same figure, or table and one error line, that every adapter emits. Options:width, andtheme/palette/surfacefor blocks that leave them out.markvis/react:<Markvis source>or<Markvis chart data>draws one block at the width of its container;markvisComponentsdraws chart code blocks in react-markdown;remarkMarkvisStreamingshows a placeholder and the rows so far while a block streams, never an error, and<Markvis streaming>does the same for a host that passes a block's partial text to its own renderer.reactis an optional peer. It starts with"use client";so server-component frameworks load it as a client module.- One script tag in any Markdown page: the drop-in finds chart blocks in the HTML of marked, markdown-it, highlight.js, Jekyll (kramdown + rouge), Hugo, MkDocs (custom fence), Pandoc, and docsify, and draws the comment + table form from the page.
markvis.run({ nodes, querySelector, root }),markvis.initialize({ theme, palette, surface, width }), anddata-start-on-load="false". markvis/rehype: chart code blocks become real hast (figure, SVG, table) for MDX hosts such as Docusaurus and Astro. No raw HTML nodes, no new runtime dependency.- A GitHub Action for any repository:
uses: geekplux/markvis@masterbakes chart blocks to SVG and commits them, so github.com shows the charts. Inputspaths,commit,message; a second run commits nothing. It runs the markvis release it ships with, commits only the files bake writes, and never pushes for a pull request from a fork. - A 250-token model instruction (
docs/prompt.md, also the opening ofllms.txtand Integrate's Tell a model section).pnpm eval-prompts --outputs <dir>scores a directory of model answers: valid blocks, error codes, and chart type againstexamples/prompts.md. - Site: a one-screen home whose first screen is a chart block that types itself while
render()draws it, through each step from writing the block to its table fallback, on real data (the largest US energy flows in 2023, US EIA); a click opens it as a live editor. Below it: four ways in, a Your colors section (one SVG on a light and a dark page), and a card for each of the seventeen chart types. Charts on the site follow the site's light or dark mode, the social card is the hero's finished chart, and a Theming page lists the CSS variables. One generated page per chart type./themesand/airedirect to Theming and Integrate. - Wave 4 types:
dumbbell(two values per category, first hollow and second filled, signed change at the right)bullet(actual against an optionaltarget:column, optionalmin/max),boxplot(distributions from raw rows, type-7 quartiles, Tukey whiskers), andcalendar(one cell perYYYY-MM-DDday; new codeE_BAD_DATE). graphitetheme: editorial mono with a gray ladder, one accent on the largest value, pill bars, and heavy numbers. Inspired by lieflat-charts.- Playground Surface control (
?surface=dark). - Horizontal bars (
orient), surfaces (light/dark/export), heatmap color domain, waterfallrole, and width-awarerenderSvg. Seedocs/release-2.2.md.
Changed
- Inline SVG bytes: a chart drawn in a page (
render(), the drop-in, React, markdown-it, remark, rehype,renderSvg()) carries onestyleattribute per colored element and afont-familyvariable on the root, about 0.8 kB more per chart (about 0.1–0.2 kB gzip).bake,markvis render, andsurface: exportwrite the attribute only by default; host colors never change a baked file (--colors hostopts in). - The drop-in renders through
render(): an empty block's error table shows the block as the page holds it, not a fence wrapped around it. - The browser drop-in keeps cells past the header width in the error table, like every other path.
- markdown-it, remark, rehype, React, the drop-in, the Playground, and the command line's
previewandgalleryshare one HTML module, so their output is the same. The command line's error table now keeps cells past the header width too. - The themes now look distinct in structure, not just color: each owns its type ramp (floor: title 15, other roles 11), title alignment and subtitle, grid dash, axis lines and ticks, curve, point markers, legend keys, card frame, and dark paper. Every pair differs on at least four structural switches. See
docs/themes.md. - Readable type, wrapped labels, explicit paper/dark/export surfaces, gauge range 0–100 when
maxis omitted, sankey thickness. Funnel stays a centered silhouette.
Fixed
- Narrow widths: below the theme's width (720) a chart's height follows its width at the theme's aspect (never under 200), and waterfall step labels split over two lines no longer reach into the title. A title wraps at most once at any width and ends in an ellipsis; the full title stays in the tooltip.
- A line or area over whole-number x (weeks 1–4, years) spans its data instead of padding to a 0 or 5 that no row has: up to 12 distinct values, each is a tick; past that, only whole-number ticks inside the data.
- Values spanning about -1e308 to 1e308 no longer hang axis ticks (bar, dumbbell, bullet, boxplot) or crash histogram bins.
- A fence cut off before its closing line, or an indented fence, keeps its rows when parsed as one block.
- Chart ids no longer import
node:crypto, sorender()andmarkvis/reactbundle for the browser without a shim. Ids are unchanged. - A top legend no longer sits on a wrapped title, and the y-axis title no longer runs through tick labels or repeats the unit.
- Non-numeric cells, duplicate keys,
markvisversion, multi-chartcheck, bake references, ragged fallback cells, and line gaps for missing values. - Bullet charts keep a positive track at tiny widths; a value range that overflows to Infinity no longer writes
NaNinto a color or corner radius. Horizontal bar, dumbbell, boxplot, heatmap, pie and the axis charts also keep a positive plot (or radius) at tiny widths, and row charts drop their per-row value labels when there is no room for them. - A stacked or percent bar reads as one bar in a theme with rounded bars: only its outermost segment is capped, the segments under it are plain rectangles, and the cap sits on the last series that has a value in that category. Before, every segment carried its own rounded end.
What 2.2.0 ships. Language version stays markvis: 2. The new ways to draw a block (render, markvis/react, the script tag in any Markdown page, markvis/rehype, the GitHub Action, the model instruction) are listed in CHANGELOG.md; this note covers the chart changes and the site.
Charts follow the host page
The one chart-visible change beyond layout: a chart drawn in a page takes its colors from the page.
- Every chart drawn in a page (the script tag,
render(),renderSvg(), React, markdown-it, remark, rehype) writes each color twice: a fixed attribute, and astylethat reads a CSS variable with the same color as its fallback, for examplefill="#3B82F6" style="fill:var(--markvis-series-1,#3B82F6)". The root<svg>reads--markvis-font. - Eighteen color variables:
--markvis-ink,--markvis-muted,--markvis-grid,--markvis-paper,--markvis-plate,--markvis-plot,--markvis-series-1through--markvis-series-8,--markvis-accent,--markvis-up,--markvis-down,--markvis-total, plus the opt-in--markvis-font. They ship asCSS_VARIABLESandPAINT_NAMESfrommarkvis/themes. - A page that sets none of them sees the same colors as before. Where styles are stripped, or the SVG is opened as an image, the fixed color shows.
- Files keep fixed colors:
markvis bake,markvis render, andsurface: exportwrite the attribute only; host colors never change a baked file (--colors hostopts in).colors: "host" | "static"onrender(),renderSvg(),initialize(), and<Markvis>, and--colors host|staticonrenderandbake, choose either mode. - Heatmap and calendar color mixes, labels drawn on a mark, and the export plot border stay fixed. The Theming page lists every variable and three host snippets (system light or dark mode, a dark class on the page, and an editor theme's own variables).
- Inline SVG grows by about 0.8 kB per chart (about 0.1 to 0.2 kB gzip).
Narrow widths
- Below the theme's width (720) a chart's height follows its width at the theme's aspect, never under 200. A title wraps at most once and ends in an ellipsis; the full title stays in the tooltip.
- A line or area over whole-number x (weeks, years) spans its data with whole-number ticks instead of padding to a value no row has.
Site
markvis.js.org, not an npm API change:
- The home page fits its point on one screen. The first screen is a chart block that types itself, and the chart beside it is
render()on that text, step by step: write, paste, change the kind (bar, sankey, treemap, heatmap), stack, theme, palette, page color, dark, width, and the table that stays when a block is invalid. The rows are real: the ten largest US source-to-sector energy flows in 2023 (US EIA, Monthly Energy Review). A click or a key opens the block as a live editor; reduced motion shows the finished chart with a Play button. - Below it: four ways in (script tag, JavaScript, React, GitHub), Your colors (one SVG drawn on a light and a dark page, each setting a few
--markvis-*variables), and "Seventeen kinds of chart": one card per type, each drawing the first example of that type, with its block and an Open in Play link to the same file. A numbers line (58 kB gzip · 0 dependencies · under 1 ms a chart · plain SVG) is held todist/markvis.min.jsby a test. - Charts on the site, in the examples gallery, and in Play follow the site's light or dark mode through the same CSS variables, so no chart sits on a white card in dark mode.
- The social card (
og.png) is the hero's finished chart. - The docs sidebar runs Get started, Integrate, Theming, Chart types, Spec, Contributing. Theme content and the CSS variables live on the new Theming page; the model instruction moves to Integrate's "Tell a model".
/themesand/airedirect. - The Playground is called Play.
- The README opens with a chart block and its picture, the numbers line, four ways in, and the model line.
User-visible improvements
- Figures are laid out for the width you ask for.
renderSvg(chart, { width: 390 })reflows the title, labels, and plot. Type stays about 21px for titles and 12px for ticks instead of shrinking with the SVG. - Long titles wrap, including the unit on the last line, and the plot starts below those lines. Long category labels wrap or truncate, and the full string stays in an SVG
<title>. - Bar charts accept
orient: horizontalfor long labels. Vertical fences stay vertical. Input order and a zero baseline stay. - Heatmaps draw a labeled color scale. A missing cell is hatched, not painted as the low color.
minandmaxshare that scale across figures. - Treemaps use squarified tiles, parent headings, and parent-related colors. Tiny cells are not labeled.
- Sankey ribbons use one thickness for a value, at both ends and across the figure. Outer labels use that margin, and a truncated name keeps the full string in a
<title>. - Waterfall bars show the delta and the level after it. Increases and decreases use different colors and a sign. An optional
rolecolumn can marktotalorsubtotal. Step text is never treated as a total. - Gauges are a labeled range. If you omit
max, the range is 0–100, and the unit sits next to the value. A value outside the range is marked above or below range. - Radar charts show numeric rings, one scale for every series, and lighter fills. A grouped bar is the clearer choice when the exact number matters.
- Funnels stay centered bands that narrow from each stage to the next, with the stage name and value to the right of the shape.
- Line and area charts leave a gap for an empty cell. They do not connect through it or drop it to zero.
surface: light(default) keeps a transparent canvas and dark ink, so the figure sits on the Markdown host.surface: darkandsurface: exportpaint their own paper.
Correctness fixes
0stays zero. An empty measure cell is missing.N/A,abc, and any other non-numeric text isE_BAD_NUMBERwith the row and column. The original table is kept.- A repeated category/series key is
E_DUP_KEY. Scatter, histogram, and waterfall step labels may repeat. Nothing silently keeps the last row. - Tick and value labels use one formatter.
0.001,0.002, and0.003stay distinct. Negative zero is shown as0. - Sankey thickness is
value × one scale. Equal values match. A cycle isE_SANKEY_CYCLE. markvis checkreads every chart in a file and in a directory. One invalid chart makes the exit code non-zero and names the file and chart. Valid siblings are still reported.markvis bakerewrites an owned image reference when the chart count changes, overwrites the SVG that reference points at, and leaves author images alone. A second bake of an unchanged file does not rewrite it. A generated file is deleted only when this bake owned it and no chart still references it.markvis: 99isE_BAD_VERSION. Omittingmarkvisstill means version 2.- Fallback HTML from markdown-it and remark keeps extra cells that the parser kept.
Intentional visual changes
- Default type is larger. Titles are 21px, ticks 12px, values 13px.
- The default figure does not paint a background. The host page does.
- Category labels are no longer rotated 55 degrees.
- Theme packs still differ in chrome, palette, and any type size already larger than the readable floor.
Compatibility
- Existing vertical fences, themes, palettes, and layouts still parse.
- These inputs now fail instead of drawing a misleading figure:
markvisset to anything other than2- a non-numeric measure cell
- an empty cell on pie, funnel, sankey, treemap, waterfall, gauge, or a stacked/percent layout
- a repeated category/series key on bar, line, area, pie, heatmap, radar, sankey, or treemap
- a sankey cycle
- a gauge with more than one row
- A gauge that omitted
maxused to draw a full arc (max(y, 1)). It now draws against 0–100 unless you setmax. - Baked filenames for a single chart stay
name.svg. Adding a second chart renames the owned reference toname-1.svgand writesname-2.svg.
Migration
- Put
maxon a gauge whose value is not a percent of 100. - Replace
N/Aor blank required cells, or switch a line/area chart to a gap by leaving the cell empty. - Split or delete duplicate category/series rows. Do not rely on the last row winning.
- Remove sankey cycles, or the chart will fail and keep the table.
- Re-run
markvis bakeonce so image references match the new filenames after you add or reorder charts.