Open app
Contents Designs in Markdown

These docs follow the latest development version of vDesign.

Designs in Markdown

A ```vd fence holds a vDesign document. With these plugins it is drawn as the design rather than shown as its source, wherever the Markdown is turned into a page.

Source
version 4
kind design
title "Pricing card"
frame card w=480 h=200 background=#ffffff
  rect card.bg x=0 y=0 w=100% h=100% fill=#f4f4f6 radius=16
  text card.name x=32 y=32 w=300 h=29 size=24 weight=700 fill=#101014
    "Pro"
  text card.price x=32 y=80 w=400 h=58 size=48 weight=800 fill=#d64545
    "$49 a month"

That figure is a fence, drawn when the page was built. Open Source under it to see the fence: it asks for that with the source option.

VS Code's own Markdown preview drawing two vd fences

Options

After vd on the fence’s first line. The spelling is the same in every host, so a fence written once works in a docs site, VS Code and Obsidian.

OptionWhat it does
page=pricingDraw one page, by id or 0-based number. Default: the whole document.
src=home.vdDraw a .vd or .vd.svg file, relative to the Markdown file.
mode=viewA live view in the browser: pages, zoom and Present. Default: a still drawing.
height=480The live view’s height, in pixels.
sourceAlso show the fence’s text, folded, under the drawing.
alt="…"The drawing’s accessible name. Default: the document’s title.

Unknown options are ignored, so a fence written for a later version still draws.

A fence that cannot be drawn, for instance one with no page in it, stays the code block it would have been, with what was wrong listed under it.

A live view

With mode=view, the drawing is still made at build time, and a script then turns it into a view the reader can page through and present. With no script, or before it runs, the reader sees the drawing.

That is src= pointing at a three-page deck, drawn with mode=view height=380.

In a docs site

The plugins are entries of @getv.design/embed, version 0.2 and later. They import nothing from the Markdown library they plug into, so they work with whichever version a site has.

Site built withUse
VitePress, Slidev, Marp, markdown-it@getv.design/embed/markdown-it
Astro, Next.js MDX, Docusaurus, Gatsby, Starlight@getv.design/embed/rehype
marked@getv.design/embed/marked

markdown-it, for example in VitePress’s config.mts:

import { vdesignMarkdownIt, createRenderer } from '@getv.design/embed/markdown-it';

// Optional: lay text out from the font files, so lines break where the app breaks them.
const renderer = await createRenderer({ fonts: 'exact' });

export default {
  markdown: {
    config: (md) => md.use(vdesignMarkdownIt, { renderer }),
  },
};

rehype, for example in Astro’s astro.config.mjs:

import { rehypeVdesign } from '@getv.design/embed/rehype';

export default {
  markdown: { rehypePlugins: [rehypeVdesign] },
};

The rehype plugin lays text out exactly by default and reads src= files relative to each Markdown file, never outside the folder the build runs in.

Link the styles on the pages that show designs, from your copy of the package. fonts.css makes the drawings’ text Inter, from the fonts/ folder beside it; leave it out if your site has Inter already:

<link rel="stylesheet" href="/vendor/vdesign/markdown.css" />
<link rel="stylesheet" href="/vendor/vdesign/fonts.css" />

For live views, run hydrate once the page has loaded, and link view.css with the id the view looks for, so it injects no <style> of its own:

<link rel="stylesheet" id="vd-viewer-styles" href="/vendor/vdesign/view.css" />
<script type="module">
  import { hydrate } from '@getv.design/embed/auto';
  hydrate();
</script>

Untrusted Markdown

A fence in a wiki page or a README may be written by somebody else. The drawing follows only https:, mailto: and in-design links, draws pictures only from the document itself, fetches nothing, and runs nothing. A fence over 1 MB of text or 5,000 shapes stays a code block (limits changes both).

On GitHub

GitHub draws only the diagram languages it knows, and a ```vd fence shows as text there. Two ways to show a design in a README:

  • A .vd.svg. Export with “Keep the design inside” in the app: an SVG that GitHub, npm and every browser show as a picture, and that vDesign and the VS Code extension open as the design. See .vd.svg.
  • vd-render in CI. It writes an .svg beside each .vd, and --check fails a pull request whose SVGs are out of date:
npx -p @getv.design/embed vd-render designs/
# .github/workflows/designs.yml
name: Designs
on: pull_request
jobs:
  rendered:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npx -p @getv.design/embed vd-render designs/ --check

Then, in the README:

![Home](designs/home.svg)

The SVG names the font and does not carry it, so a reader without Inter sees the same layout in another sans-serif; a .vd.svg from the app carries its fonts.

In Obsidian

The vDesign plugin draws ```vd blocks in notes as live views, with the same options. Its src= reads from the vault.

In VS Code

The VS Code extension draws fences in VS Code’s own Markdown preview, as still drawings.