YAML Reference¶
Complete syntax for ui-sketch blocks. For a gentler intro, read Getting Started first.
Top-level keys¶
viewport: desktop # desktop | tablet | mobile | custom (default: desktop)
width: 375 # number (px) — required when viewport: custom
height: 640 # number (px) — required when viewport: custom
theme: adaptive # only "adaptive" is supported in v0.2
background: default # default | muted | transparent
screen: # required: layout array OR a single grid object
- ...
Viewport presets¶
| Value | Width | Use case |
|---|---|---|
desktop |
1200px | Standard web UI, the default |
tablet |
768px | Responsive breakpoint sketch |
mobile |
375px | iPhone-sized mockup |
custom |
— | Any size; requires width: and height: |
Background¶
default— matches Obsidian's primary surface (most themes = note body color)muted— slightly darker/lighter surface for contrasttransparent— no background (lets the note color show through)
Theme¶
theme: adaptive means the wireframe inherits Obsidian's CSS variables — it adjusts automatically to light/dark/community themes. No other values are supported in v0.2.
The screen key — two layout models¶
screen is either a layout array (flex model) or a single grid object. The two are mutually exclusive at the root.
Flex array (primary)¶
Top-level is an array. Each item is a single-keyed map: the key is either a layout primitive (row, col) or a component type.
screen:
- navbar: { brand: "DocHub" }
- row:
gap: 16
items:
- card: { title: "Card A" }
- card: { title: "Card B" }
Named-area grid¶
Top-level is a single grid: object with CSS Grid template areas. Inside map:, assign one component per named area.
screen:
grid:
areas:
- "nav nav nav"
- "side main main"
- "side foot foot"
cols: "180px 1fr 1fr" # optional; matches column count in areas
rows: "56px 1fr 48px" # optional; matches row count
map:
nav: { navbar: { brand: "MyApp" } }
side: { sidebar: { items: ["Home", "Docs"] } }
main: { card: { title: "Welcome" } }
foot: { text: { value: "© 2026" } }
Grid cells can only hold a single component; nest a container: inside if you need composition.
Layout primitives¶
row¶
Lays out items horizontally (flex row).
| Prop | Type | Description |
|---|---|---|
gap |
number | Pixels between items |
items |
array | Child entries (components or nested row/col) |
row:
gap: 12
items:
- button: { label: "Cancel", variant: ghost }
- button: { label: "Save", variant: primary }
col¶
Lays out items vertically (flex column). Useful to group items that share a column inside a row.
| Prop | Type | Description |
|---|---|---|
flex |
number | Flex-grow ratio (e.g. 1, 3) when inside a row |
items |
array | Child entries |
row:
gap: 16
items:
- col: { flex: 1, items: [ { sidebar: { items: ["Home"] } } ] }
- col: { flex: 3, items: [ { card: { title: "Main" } } ] }
Base props (all components)¶
Every component, in addition to its type-specific props, accepts these base props:
| Prop | Type | Description |
|---|---|---|
id |
string | Optional identifier (CSS-safe) |
w |
number | string | Width: number = px, string = any CSS length (e.g. "50%", "12rem") |
h |
number | string | Height: same rules |
align |
start | center | end |
Self-alignment along cross-axis |
pad |
number | string | Padding (number = px, string = CSS) |
note |
string | Hover tooltip annotation — adds a ⓘ marker |
muted |
boolean | De-emphasize (reduces opacity/contrast) |
Alignment idioms¶
Flex primitives cover most layouts, but two things need explicit patterns and are easy to get wrong the first time: right-aligning items in a row, and keeping label columns aligned across rows that don't have a label.
Right-aligning items in a row¶
Why align: end doesn't work here. align is a base prop that maps to CSS alignSelf — it positions an element along the cross-axis of its flex parent. Inside a row (horizontal flex), the cross-axis is vertical, so align: end on the row or its children pushes them down, not right.
To push items to the right edge, use an empty col with flex: 1 as a spacer. The flex-grow: 1 absorbs remaining horizontal space, shifting the following items to the end:
- row:
gap: 8
items:
- col: { flex: 1, items: [] } # absorbs remaining space
- button: { label: "Cancel", variant: ghost }
- button: { label: "Save", variant: primary }
Keeping label columns aligned across rows¶
In a vertically-stacked form, every row typically starts with a label (e.g. text: { value: "Name", w: 120 }), which makes all the inputs line up at x = 128px. But a row that groups several inputs without a label starts at x = 0, breaking alignment:
# ✗ Misaligned — inputs start from the left edge
- row:
items:
- text: { value: "Name", w: 120 }
- input: { placeholder: "Name", w: 420 }
- row:
items:
- input: { placeholder: "Purpose", w: 200 } # ← starts at x=0
- input: { placeholder: "Spec", w: 200 }
Add a dummy text with an empty value and the same width to preserve the label column:
# ✓ Aligned — dummy text reserves the label column
- row:
items:
- text: { value: "", w: 120 }
- input: { placeholder: "Purpose", w: 200 }
- input: { placeholder: "Spec", w: 200 }
Space-between: push the last item right¶
Combine both patterns — put a flex-grow spacer in the middle of a row to split items left/right:
- row:
gap: 8
align: center
items:
- heading: { level: 3, text: "Account settings" }
- col: { flex: 1, items: [] } # pushes the button right
- button: { label: "Delete account", variant: danger }
Safety limits¶
To prevent a runaway block from freezing Obsidian, the plugin enforces:
- Max tree depth: 32 (
row/col/grid/component nesting combined) - Max node count: 5000 per block
Hitting either produces a block-level error. If you need more, split into multiple ui-sketch blocks across several code fences.
The raw: escape hatch¶
For cases where a builtin component doesn't fit, raw: accepts a limited subset of HTML (piped through sanitize-html):
See raw component reference for the allowlist and security notes.
Error levels¶
| Level | When | Visual |
|---|---|---|
| L1 YAML parse | Syntax error | Block-level red box with line/col |
| L2 Structure | Missing screen, bad viewport, etc. |
Block-level red box with path |
| L3 Component | Unknown type or invalid prop | Inline red box at that position — rest still renders |
| L4 Empty | No content | Friendly placeholder with starter example |
Unknown component types get Levenshtein-based suggestions (butn → "Did you mean button?").
See Troubleshooting for the common mistakes.