Tufte Chart

by Arvin Dang
5
4
3
2
1
Score: 35/100
New Plugin

Description

Render chart code blocks as Tufte-style ASCII charts using Markdown. - This plugin has not been manually reviewed by Obsidian staff.

Reviews

No reviews yet.

Stats

stars
downloads
0
forks
0
days
NaN
days
NaN
days
0
total PRs
0
open PRs
0
closed PRs
0
merged PRs
0
total issues
0
open issues
0
closed issues
0
commits

Latest Version

Invalid date

Changelog

README file from

Github

tufte

ASCII charts that go anywhere text goes, and a Mermaid-style chart format for Markdown. Paste CSV, get a clean Tufte-style chart in plain text — drop it in a README, a terminal, a commit message, a diff, or an LLM's reply. No images, no server, no renderer required at the other end.

🔗 Live playground: tufte.ai · 📦 npm: @tufte · 📄 Format spec: SPEC.md

Revenue by Quarter ($M)

Q1  ██████████████████████████████  36
Q2  ████████████████████████████████████  43
Q3  █████████████████████████  30
Q4  ████████████████████████████████████████████  52

That chart is just text. It renders identically on GitHub, in your terminal, and anywhere else — because there's nothing to render. That's the whole idea, and it's the one thing Mermaid can't do (its output is an image; ours is the artifact).


The chart format

A fenced code block tagged chart: a type, an optional "title", optional @directives, then your CSV. You write this:

```chart
hbar "Revenue by Quarter ($M)"
Q1, 36
Q2, 43
Q3, 30
Q4, 52
```

…and a plugin (or the CLI) turns it into the chart above. Lines starting with @ set options (@width, @height); the @ sigil keeps them from colliding with data labels that contain colons, like 0:00, 22.

More examples

Sparklines (multi-series — one row per series):

```chart
sparkline "System Metrics — 24h"
CPU, 12 25 38 55 72 88 72 55 38 25
Mem, 30 32 40 55 65 72 78 85 80 70
Net, 5 8 15 8 72 88 18 5 8 32
```
System Metrics — 24h

CPU  ▁▂▃▅▇█▇▅▃▂
Mem  ▁▁▂▄▅▆▇█▇▆
Net  ▁▁▂▁▇█▂▁▁▃

Inline sparklines — a sparkline is a single line, so it can also flow inside a sentence using a one-backtick code span (markdown-it / remark adapters, and the CLI):

CPU held steady `sparkline: 12 24 36 30 18` all morning.

renders as CPU held steady ▁▅█▇▃ all morning.

Tables (first row is the header):

```chart
table "Sprint Tracker"
Feature, Status, Owner
Explode View, Done, Marcus
Annotations, WIP, Arvin
Cross-Section, Next, Sara
```
Sprint Tracker

┌───────────────┬────────┬────────┐
│    Feature    │ Status │ Owner  │
├───────────────┼────────┼────────┤
│ Explode View  │  Done  │ Marcus │
│  Annotations  │  WIP   │ Arvin  │
│ Cross-Section │  Next  │  Sara  │
└───────────────┴────────┴────────┘

Chart types

Type Aliases Data per row
hbar bar label, value
vbar column label, value
sparkline series, v1 v2 v3 …
line label, value
table header row, then cells
histogram bin, count
progress label, percent (0–100)
scatter x, y

Full grammar and directives: SPEC.md.


Packages

Package Install What it does
@tufte/chart-core npm i @tufte/chart-core The renderer + format. Zero deps.
@tufte/chart-cli npm i -g @tufte/chart-cli Pre-render Markdown, or pipe a spec via stdin.
@tufte/markdown-it-chart npm i @tufte/markdown-it-chart markdown-it plugin (VitePress, Eleventy, CMSs).
@tufte/remark-chart npm i @tufte/remark-chart remark/unified plugin (Next, Astro, MDX, Docusaurus).

Not on npm: a Tufte Chart Obsidian plugin (in the Obsidian community store — renders chart blocks live in your notes) and a zero-install CDN build.


Usage

Library

import { render } from "@tufte/chart-core";

render(`hbar "Revenue by Quarter ($M)"
Q1, 36
Q2, 43
Q3, 30
Q4, 52`);
// → the ASCII chart as a string

Also exported: parseChartBlock, renderSpec, renderInPlace, GENERATORS.

Use it in Markdown

markdown-it — VitePress, Eleventy, many CMSs:

import MarkdownIt from "markdown-it";
import chart from "@tufte/markdown-it-chart";

const md = new MarkdownIt().use(chart);
md.render(source); // ```chart blocks become <pre class="tufte-chart">

remark / unified — Next.js, Astro, MDX, Docusaurus:

import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkChart from "@tufte/remark-chart";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";

const html = await unified()
  .use(remarkParse)
  .use(remarkChart)
  .use(remarkRehype)
  .use(rehypeStringify)
  .process(source);

Both emit <pre class="tufte-chart">…</pre>. A broken spec falls back to a normal code block, so a typo never breaks the page.

Use in Obsidian

The Tufte Chart plugin renders ```chart blocks live in Obsidian's reading view — same format, no build step, same fail-safe fallback (a broken spec shows its raw source instead of breaking the note):

```chart
line "Heart rate"
0:00, 22
8:00, 68
12:00, 74
```

Install it from Obsidian directly: Settings → Community plugins → Browse, search for Tufte Chart, and click Install — or view it in the community store. The plugin is built and released from this repo — see the plugin README for the build/release flow.

CLI

# Pipe a spec → ASCII
printf 'line "Latency"\n0:00, 22\n8:00, 68\n12:00, 110\n' | chart

# Pre-render every ```chart block in a Markdown file, in place (idempotent).
# Great in a pre-commit hook or CI so committed Markdown shows real charts
# everywhere — no plugin needed.
chart render README.md --write

The pre-render embeds the source in an HTML comment alongside the rendered ASCII, so the chart stays both readable (anywhere) and re-editable. Re-running picks up edits to the embedded source. This round-trip is what makes a chart block degrade gracefully where Markdown can't.

Use from a CDN

The packages are ESM, and @tufte/chart-core has zero dependencies — so it loads straight into a browser or Deno with no install and no build step.

Self-hosted (no npm required). A pre-bundled, pinned build of the core renderer is served off tufte.ai:

import { render } from "https://tufte.ai/[email protected]";
// or always-latest: https://tufte.ai/chart-core.js
render(`line "Latency"
0:00, 22
8:00, 68`);

npm-backed CDNs (esm.sh, jsDelivr, unpkg) mirror npm automatically — so they require the package to be published to npm first:

import { render } from "https://esm.sh/@tufte/[email protected]";
import { render } from "https://cdn.jsdelivr.net/npm/@tufte/[email protected]/src/index.js";
import { render } from "https://unpkg.com/@tufte/[email protected]/src/index.js";

For the markdown-it / remark plugins (which have dependencies), use esm.sh's ?bundle so deps load in one request — see each plugin's README.


Development

This is an npm-workspaces monorepo.

npm install        # install all workspaces
npm test           # run every package's tests
npm run build -w app   # build the tufte.ai site

Layout:

packages/core              @tufte/chart-core   — renderer + format + SPEC.md
packages/cli               @tufte/chart-cli    — pre-render / stdin CLI
packages/markdown-it-chart @tufte/markdown-it-chart
packages/remark-chart      @tufte/remark-chart
packages/obsidian-chart    Obsidian plugin (built + released here, not on npm)
app/                       the tufte.ai web app (playground + docs + CDN bundle)

Releases use Changesets: npm run changesetnpm run version-packagesnpm run release.

License

MIT © Arvin Dang