18. SVG Output
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="X Y W H" width="W" height="H" class="lini lini-scope-HHHHHHHH">
<style>
@layer lini.defaults {
:root, .lini-scope-HHHHHHHH { color-scheme: light dark; /* --lini-*: light-dark(…, …) */ }
.lini-scope-HHHHHHHH[data-theme="dark"], [data-theme="dark"] .lini-scope-HHHHHHHH { color-scheme: dark; }
.lini-scope-HHHHHHHH[data-theme="light"], [data-theme="light"] .lini-scope-HHHHHHHH { color-scheme: light; }
}
.lini-scope-HHHHHHHH { font-family: var(--lini-font-family); font-size: 15px; font-weight: var(--lini-font-weight); color: var(--lini-text-color); }
.lini-scope-HHHHHHHH .lini-canvas { fill: #eef; } /* only when the scene sets a background */
.lini-scope-HHHHHHHH .lini-box { fill: var(--lini-fill); stroke: var(--lini-stroke); stroke-width: 2; }
.lini-scope-HHHHHHHH .lini-style-hot { stroke-width: 3; } /* one rule per class def */
.lini-scope-HHHHHHHH .lini-link { stroke: var(--lini-stroke); stroke-width: 2; fill: none; }
</style>
<defs><!-- filters, gradients, clipPaths --></defs>
<rect class="lini-canvas" .../> <!-- …and then this plate, over the viewBox -->
<g class="lini-scene"> <!-- scene tree --> </g>
<g class="lini-links"> <!-- links --> </g>
</svg>
A figure paints no background it was not given. The lini-canvas plate — rect
and rule — is emitted only when the scene asks for one: a root fill: (a schematic
root’s --lini-sheet wash rides exactly this), or --static, whose output is a
standalone document for renderers with no CSS variables and so carries its own opaque
--lini-bg backdrop. Otherwise there is no rect and no rule, so a figure inlined in a
page shows the page through it with nothing to override. fill: --bg on the root is
how a live figure asks for the themed backdrop; fill: none is that default said out
loud.
viewBox auto-sizes to content + the scene’s padding (20 px by default) on every
side. When a file’s drawn content
is only |page|s (SPEC 15.8), the root width
and height carry the sheet’s trimmed size in real millimetres rather than pixels,
so a print is true-scale; the viewBox is unchanged, so on-screen layout and CSS sizing
are not.
Names are content-addressed. Two figures inlined in one HTML document share its
id and selector spaces, so every name Lini writes into either comes from the thing
it names: a <defs> id from its definition, an asset’s prefix from its bytes, a
glyph from its outline, the root’s lini-scope-HHHHHHHH class from its stylesheet’s
text. Figures then collide only on equal things, where sharing is correct —
url(#…) resolves to an equal def, a duplicate rule is a no-op. That class heads
every selector in place of .lini (one class either way, so specificity and host
overrides are unchanged); lini stays on the root as the host hook.
Paint compiles to CSS; geometry bakes. Node and link paint defaults — and every
rule — are stated once as class rules; only the classes actually used are emitted — and
likewise only the --lini-* variables actually referenced, so the built-in palette
(SPEC 10.2) adds nothing unless a diagram uses it. A node whose
resolved paint differs from those rules carries the difference as an inline style="…"
(inline beats class, mirroring the cascade). Geometry —
sizes, positions (pin and translate fold into the baked origin), radii, points, paths,
transforms — is always baked into attributes. Inherited text properties state on .lini
and cascade natively; a node’s own text property emits on its <g> (or directly on the
<text>) and inherits to its subtree.
Fonts — three output modes (SPEC 6). By default the SVG
carries names only — zero font bytes; the stack leads with the bundled family
names, so an installed or hosted copy engages. --embed-font inlines a base64
@font-face per family × weight actually used, under Lini-scoped family names
(never colliding with a user’s installed versions) — browser-faithful and
browser-only by design: resvg and librsvg ignore @font-face.
--static (SPEC 10.6) outlines text to paths — glyphs
deduplicated through <defs> / <use>, italic as synthetic oblique: faithful
in every renderer for the bundled charset (Latin-1, Latin Extended-A, general
punctuation). A run holding any other character — CJK, Arabic, an arrow, an emoji —
stays a <text> drawn by the viewer’s fonts, and the export warns, naming the
characters (SPEC 21); the same holds for a font-family override, whose
name is emitted but whose glyphs are not bundled (SPEC 6).
Layout never varies by mode — measurement always reads the compiled-in metrics
tables (SPEC 5), a character outside them at a flat estimate.
Embedded assets. A local |image| (SPEC 7) emits its resolved form:
an SVG asset nests as a child <svg> mapped into the node box (fit: sets its
preserveAspectRatio) — with every id prefixed lini-aHHHHHHHH- (a tag of the
asset’s own bytes) and every internal reference rewritten to match (url(#…)
in attributes and inline style, fragment href / xlink:href), since nesting
alone does not isolate ids; a raster asset emits
<image href="data:…;base64,…"/>. Authored URLs and data URIs emit unchanged.
Embedding is deterministic from the asset bytes.
Box:
<g class="lini-node lini-{type} lini-{base} lini-style-{class}"
data-id="ID" transform="translate(X,Y)">
<title>…</title> <!-- when `hint:` is set -->
<!-- geometry, then children -->
</g>
Auto-classes: lini-node (every box); lini-{name} (the type and every type it
inherits, down to lini-block); lini-style-{name} (per worn class). With rotation,
the transform becomes translate(X,Y) rotate(N).
Text emits a bare <text class="lini-text">…</text> at its placed position — no
wrapping <g>; a worn class joins it (class="lini-text lini-style-quiet"). A table’s
cells are |block|s wrapping their text, so each renders as a
<g class="lini-block …"><text>…</text></g>; the header and any |footer| cells carry
a fill, a body cell is frameless (SPEC 8). Text’s font and colour come by
inheritance from the enclosing <g>; a string’s own style block emits as a style="…"
(and translate / rotate as a transform) on the <text> itself.
Link:
<g class="lini-link lini-style-{class}" data-from="A" data-to="B">
<path d="…" fill="none" stroke="…"/>
<polygon class="lini-marker lini-marker-arrow" …/>
<text class="lini-text" …>label</text> <!-- placed by along: -->
</g>
Host CSS may restyle any lini--prefixed class; layout is computed at compile time, so
runtime restyling (a fatter stroke-width) restyles without re-layout. A chart’s or
sequence’s lowered primitives (SPEC 19) emit exactly like the boxes,
text, and lines above — a chart’s tooltip card is a <g class="lini-chart-tip">, a
reserved styling hook. Every generated dress is one rule, never a style= per
wearer, each rule emitted only when its role is actually worn, and an authored
class rule of the same name replaces the generated one (which is what lets
|halo| { … } restyle chrome scope-wide rather than layer under it). The hook
families:
| Family | Classes |
|---|---|
| core | lini-node · lini-{type} · lini-style-{class} · lini-text · lini-canvas · lini-gutter |
| link | lini-link · lini-link-label · lini-link-dashed / -dotted · lini-stray · lini-marker + lini-marker-{kind} (arrow·dot·circle·diamond·datum·dim·open) · lini-cut / lini-cut-bg (label mask) |
| chart | lini-chart-title · lini-chart-text (every other chart string) · lini-chart-legend · lini-chart-label · lini-chart-tip · lini-tip-N / lini-hit-N |
| sequence | lini-sequence-tab · lini-sequence-guard · lini-sequence-message |
| tree | lini-level-N · lini-hue-{name} (the mindmap walk) |
| drawing | lini-dim-line (dimension / leader linework) · lini-ext-line (--lini-stroke-light) · lini-dim-text (annotation text at the drawing’s link size, SPEC 10.5, and the link-label weight — no annotation leaf inlines either) · lini-dim (the restyled (-) tier’s compound, on dimension-owned chrome only) · lini-frame-cell / lini-frame-plate (GD&T) · lini-plane-end / -shaft / -arrow · lini-drafting-glyph · lini-datum-frame · lini-halo |
| floorplan | lini-door-leaf (a door’s leaf, a slider’s panels) · lini-door-swing (the quarter arc) · lini-window-sill · lini-stair-tread (a flight’s risers) · lini-stair-arrow (its up arrow) (SPEC 15.11) |
| schematic | lini-schematic-wire (a nested sheet’s dress) · lini-sch-line / -solid · lini-sch-tag-line · lini-tag-outline / -round / -flag-left / -flag-right / -flag-both · lini-net-run / lini-net-run-turned (a plain label’s run of trace, SPEC 16.4) · lini-pin-stub · lini-pin-number · lini-ref · lini-part-value |
| highlight | lini-tok-{kind} — a source listing’s token spans, not a figure’s: lini highlight writes them and lini highlight --css paints them (SPEC 20) |
| marker | lini-align-* / lini-justify-* (a table column’s carried alignment, SPEC 8) · lini-side-left / -right (which half of a bilateral tree a first-level topic fills, SPEC 12) · lini-pose-90 / -180 / -270 and lini-mirror-x / -y (a schematic part’s turn and flip, consumed at lowering, SPEC 16.1) · lini-carried (an annotation node riding a drawing statement’s [ ], SPEC 15.9) |
The last family is the odd one out: its classes carry structure, not paint. They emit no CSS rule and there is nothing in them for host CSS to restyle — the engine reads them back off the chain, and they are listed so nothing a figure emits is undocumented. Every other family above is a paint hook.
Generated ids are prefixed too — every one, without exception — each
tagged with what it names: lini-aHHHHHHHH- for embedded assets,
lini-shadow-HHHHHHHH / lini-clip-… / lini-gradient-… / lini-hatch-… /
lini-label-cut-… / lini-halo-… in <defs>, and --static’s glyph defs
lini-g{kind}{weight}-{gid} (an outline is equal whenever those three are, so
two figures share the def rather than collide). A detail view (|drawing| { of: <magnifier> }, SPEC 15.8)
clips to its region with one interned <clipPath> in <defs> and a clip-path= on
its group.