Style, colour & themes
Every figure in the tour is dressed by the same cascade, so what this page shows about a chain of boxes holds for a chart, a sequence or a floor plan.
What the defaults already say
|box#ingest| "Ingest"
|box#queue| "Queue"
|cyl#store| "Store"
ingest -> queue "raw"
queue -> store "rows"
No stylesheet — and the figure still has a fill, a stroke colour, a corner radius, a font, a gap and a wire weight.
Those are the templates’ baked defaults: the bottom tier of the cascade, and what every section below is a way of overriding. A figure that says nothing about style is not unstyled.
One block at the root
{
gap: 44; padding: 26;
fill: --bg; color: --sky-ink; font-size: 13;
}
|box#ingest| "Ingest"
|box#queue| "Queue"
|cyl#store| "Store"
ingest -> queue "raw"
queue -> store "rows"
Declarations written straight into the root { } are the scene’s own
properties. padding is the SVG margin, and fill: --bg paints the backdrop a
figure otherwise leaves transparent.
The text family behaves differently from the rest: color, font-size,
font-family and font-weight inherit down the tree, nearest ancestor
winning. That is why both link labels take their colour and size from a root
that never mentions them.
Captions and link labels size themselves as fractions of the inherited
number, so one font-size at the root rescales the whole scene in proportion.
The ladder
{
gap: 44;
|box| { radius: 0; }
.stage { fill: --amber-wash; stroke: --amber-deep; color: --amber-ink; }
#store { stroke-width: 3; }
}
|box#ingest| "Ingest" .stage
|box#queue| "Queue" .stage
|cyl#store| "Store" .stage { fill: --gray-wash; }
ingest -> queue "raw"
queue -> store "rows"
A selector is a type (|box|, or |-| for links), a class, an id, or a
space-separated run of those for descendants (|group| |box|).
They stack in five tiers, low to high:
- Type rules —
|box| { } - Descendant rules —
|group| |box| { } - Classes —
.stage { } - The id rule —
#store { } - The node’s own
{ }
Most specific wins per property, and ties go to whichever was written later.
Follow the cylinder through all five and you can read the figure: it keeps its
corners because |box| never matched it at all, takes its ink from .stage,
its stroke weight from #store, and its grey from its own block.
Colour from the palette
{
gap: 44;
--brand: #6d5bd0;
|svc::box| { fill: --teal-wash; stroke: --teal-deep; color: --teal-ink; }
|-| { stroke: --gray-deep; }
#store { fill: --brand; stroke: --brand; color: white; }
}
|svc#ingest| "Ingest"
|svc#queue| "Queue"
|svc#store| "Store"
ingest -> queue -> store
Eleven hues — red rose orange amber lime green teal sky blue purple gray —
each carrying five tiers: wash, soft, the bare name, deep, ink.
They are named for the job they do, not for their lightness. Every colour
is a light-dark() pair, so a pale surface in light mode has to become a dark
one in dark mode — and a name like “light” would invert and start lying.
--teal-wash is the faint surface either way.
Wash fill, deep stroke, ink text is the recipe worth reaching for; most figures in this tour are exactly that.
--brand is your own variable. --name covers colours and the font family,
since sizes bake at compile time.
Gradients, hatches and shadows
{ gap: 30; }
|box#g| "gradient" { fill: gradient(--rose, --sky); color: white; stroke: none; }
|box#h| "hatch" { fill: hatch(45, 6, --gray-deep); }
|box#s| "shadow" { shadow: 3; }
|box#m| "multiple" { multiple: 3; fill: --lime-wash; stroke: --lime-deep; }
Four ways to make a fill say more than a colour does:
gradient(a, b)blends two hues at a flattering angle, on a fill or a stroke.linear-gradient(angle, …)andradial-gradient(…)are the control gates when you want the angle yourself.hatch(angle, pitch, colour)is the drafting section texture, and works on any fill.shadowdrops a soft shadow.multipledraws offset duplicates behind a node — the “several of these” reading.
Dark mode and themes
{ gap: 40; fill: --bg; padding: 24; }
|box#a| "Ingest" { fill: --teal-wash; stroke: --teal-deep; color: --teal-ink; }
|box#b| "Store" { fill: --amber-wash; stroke: --amber-deep; color: --amber-ink; }
a -> b
Toggle this book’s theme and the figure follows. Every colour Lini emits is a
light-dark() pair keyed on color-scheme, so one SVG carries both
palettes and follows the viewer’s OS with no script at all.
data-theme="dark"on any ancestor forces one.- At export,
lini --theme darkpins a palette —light,high-contrastandblueprinttoo, or a CSS file of your own--lini-*overrides. lini theme NAMEprints one to start from.
Everything sits in @layer lini.defaults, so a host page re-themes a figure
with plain CSS and no !important:
.lini { --lini-accent: #ff6600; --lini-font-family: Inter, sans-serif; }
Go deeper
Selectors, cascade & specificity has the full ladder, paint, stroke & text the property behaviour, and colour, variables & expressions the palette, gradients, hatches and the variable rules.