Styles, themes and presets
A diagram’s look comes from its theme and from a small set of style statements. Everything is
written in the .meltemi file, so the same source renders the same way in the CLI, the editor and
every export format. There is no CSS.
Themes
Section titled “Themes”A theme is a set of design tokens. Eight themes are built in:
| Theme | For | Character |
|---|---|---|
technical.light (default), technical.dark |
architecture review | Inter, the Meltemi palette, a footer frame with the mark |
editorial.light, editorial.dark |
documents and long reading | IBM Plex Sans, quiet greys, open arrowheads, no frame |
stage.light, stage.dark |
presentations and video | Inter, high contrast, larger type and strokes, wide margins, a header frame |
sketch.light, sketch.dark |
whiteboards and early thinking | the technical themes with strokes painted by hand (sketch light) and rounded bends; Patrick Hand |
A diagram picks a theme with theme "<name>". Without one it uses the project’s default theme
("theme" in project.json, see projects.md) or technical.light. meltemi-diagrams export --theme <name> overrides both.
Theme blocks
Section titled “Theme blocks”theme <id> { } defines a theme at the top level of a file. It extends a built-in theme or another
theme and sets only the tokens it changes:
theme company { extends "technical.light" accent "#6a4fd0" labelSize 14 brand "Company · Meltemi Diagrams"
class partner { line dashed; stroke accent } rule data { pattern dots }}A theme without extends extends technical.light. Themes are imported like any block
(projects.md).
Tokens
Section titled “Tokens”| Group | Tokens | Value | technical.* |
|---|---|---|---|
| Colours | background, surface, ink, muted, line, connector, band, bandLine, tag, tagText, accent, accentEnd |
quoted colour | see svg-export.md |
| Signals | success, warning, danger, info |
quoted colour | #15803D, #B45309, #B91C1C, #0369A1 on light |
| Whiteboard | highlight (highlighter strokes), sticky (the card of stickies), stickyText (their text) |
quoted colour | #ffe600, #fff59d, #1f2328 on light; #ffd600, #f3e37c, #1f2328 on dark |
| Fonts | font, mono |
quoted CSS font stack | Inter, Cascadia Mono |
| Type | labelSize, descriptionSize, groupTitleSize, noteSize, tagSize, relationLabelSize, frameSize |
number, 6 to 48 | 13, 11, 13, 11, 10.5, 11, 12 |
labelWeight |
400, 500, 600 or 700 | 600 | |
| Spacing | padding (text to box edge), groupPadding, groupHeader (group title band), margin (around the canvas) |
number, 0 to 200 | 16, 16, 40, 20 |
| Shape | radius (boxes, notes), groupRadius: defaults that an element’s radius overrides |
number, 0 to 64 | 8, 14 |
| Strokes | strokeWidth (outlines), connectorWidth |
number, 0 to 8 | 1, 1.3 |
| Arrows | arrowhead, arrowSize, corners |
a head (below); number, 0 to 32; sharp or rounded |
triangle, 8, sharp (sketch.*: rounded) |
| Sketch | sketch |
none, light or rough |
none (sketch.*: light) |
| Icons | iconSize, iconPosition |
number, 8 to 64; left, right, top, bottom or center |
18, left (stage: 24, top) |
| Frame | frame, brand, mark |
none, footer or header; quoted text; meltemi or none |
footer, Meltemi Diagrams, meltemi |
| Motion | flowDuration, dashDuration, pulseDuration, traceDuration |
a time, 100ms to 60s |
none: 1200 ms for flow, 1500 ms for trace, 1 s otherwise |
| Canvas | selection, guide |
quoted colour | none: the canvas uses accent |
The canvas tokens colour the editor only: selection its selection, handles and the point a
connector end takes, guide its alignment guides and the outline and magnets of the box an end
is dragged over. Exports never use them, and styles cannot name them.
The whiteboard tokens are colour tokens like the others, set in every built-in theme and inherited
by the themes that extend them: stroke highlight colours a highlighter stroke, and stickies take
sticky and stickyText unless styled (free.md). stickyText is near-black
in the dark themes too, since a sticky is paper. Pens take ink (free.md).
The theme panel shows the three in a Whiteboard group.
sketch paints every stroke as if drawn by hand: outlines, connectors, heads, label pills, group
bands and the frame are drawn twice over a clean fill, each pass wobbling a little (light) or
more (rough), while text, icons and images stay crisp. The layout does not change, and every
export and the canvas show the same wobble for the same text
(svg-export.md). It is a theme token, not a style property: a
diagram is sketched whole or not at all, and style a { sketch rough } is prop.unknown.
theme whiteboard { extends "technical.light" sketch rough}Numbers are written without a unit or with px; durations with ms or s, as in animate. A
duration token is the default speed of its effect for every diagram that uses the theme: an
animate statement without duration takes it (motion). Line heights, pill heights, the frame strip and
default box sizes scale with the type sizes, and boxes without size grow to fit their text, so a
larger labelSize never makes text overflow.
Style statements
Section titled “Style statements”Three statements style a diagram, from the most general to the most specific:
diagram checkout: flow { theme "company"
rule data { pattern dots } // every data store class hot { stroke danger; width 2 } // a reusable set of properties
api["API"] { class hot, partner } // classes applied where an element is declared api -> db as save { class proposed } // and where a relation is written style db { class critical; fill "#fff" } // classes and explicit properties for targets}| Property | Value | Applies to |
|---|---|---|
fill |
quoted colour ("none" for no background) or colour token name |
elements, relation labels |
stroke |
quoted colour or colour token name | outlines, connectors and pens |
color |
quoted colour or colour token name | text |
line |
solid, dashed or dotted |
outlines, connectors and pens |
width |
number, 0 to 64 (1 to 64 on a pen) | outlines, connectors and pens |
opacity |
number, 0 (invisible) to 1 (opaque) | free elements, groups, lines and pens |
radius |
number, 0 to 64: the corner radius, over the theme’s radius or groupRadius |
elements and groups only |
rotation |
number, 0 to 360: degrees the element turns, clockwise about its box’s centre | flow and free elements |
shape |
one of the twenty-four shapes of free.md | flow nodes and free boxes |
pattern |
none, hatch, dots or cross |
elements only |
arrowhead |
the head at the target (below) | relations, free lines and pens |
arrowtail |
the head at the source (below) | relations, free lines and pens |
routing |
auto, orthogonal, polyline or bezier (layout) |
relations and free lines |
corners |
sharp or rounded bends of straight and elbow routes |
relations and free lines |
icon |
"<pack>:<name>" (assets.md) |
elements and relations |
iconColor |
quoted colour or colour token name | monochrome icons |
iconPosition |
left, right, top, bottom or center (icons in boxes) |
nodes, decisions, data, boxes |
labelPosition |
top, bottom, left or right: the edge of the label around a centred icon |
nodes, decisions, data, boxes |
iconSize |
auto, or a number from 8 to 128: the side of the icon |
nodes, decisions, data, boxes |
font |
a quoted font stack: the family the text is measured and drawn with (fonts.md) | everything that draws text |
fontSize, fontWeight |
the text’s size, 6 to 160, and weight, 400, 500, 600 or 700 (fonts.md) | everything that draws text |
textAlign |
left, center or right: where the text sits across its box |
free boxes, texts and stickies |
verticalAlign |
top, middle or bottom: where the text sits down its shape |
free boxes and stickies |
class |
one or more class ids, in style statements and declaration blocks |
elements and relations |
- A colour token name (
danger,accent,muted, …) follows the theme:stroke dangeris red in every theme, in that theme’s red. A bare word that is not a colour token isprop.invalid-value; write CSS colours quoted, as infill "red". patternoverlays the shape with hatching, dots or a grid in the element’s stroke colour (ormuted) at 35 % opacity, so meaning never depends on colour alone.pattern,iconPosition,labelPosition,iconSize,radius,rotationandopacityon a relation,arrowheadandarrowtailon an element other than a free line or a pen,routingorcornerson an element other than a free line, andshapeon anything but a flownodeor a freebox, areprop.invalid-valueinstylestatements and rules. A free line takes the properties of a relation and refuses those of a box (free.md); a pen takesstroke,width,line,opacity,arrowheadandarrowtailand refuses the others (free.md).font,fontSizeandfontWeightapply in every profile to what draws text, and icons, images and pens refuse them;textAlign,verticalAlignandopacityare free-only: flow diagrams refuse them. A class may hold them all; each target takes what applies to it.widthis an outline’s, a connector’s or a pen stroke’s width, from 0 to 64; a pen is drawn at least 1 px wide, sowidth 0on a pen isprop.invalid-value.width 20draws a highlighter stroke.opacityfades an element, a group with everything it holds, a free line or a pen of a free diagram, from 0 (invisible) to 1 (opaque); a story’s visibility multiplies with it.radiusrounds the corners of every shape that has them, clamped to half the shorter side: nodes androundedboxes (the theme’sradiusby default),rectangle,squareandframedboxes and nodes (square by default), the vertices of the polygons and of decisions (sharp by default; each is cutradiusalong both of its edges, at most half of each, and joined by a curve), the bodies ofpersonandagent, notes, framed texts, icons and images, and groups:style api { radius 0 }squares one node,style h { shape hexagon; radius 10 }softens a hexagon,rule group { radius 0 }squares every group. Connectors, magnets and the text area follow the rounded outline. Ellipses, circles, pills, cylinders, clouds, documents, data stores, tags and the kinds of sequence diagrams have no corners to round.rotationturns an element of a flow diagram or a free composition clockwise by that many degrees about the centre of its box, its text, icon and outside label with it:style api { rotation 90 },rule text { rotation 270 }.placeandsizekeep describing the unturned box. Connectors meet the turned outline; a port or a pinned point (api.north 30%) turns with the element and keeps its side’s name. The layout engine, groups drawn around their children and the composition checks use the turned drawing’s bounding box. Groups, sequence diagrams and C4 views refuse it withprop.invalid-value; 0 and 360 draw upright.shapedraws a node or a box as one of twenty-four shapes, with its own default size, text area and an outline that connectors meet (free.md):style api { shape hexagon },style planner { shape agent }for an AI agent, orstyle fn { icon "aws:lambda"; shape icon }for an element drawn as its icon. Decisions, data stores, notes, tags, groups, free texts, icons and images, sequence participants and C4 entities refuse it, since their kind decides their drawing.line dotteddraws round dots spaced by the line’s width;-->,---andline dasheddraw dashes.
Arrow heads
Section titled “Arrow heads”A relation has a head at its target (arrowhead, else the theme’s) and, optionally, at its source
(arrowtail). Without arrowtail the source has no head, or the target’s head when the relation
is written <-> or <-->. A relation written -- (a line) or --- (a dashed line) has no head
at either end unless arrowhead or arrowtail is written: the theme’s head is only the default of
the arrows.
api <-> cache // the theme's head at both endsapi -- db as link // a line: no head at either endstyle link { arrowhead circle } // unless a style writes onecustomer -> order as placesstyle places { arrowtail onlyOne; arrowhead zeroOrMany }| Head | Drawn as |
|---|---|
triangle, triangleOutline |
a filled or hollow triangle (UML generalisation) |
open |
a V |
diamond, diamondOutline |
a filled or hollow diamond (UML composition and aggregation) |
circle, circleOutline |
a filled or hollow circle |
bar, cross |
a bar across the line, an X |
many, oneOrMany, onlyOne |
crow’s foot, crow’s foot and bar, two bars (ER notation) |
zeroOrOne, zeroOrMany |
a bar or a crow’s foot, with a hollow circle behind it |
none |
no head |
Heads take the size of the theme’s arrowSize; hollow shapes are not filled, and the line stops
at their back, so nothing shows through them. corners rounded replaces each bend of an auto,
orthogonal or polyline route by a curve 10 px from it; Bézier routes are smooth already.
Icons in boxes
Section titled “Icons in boxes”The icon of a node, a decision, a data store or a box goes where iconPosition says, at the
size iconSize gives it:
diagram icons: flow { layout manual
api["API"] orders["Orders"] fn["Checkout"] api -> orders orders -> fn
placement { place api at (120, 80) place orders at (360, 80) place fn at (600, 80) }
style api { icon "tabler:server"; iconPosition right; iconSize 32 } style orders { icon "tabler:database"; iconPosition center; labelPosition top } style fn { icon "aws:lambda"; shape icon }}iconPosition |
Drawing |
|---|---|
left (default), right |
beside the text: the icon and the text form one row, centred in the shape’s text area |
top, bottom |
above or below the text, in one column |
center |
the picture of the box: the label and the description take a band inside the box at the labelPosition edge, and the icon is centred in the rest |
- Size.
iconSizeis the side of the icon, from 8 to 128 (with or withoutpx), orauto. Beside the text it defaults to the theme’siconSize(18 in the technical themes). A box withoutsizegrows to hold a written number with its text, taller for a large icon beside it. A box with a writtensizekeeps it: a number is clamped to the room the text leaves, never cut, andautofills that room, half the padding from the edges. Without asize,autobeside the text is the theme’s size. - Centred. With
centerthe icon is as large as fits with the padding around it, or the writteniconSizewhen that is smaller. A node, decision, data store or box withoutsizestarts at 120 × 120 (at the 13 px label size, scaled with it), wider for a long label and larger for a writteniconSize. Decisions, data stores, and nodes and boxes drawn with a shape other thanrounded,rectangleorsquarelay the label and the icon out in the rectangle inscribed in their outline, and grow until it holds them. In a box with a writtensizethe icon takes the room the label leaves, with half the padding around it when the room is short, down to 8 px; asizewithout room for that islayout.text-overflow, whose hint gives a size that holds the icon and the label. - Label position.
labelPosition(top,bottomby default,leftorright) is the edge the label sits at when the icon is the centre piece: inside the box withiconPosition center, outside the icon withshape icon(free.md). Anywhere else it changes nothing, and the layout reportslayout.label-position-ignored(warning) on the property, with the hint to seticonPosition centerorshape icon; written in a rule or a class, only when none of the elements it styles has its icon as the centre piece, and from a theme’s rule or class once, on the diagram’stheme. On groups, tags, notes, free texts, icons, images and C4 entities, whose icon is never the centre piece, the hint is to remove it. - The icon as the shape.
shape icondraws a node or a box as its icon alone, with its label outside;iconPositiondoes not apply to it, andiconSizesets its square (free.md). - C4 entities place their icon
left,right,toporbottomof their text as nodes do, and drawcenterastop, since their kind decides their drawing. - Groups, tags and notes draw their icon before their text, at 1.3 times the text size, whatever
iconPositionandiconSizesay. The theme’siconPositiontakes the five positions too. - Icons are drawn from the built-in packs or the project’s packs, normalised in size; see assets.md.
Classes
Section titled “Classes”class <id> { <properties> } is declared at the top level of a diagram or inside a theme block.
Classes have their own namespace, cannot apply other classes, and may not be declared inside
groups. They are applied with class a, b in an element, group or relation block, or in a style
statement. A class name resolves to a class of the diagram, then of its theme or the theme’s bases
(most specific first), then to a built-in class:
| Class | Properties |
|---|---|
highlight |
stroke accent; width 2 |
critical |
stroke danger; width 2 |
proposed |
line dashed; stroke warning |
deprecated |
pattern hatch; stroke muted; color muted |
external |
line dashed; stroke muted |
A name that resolves to nothing is theme.unknown-class (warning), reported by project
compilation, which is where the whole theme chain is known; the class is then ignored.
rule <kind> { <properties> } styles every element of a kind (node, decision, data, group,
note, tag; in free diagrams box, text, icon, image, note, sticky, line, pen and
group) or every relation (relation). rule line and rule pen take heads too:
rule pen { arrowhead triangle } ends every pen in a triangle. rule line { line dashed } reads
oddly but is right: the kind, then the property. Rules are written in diagrams and in theme
blocks, where they reach every diagram that uses the theme; a project-wide rule therefore lives in
the project’s default theme. A property set twice for the same kind is prop.duplicate. Only
rule node and rule box may set shape; rule group { radius 0 } squares every group.
Precedence
Section titled “Precedence”Each property of an element or relation takes the last value set in this order:
- the theme’s tokens (the project’s default theme when the diagram has no
theme); - the theme’s rules, from the base theme to the most specific;
- the diagram’s rules;
- applied classes: those of the declaration block, then those of
stylestatements in source order; - explicit properties of
stylestatements.
Animation state, added by the motion engine, applies on top of the result.
Presets and canvas
Section titled “Presets and canvas”| Statement | Canvas | layout.too-large (warning) |
|---|---|---|
| (none) | the content plus margin, plus the frame |
never |
preset document |
as above | wider than 1,200 px |
preset slide |
as above, extended to 16:9 with the content centred | wider than 1,920 px or taller than 1,080 px |
preset board |
as above, whatever its size | never |
canvas (w, h) |
w × h with the content centred; grows where it must |
when the content does not fit |
preset and canvas cannot be combined, and another preset word is prop.invalid-value listing
document, slide, board. The frame strip spans the canvas at its top or bottom edge. Text is
never shrunk to fit a canvas: when the warning appears, split the diagram into views or story
scenes. preset board is for whiteboards, which grow with what is drawn on them: a kanban board
or an explanation drawn with pens and stickies (free.md). The warning of
preset document and preset slide says so in its hints.