7. Nodes
12 primitives. All accept position (SPEC 5) and paint (SPEC 6);
closed primitives also accept multiple, rotate, shadow. Text is not a primitive —
it is bare content (SPEC 3); the frameless |block| box
(SPEC 8) is what you reach for when text needs an id, a class, a link, or box
layout.
Dimensions use width / height, each defaulting to auto (content + padding,
border-box — SPEC 5). They are always bbox dimensions:
|oval| { width: 60; height: 40 } is an ellipse in a 60×40 box; equal dimensions (or an
empty |oval|) make a circle.
| Primitive | Required | Notes |
|---|---|---|
|block| | size (auto) | The base rectangle — frameless (no fill/stroke, radius: 0, padding: 0), like a div. It keeps stroke-width: 2 (invisible while stroke: none), so a styled block gets a sensible border. |box| frames + rounds it, |rect| frames it sharp (SPEC 8). |
|oval| | size (auto) | Bbox ellipse; equal width/height = circle. |
|hex| | size (auto) | Regular hex, flat top/bottom. |
|slant| | size (auto) | Parallelogram; top edge shifted tan(skew) × h. skew in degrees, (-89, 89), default 15. |
|cyl| | size (auto) | Cylinder; end ellipses ≈ h/10. |
|diamond| | size (auto) | Rhombus inscribed in the bbox. |
|poly| | points | ≥3 points, local (centre-origin) coords. Closed. |
|path| | path | Raw SVG path. Native top-left coords. |
|line| | points | 2+ points. Markers via marker*:. |
|icon| | symbol | A Phosphor icon — symbol: (or the label) names it; paints two-tone like a box (fill body, stroke line, counter-scaled stroke-width). A square that grows with its [ ] text (32 floor); |sign| is the larger preset. See Icons. |
|image| | src, width, height | A picture — src: a URL, a data URI, or a local path; local files embed (see Images); both dimensions required. fit maps it into the box — auto (default, letterbox), contain, cover, or stretch. |
|sketch| | draw | A pen that folds to a path — profiles drawn call by call, with named points and edges, mirroring, and view breaks (SPEC 15.3). Closed-primitive paint; bbox from the geometry. |
radius rounds a rectangle’s corners — |box| defaults to 8, |block| / |rect|
to 0. It is honoured on the rectangle (and on a multi-point |line|’s joins); radius
on the non-rect primitives (hex / diamond / slant / poly) is deferred (SPEC 24).
Visual modifiers (closed primitives)
| Property | Forms | Effect |
|---|---|---|
stroke-style | solid / dashed / dotted / center / phantom | Stroke pattern. Default solid. center (dash-dot) and phantom (dash-dot-dot) are the drafting line conventions — axes and alternate positions — valid on shapes and |line|s everywhere (SPEC 15.7); a link’s set stays solid / dashed / dotted / wavy (SPEC 9). wavy is link-only by design — a wire waves, an outline never does. |
multiple | N / dx dy | Draw one offset duplicate behind the node — the “several of these” reading. Scalar N = N -N. |
rotate | N degrees | Rotate around the bbox centre (SPEC 5). |
shadow | N / dx dy / dx dy blur / dx dy blur color | Drop shadow via SVG <filter>. Scalar N = offset N N, blur N; tint defaults to --lini-shadow-color. |
Markers (on |line| and links)
| Property | Effect |
|---|---|
marker: X | Both ends. |
marker-start: X | Start end (link source). |
marker-end: X | End end (link target). |
Values: none, arrow, dot, circle, diamond, datum (the filled drafting
triangle a drawing’s >- leader lowers to — SPEC 15.7), and the ER cardinality set —
crow (the “many” foot — many is its alias), one (a bar |), exactly-one (a double bar ‖),
zero-or-one, one-or-many, zero-or-many (a bar or ○ paired with the foot). The
compositional operators -+ / -< / -o+ / -+< / -o< / -++ are sugar over this
set (SPEC 9). circle is a larger dot — a filled point sized for
hovering or reading (on a chart line it marks a data point; SPEC 14). Markers scale
with stroke-width (a link’s wire and a shape’s outline alike; the size law is
SPEC 19’s); colour follows the stroke.
|line| is bare by default — write |line| { marker-end: arrow } for a one-shot
arrow. For links the operator picks markers (see SPEC 9). Source order wins:
marker: arrow; marker-end: dot → start arrow, end dot.
Icons
|icon| draws a Phosphor icon (MIT) as inline SVG
paths — themeable, reproducible, and renderer-agnostic (no icon font). The symbol
property names it — or, as the smart label, the string does (|icon| "heart" is
|icon| { symbol: heart }); everything else paints like a box:
|icon| "bell" // symbol via the label
|icon| { symbol: warning-circle; stroke: --amber-ink } // the longhand
|icon| "heart" { fill: --rose-wash; stroke: --rose-ink }
|icon#tag| "bell" [ "3" ] // symbol bell, "3" rides as text
Setting the symbol twice — a label and { symbol: … } — is an error; pick one.
Phosphor icons are two-tone (a soft fill behind a line), so an icon wears Lini’s
paint roles like any node: fill the body (default the soft grey --icon-fill),
stroke the line (default --stroke, matching borders and wires),
stroke-width its weight (default 2). A single-tone line icon is fill: none.
stroke-width is counter-scaled: an icon is authored on a 256-unit grid and fit
to its box, and the stroke is divided by that scale (baked at compile time), so its
line weight holds as the icon resizes.
An icon is a square that grows uniformly with its [ ] text (and padding): the
side is a 32 floor (icon-size) over the text + padding on either axis — an empty
icon is 32×32; a longer label scales the whole icon up, symbol and all, never
distorting it. For a larger stand-alone icon, reach for |sign| (SPEC 8).
fit controls how the symbol fills that box. auto (default) keeps Phosphor’s
authored framing — each glyph’s built-in 256-grid margin — so a row of mixed icons
reads at an even weight; contain scales the glyph’s own bounds up to meet the
box (|sign|’s default); cover scales until the box is covered (may overflow);
stretch fits both axes (may distort). The counter-scaled stroke-width follows
the resulting scale, so line weight is constant whichever fit you choose.
A missing symbol errors like |poly| without points; an unknown one suggests the
nearest name. Only the icons a diagram uses are embedded (a default-on icons feature,
SPEC 24).
Images
|image|’s src: takes an HTTP(S) URL, a data: URI, or a local path,
resolved against the source .lini file’s directory. A local file’s bytes are read
once, at resolve — a missing or unreadable path errors at the src: span — and
embedded in the output (SPEC 18): SVG as a nested, id-isolated
<svg>; raster (PNG / JPEG / GIF / WebP) as a base64 data URI. Embedding is the one
behaviour for a path (there is no opt-out — a self-contained SVG is the output
contract) and is deterministic from the bytes: the same file and assets give
byte-identical output on every run. The compiler never touches the network — URLs and
authored data URIs pass through untouched, so a URL is the authored non-embedded
form. Under lini serve, assets resolve inside the served root only
(SPEC 20).