Zaviel34 downloadsMobile mockups for plugins and vault apps: lay out screens on real phone frames, saved as small JSON files your coding agent reads and builds from.
Mobile mockups for Obsidian plugins and vault apps, designed with your coding agent.
English · 繁體中文
Sketch your plugin's screens on a real phone frame, inside your vault. Vellum keeps each screen as a small JSON file: your agent reads exactly where everything is and builds the plugin to match, with no screenshots and no long descriptions. Colours, fonts and icons come from your Obsidian theme, so the mockup already looks like the plugin.
The JSON is a layout intent, not native layout code. It maps directly to HTML and CSS, so to Obsidian plugins; for SwiftUI or Compose, see the FAQ.
Install · Quick start · With your agent · File format · FAQ
![]() |
![]() |
![]() |
![]() |
Screens of ZeLedger, a personal finance app built in Obsidian, designed in Vellum with Claude Code. Every number is made up.

Real devices iPhone 12 – 17, Pixel 8, Galaxy S24 / S25, or any size, with cut-outs and safe areas. |
Portrait and landscape A layout for each, one switch for the whole editor. |
Blocks and gravity Text and shapes keep an anchor point, an offset, and hug / fixed / fill sizes. |
Pictures and icons Images by address or vault path; Lucide icons by name. |
Frames Screens share a layout and store only what they change. |
Snapping Edges, middles, margins and neighbours, with guide lines. |
Screen map Every screen as a tree, joined by how you get there. |
Its own window A full editor on a computer, a bottom sheet on a phone. |
Plain JSON One file per screen, one block per line. Small diffs. |

Map: every screen as a tree; mocks have a dashed outline
zelvet-velvet/vellum.main.js, manifest.json and styles.css from the latest release into <vault>/.obsidian/plugins/vellum/, then enable Vellum.Screens/.Want sample screens? Copy examples/Vellum Demo.md into your vault.
Any agent that reads and writes files works: Claude Code, Codex, Cursor. No plugin API, no server.
![]() |
The card with the mix bars, as its file holds it:
One screen is one small file. The agent reads exactly where each thing is, and changes only what it needs to. |
Read github.com/zelvet-velvet/vellum's README for the file format.
Write each page of my app as a Vellum screen in Design/Screens, one JSON file per screen.
Use real text, pictures by https address and icons by Lucide name. Keep ids stable.
Draw a card's background or outline with the block's own fill / stroke, and a chip or tile with the text's own box,
not with rectangles under them.
[!TIP] Frames belong to what they frame: a block has its own
fill,strokeandsides, a text its own box. A rectangle drawn as a frame (an older screen) shows Make it the block's look or Make it the text's box when you choose it.Ask for two or three mocks (
"mock": true) as sub screens and keep the one you like. The agent can write while the editor is open: Vellum reloads the files and never overwrites your unsaved edits.
The screens of Morellonomicon and ZeLedger were designed this way, with Claude Code.
flowchart LR
D["Doc<br/>device, safe areas"] --> P["Page<br/>one screen"]
P --> B["Block<br/>x, y, w, h, kind"]
B --> I["Item<br/>text · rect · ellipse · line<br/>img · icon · row · col"]
P -. "inherit: true" .-> F["Parent page<br/>used as its frame"]
A folder holds one JSON file per screen, plus _doc.json for the device and the screen order. Show it in a note with:
```vellum
src: Design/Screens
```
{"id": "home", "name": "Home", "blocks": [
{"id": "b1", "name": "Title", "kind": "nav", "x": 16, "y": 70, "w": 370, "h": 44, "items": [
{"id": "t1", "type": "text", "v": "HOME", "font": "display", "size": 32, "g": "l"}]}]}
| Level | Fields |
|---|---|
| Doc | device; safe, safeLand (top bottom left right, each optional) |
| Page | name, note, parent, link (how you get there), inherit (true: the parent is its frame), device, bg, mock, blocks, land (the landscape layout) |
| Block | name, kind, note, x y w h (screen points), bleed (true: may go into the safe areas), its own look: fill stroke sw r and sides (the outline on some sides only: "b", "tb"…), items |
| Any item | g (anchor: tl t tr l c r bl b br), dx dy, w h (a number, "hug" or "fill"), op, note |
text |
v size wt font color ls lh upper, and a box of its own: fill stroke sw r pad (a chip, a tile, a button; with a set h the text sits in its middle) |
rect ellipse line |
fill stroke sw r (r: one radius, or four: [tl, tr, br, bl]) |
img |
src (https://… or a vault path), fit (cover · contain · fill), pos (CSS object-position), r |
icon |
name (Lucide), color size sw |
row col |
items gap pad main cross, fill stroke sw r |
Frames. A page with "inherit": true draws its parent's blocks under its own. An own block with the same id replaces the parent's one; the page stores only its own blocks.
Colours and fonts. Items use theme names (paper text dim line tile loud accent; ui display mono), set in Settings → Vellum, or any CSS value. The editor's own panels follow your Obsidian theme, or the Editor theme colours in the same settings.
| ← → ↑ ↓ | Nudge 1 pt, with Shift one grid step |
| Ctrl Z · Ctrl Shift Z | Undo · redo |
| Ctrl D · Delete | Duplicate · delete |
| Ctrl S | Save |
| Alt / Shift while dragging | No snapping · keep the shape |
| Hold a number box, then drag up / down | Change it (a click types; ↑ ↓ step it, with Shift ×10) |
Only to show a picture whose src is an https:// address, as an image in a note would. Vellum makes no requests of its own.
Yes, as a sketch your agent translates. Inside a block, items are placed the way native layouts work:
| Vellum | SwiftUI | Compose |
|---|---|---|
anchor point (g) |
alignment |
Alignment |
"hug" / "fill" |
.fixedSize() / .frame(maxWidth: .infinity) |
wrapContentSize() / fillMaxWidth() |
row / col |
HStack / VStack |
Row / Column |
Blocks themselves sit at fixed positions on the screen, so the agent turns those into stacks and spacing.
Yes. The editor takes the full screen, with its settings in a bottom sheet.
On the device, in Obsidian's storage for that vault, until you save.
npm install && npm run check && npm run build
npm run install-vault -- /path/to/vault
Vellum is free and made in my spare time. If it helps you, a coffee keeps it going.