Frames, anchors & flow
This is what turns wiremark from "draws boxes" into "captures a user flow". By naming frames and linking them, you get an embedded navigation graph at no extra cost — and a way to compose shared app chrome once and reuse it.
Naming a frame: #id
A frame is named with a #id token on its Wireframe root. It is
HTML-anchor-like and unmistakable to parse:
Wireframe #login mobile
Wireframe #dashboard landscape
The name is optional, but you need one on any frame you want to link to.
Linking: to=#id
Any element can carry a to= property, which makes that element — or its
whole region — a clickable zone that navigates to a frame:
Button "Sign in" primary to=#dashboard
ListItem "Settings" to=#settings
Box * * to=#detail // an entire region is clickable
Card to=#product // the whole card navigates
The to= value is always a frame anchor.
Two deliberate constraints
These keep the flow model trivial:
to=targets frames only — never elements inside a frame. There is noto=#dashboard.settingsdeep-linking. The flow graph stays at the screen level.- The graph is inferred, never declared. There is no
from. The navigation graph is reconstructed entirely from whereto=links live. Read a document and you can build the full screen-to-screen flow with zero extra syntax.
Because links are inline and frame-level, a renderer can emit the inferred navigation as a Mermaid flowchart automatically — the same structure that powers the multi-frame flow view (see below).
Composing shared chrome: background=#id
Most apps repeat the same shell — app bar, nav rail, footer — on every screen.
Rather than copy it into each frame, author it once as its own frame and pull it
in underneath another with background=#id:
Wireframe #shell landscape visible=false
AppBar
Toolbar
Typography h6 "Acme"
Box 240px * // left nav rail, part of the shared chrome
Wireframe #dashboard landscape background=#shell
Grid cols=3 // only the screen-specific content; chrome comes from #shell
Card
Card
Card
#dashboard renders its grid on top of the #shell frame. Edit the shell once
and every screen that backgrounds it updates.
How it behaves:
- Resolution scope. The target may live in the same block or a different
block in the document. This is the one sanctioned cross-block dependency in
v0.1 — a frame that uses
background=is no longer independently renderable, because the renderer must resolve the id across the whole document. - Missing target. If the id cannot be resolved, the renderer draws the foreground frame alone and emits a warning. It never hard-fails.
- Chaining and cycles. Backgrounds may chain (
#abackgrounds#bwhich backgrounds#c), painted deepest-first. A frame must not reference itself directly or transitively; renderers detect cycles and break them with a warning. - Alignment and size. The foreground frame drives the screen size. The background is underlaid at the top-left with no scaling; if it is larger it simply overflows/clips. There is no fitting behavior.
Hiding a reusable frame: visible=false
A frame that exists only to be a background should not also be drawn as its own
screen. visible=false suppresses standalone rendering:
Wireframe #shell visible=false
AppBar
Toolbar
Typography h6 "Acme"
visibledefaults totrue.visible=falseomits the frame from standalone drawing but does not stop it being used as abackground=target. "Hidden" means "not drawn on its own", not "never drawn".
Multiple frames and the flow view
A single ```wireframe block can declare several Wireframe frames. When
more than one frame is visible, wiremark arranges them as a flow chart: each
frame is a node, positioned automatically over the inferred to=#id graph, with
hand-drawn connectors joining linked screens (the same structure toMermaid
emits).
Wireframe #home landscape
Grid cols=3
Card to=#details
Card to=#details
Wireframe #details landscape
Link "Back" to=#home
Typography h1 "Item details"
- Direction. The flow runs top-down by default. Put
direction=LRon a frame to lay it out left-to-right instead (direction=TDis the explicit default); a host may also pass{ direction }to the renderer. The first frame that declaresdirection=sets it for the whole document. - Background templates stay out of the flow. A
visible=falseframe (e.g. a#shellpulled in viabackground=#id) is never a flow node, but still composes underneath the screens that reference it. - Only resolvable links draw. A
to=#idwhose target is not a frame in the document — or a frame linking to itself — is left out of the graph and draws no connector; it never hard-fails. - Disconnected screens (frames nothing links to) are packed alongside the linked group rather than dropped.
A lone frame is unaffected: a single-Wireframe document renders exactly as it
always has, with no flow chrome.
Recap
#idnames a frame; put it on theWireframeroot.to=#idon any element links to a frame; the nav graph is inferred from those links.- Targets are frames only; there is no deep-linking and no
from. background=#idunderlays a shared frame;visible=falsekeeps that shared frame from drawing on its own.- Several visible frames auto-arrange into a connected flow chart;
direction=TD(default) ordirection=LRsets its orientation.
Next: Patterns & recipes.