Remark and mdast plugins for creating interactive markdown documents.
Remark-Tangle extends plain text markdown with a special notation (based on links) for adding controls and variables with Tangle. It's like a lightweight and inline version of Jupyter, Observable, R, or Wolfram notebooks. Or, if you like, a messier alternative to spreadsheets.
A sample Model-Driven Markdown file looks like this:
### How many cookies?
When you eat [3 cookies](cookies=[0..100]), you consume **[150 calories](calories)**.
That's [7.5%](daily_percent&margin-right=0.5ch) of your recommended daily calories.
| Calculation for daily % | |
| ----------------------- | ----------------------- |
| [`cookies`](cookies) | = [5 cookies](cookies&margin-left=1ch) |
| [`calories_per_cookie`](calories_per_cookie) | = [50 calories](calories_per_cookie=[10..100;5]&margin-left=1ch) |
| [`calories`](calories) | = [`calories_per_cookie`](calories_per_cookie&margin-left=1ch&margin-right=1ch)*[`cookies`](cookies&margin-left=1ch&margin-right=1ch) = [150 calories](calories=calories_per_cookie*cookies&margin-left=1ch) |
| [`calories_per_day`](calories_per_day) | = [2000 calories](calories_per_day=[0..10000;100]&margin-left=1ch) |
| [`daily_percent`](daily_percent) | = [`calories`](calories&margin-left=1ch&margin-right=1ch)/[`calories_per_day`](calories_per_day&margin-left=1ch&margin-right=1ch) = [7.5%](daily_percent=calories/calories_per_day&margin-left=1ch) |
And, when compiled to html
, yields a page that looks like this :
The best part about the format (IMHO) is that it has a clean fallback in standard HTML (as long as you can ignore broken links):
When you eat 3 cookies, you consume 150 calories. That's 7.5% of your recommended daily calories.
Calculation for daily % cookies
= 5 cookies calories_per_cookie
= 50 calories calories
= calories_per_cookie
*cookies
= 150 caloriescalories_per_day
= 2000 calories daily_percent
= calories
/calories_per_day
= 7.5%
For a more in-depth example, take a look at this post introducing remark-tangle.
The project exports a plugin from /dist/index.js
that you should use after remarkParse
and before remarkRehype
.
To get this to work with Next.js, I've had to downgrade hastscript -> hastscript@^6.0.0
and unist-util-visit@^2.0.1
.
The unified plugin ecosystem is making a push to full esm modules, but next.config.js
only allows require
imports.
When next.js
makes the transition, I'll deprecate the commonjs build.
The default notation takes advantage of link notation to provide a clean fallback in standard markdown.
Tangle fields take the format: [display string](variable-configuration)
.
There's an alternative notation via
remark-directive if you prefer to leave your links untouched: :t[display string]{variable configuration}
.
For example:
:t[3 cookies]{num_cookies=[1..10;.5]}
defines a variable,num_cookies
, with default value3
, display template%d cookies
(using inferred printf notation), that can take values from the range1
to10
inclusive, with step size0.5
.:t[150. calories]{num_calories=num_cookies*num_calories_per_cookie}
defines a variable,num_calories
that depends on the values ofnum_cookies
andnum_calories_per_cookie
, with display template%.0f calories
.:t[150 calories]{num_calories}
creates a reference to a variable already defined elsewhere. These are automatically synchronized.
See the reference for a full account of different types of fields and their configuration.
Note: you can add other key-value pairs to customize the styling (which is useful for spacing). Use the separator
&
in link format and a blank space in directive-format:[5 cookies](cookies=[0..10]&margin-left=1ch)
vs.:t[5 cookies]{cookies=[0..10] margin-left=1ch}
There's still a lot to do.
- Syncing hovers & actives. If you scroll over a field, you should see any references to and dependencies of that field light up.
- More inputs & more data types (such as lists/distributions).
- Replacing Tangle, which hasn't been maintained in over a decade. It's time to move on.
This opens you up for some serious XSS vulnerabilities. Be careful, know what you are doing.
This project is still very early on, so I welcome any kind of support you might want to offer.
The concepts and notation are inspired by a bunch of different projects:
- 🙌 Tangle by Bret Victor is at the root of all of these projects.
- Active Markdown by Alec Perkins most inspired the syntax.
- Dynamic Markdown by Tal Lorberbaum
- Fangle by @jotux
- TangleDown by Nicholas Bollweg
MIT © Jesse Hoogland