Skip to content

Theme file

A theme is one JSON file that holds the complete visual system for diagrams: colors, fonts, strokes and sizes. diago embeds four: default (light), dark (dark neutral), midnight (dark vibrant) and sketch (hand-drawn). A spec picks one with its top-level theme field, and -theme on the command line overrides it; without either, the theme is default. Text art takes no theme.

A theme is self-contained: no inheritance, no imports, no overrides between themes. To make a custom one, copy a real file such as midnight.json below and edit it.

A theme name that is not built in is looked up as <name>.json in the directory named by the DIAGO_THEME_DIR environment variable. Built-in names always win, so a file called dark.json there never replaces the built-in dark.

Terminal window
mkdir -p ~/.config/diago-themes
sed 's/"midnight"/"ocean"/; s/"#0f0f14"/"#0b1d2a"/' midnight.json > ~/.config/diago-themes/ocean.json
DIAGO_THEME_DIR=~/.config/diago-themes diago render -theme ocean spec.json > out.svg

The file name is what chooses the theme: ocean.json is ocean, whatever its name field says. A theme name is a plain file name: a name with a path, such as ../ocean or a/b, is an error. The same name works in a spec’s "theme": "ocean", with DIAGO_THEME_DIR set. Without the directory, -theme ocean fails with error: render flow: theme "ocean" not found (exit 2), and a theme field that does not load is a validation error on theme (exit 1). The editor schemas list only the four built-in names for theme, so an editor may flag a custom one even though diago accepts it.

A theme that fails validation is an error that names the field, such as theme "bad2": field "node.font.family": font family "Arial" is not supported; must be one of: Inter, Pangolin. The rules:

  • style must be clean or sketch. The field is required: it selects the renderer, so sketch is what gives the hand-drawn look.
  • A font family must be Inter or Pangolin, the two faces embedded in diago. Text is measured from the embedded font files, so sizing is the same on every machine.
  • Every color must be #rrggbb or #rrggbbaa: no names, no empty values.
  • Every size and stroke width marked below as positive must be greater than 0.
  • colors must define all seven named colors.

Unknown keys are ignored without a word. A typo in a key name degrades the theme instead of failing, so check a new file by rendering with it.

Every section below is required. Sizes and widths are in pixels.

Field Type Notes
name string A label for the theme. Not used to find it.
description string Free text.
style string clean or sketch. Required.
background color The diagram background. Also decides whether colors derived from a color field are made for a light or a dark ground.
colors object The palette behind a spec’s named color values. Needs red, green, blue, yellow, orange, purple and gray, each a color.
diff object Optional. The diff palette, below.
Field Type Notes
fill color The box fill.
stroke color The box outline.
stroke_width number Positive.
corner_radius number The corner radius of rounded boxes.
font font The label font.
padding {"x", "y"} The blank between a node’s label and its box, each side. At least 0. Screen output only: text art ignores it. Class boxes keep their own padding.
Field Type Notes
stroke color The wire color.
stroke_width number Positive.
arrow_size number The arrowhead size. Positive.
label_font font The edge label font.
Field Type Notes
fill color The frame fill.
stroke color The frame outline.
stroke_width number Positive.
stroke_dash string The dash pattern of the outline, such as 6,3. Not validated.
label_font font The frame label font.
padding number The blank between a group’s content and its frame, on every side. At least 12, so a group’s own wires keep clear of its border. Screen output only: text art ignores it.

The sequence diagram’s header boxes and lifelines.

Field Type Notes
fill color The header box fill.
stroke color The header box outline.
stroke_width number Positive.
font font The header label font.

The sequence diagram’s alt, opt, loop, par and break boxes.

Field Type Notes
fill color The box background. A translucent value such as #rrggbbaa lets the lifelines show through.
stroke color The box outline.
stroke_width number Positive.
stroke_dash string The dash pattern of the outline. Not validated.
label_font font The font of the type tag and the section guards.

The activation bars on sequence lifelines.

Field Type Notes
fill color The bar fill.
stroke color The bar outline.
stroke_width number Positive.
width number The bar width in px. Positive. Screen output only: text art draws its own bar.
Field Type Notes
member_font font The font of the attribute and method lines.
separator_width number The width of the lines between a class box’s compartments. Positive. Their color is the box’s stroke.

Wherever a table says font, the value is an object:

Field Type Notes
family string Inter or Pangolin.
size number Positive.
weight integer The font weight, such as 400 or 600.
color color The text color.

An optional block that sets the palette of diago diff: the stroke and text color of added, changed and removed elements. A key it leaves out falls back to the palette, per key:

Field Falls back to
added colors.green
changed colors.blue
removed colors.red

A theme without a diff block therefore still renders diffs: delete the block from a theme and its diffs use its green, blue and red. Each of the three must be a color.

themes/midnight.json
{
"name": "midnight",
"description": "Dark theme",
"style": "clean",
"background": "#0f0f14",
"colors": {
"red": "#ff6b6b",
"green": "#51cf66",
"blue": "#74c0fc",
"yellow": "#ffd43b",
"orange": "#ff922b",
"purple": "#cc5de8",
"gray": "#9090b0"
},
"diff": { "added": "#51cf66", "changed": "#74c0fc", "removed": "#ff6b6b" },
"node": {
"fill": "#1a1a2e",
"stroke": "#3a3a5c",
"stroke_width": 1.5,
"corner_radius": 8,
"font": { "family": "Inter", "size": 14, "weight": 500, "color": "#e0e0e8" },
"padding": { "x": 20, "y": 12 }
},
"edge": {
"stroke": "#5a5a7c",
"stroke_width": 1.5,
"arrow_size": 8,
"label_font": { "family": "Inter", "size": 12, "weight": 400, "color": "#a0a0b8" }
},
"group": {
"fill": "#12121e",
"stroke": "#2a2a4c",
"stroke_width": 1.5,
"stroke_dash": "6,3",
"label_font": { "family": "Inter", "size": 12, "weight": 600, "color": "#8080a0" },
"padding": 12
},
"actor": {
"fill": "#1a1a2e",
"stroke": "#3a3a5c",
"stroke_width": 1.5,
"font": { "family": "Inter", "size": 14, "weight": 600, "color": "#e0e0e8" }
},
"fragment": {
"fill": "#14141f",
"stroke": "#2a2a4c",
"stroke_width": 1.5,
"stroke_dash": "6,3",
"label_font": { "family": "Inter", "size": 11, "weight": 700, "color": "#a0a0b8" }
},
"activation": {
"fill": "#22223a",
"stroke": "#3a3a5c",
"stroke_width": 1.0,
"width": 10
},
"class": {
"member_font": { "family": "Inter", "size": 12, "weight": 400, "color": "#e0e0e8" },
"separator_width": 1.0
}
}