Styling nodes and graphs

July 12, 2026 · View on GitHub

jssm diagrams are styled in layers. At the bottom sits a base stylesheet; over that you can select one or more named themes; over that, state-kind defaults (start, end, terminal, hooked) apply; and at the top, per-state declarations always win. This page covers the named themes — what ships, and how to pick one on each rendering surface.

The web-component chrome (panel backgrounds, toolbars, JSON inspectors) has its own separate palette system, covered {@page WebComponents.md on the web components page} and summarized at the bottom of this one.

 

 

The built-in themes

Five diagram themes ship with the library:

ThemeLook
defaultWhite rectangles, black text; crimson terminals, yellow starts, dodgerblue4 active state
modernKhaki rectangles with the same saturated kind colors
oceanSea colors — cadetblue states, deepskyblue active, darkviolet terminals, aquamarine accents
plainBox-free plaintext: transparent backgrounds, black text; good for print and monochrome contexts
boldCurrently shares modern's palette; reserved as a separate name so the two can diverge

Every theme styles each state kind — ordinary states, start, end, terminal, and hooked states — plus the active variant of each, so the current state stands out while the machine runs.

You can enumerate the available names at runtime:

sm`a -> b;`.all_themes();   // [ 'default', 'modern', 'ocean', 'plain', 'bold' ]

 

 

Picking a theme in the machine source

The primary mechanism is the theme: header directive, written in the FSL source itself:

theme: ocean;

Red => Green => Yellow => Red;

Because the selection rides inside the machine source, it applies everywhere that source is rendered — the library API, the CLI, Markdown fences, and web components all honor it with no further configuration.

Unknown theme names are rejected at parse time, so a typo fails loudly rather than silently rendering the default.

Layering several themes

theme: also accepts a bracketed list:

theme: [ocean modern];

a -> b;

The first-declared theme is dominant: where two themes style the same property, the earlier one wins, and later ones only fill the gaps.

 

 

Picking a theme from code

Machines expose their theme selection as a property, so you can also set it after construction:

import { sm } from 'jssm';

const machine = sm`a -> b;`;

machine.themes;               // [ 'default' ]
machine.themes = 'ocean';     // switch themes at runtime
machine.themes = ['ocean', 'plain'];   // or layer, first-dominant

machine.style_for('a');       // the fully resolved style object for a state

style_for(state) is where the layering resolves: base stylesheet, then the selected themes, then kind defaults, then any per-state declaration from the source. A per-state declaration therefore always overrides whatever the theme says for that state.

 

 

Themes in Markdown fences

The FSL Markdown fence convention renders fsl/jssm fenced code blocks as live diagrams. A fence body is ordinary machine source, so the same header directive works:

```fsl
theme: ocean;
Red => Green => Yellow => Red;
```

The fence's syntax-highlighted code listing picks up the diagram's node colors automatically — each state name in the code is tinted with the color that state renders with — so a themed fence themes both halves at once.

There is not yet a fence-info token for themes (something like theme=ocean after the language word); the header directive inside the body is the supported spelling.

 

 

Themes in the CLI

fsl-render renders .fsl files (or stdin) to svg, dot, png, jpeg, html, or gif. It reads whole machine sources, so — again — the header directive is the selection mechanism:

// traffic.fsl
theme: ocean;
Red => Green => Yellow => Red;
fsl-render traffic.fsl --target png

There is not yet a --theme command-line flag; put the directive in the file.

 

 

Themes in web components

The rendered diagram inside <fsl-viz> or <fsl-instance> uses the same pipeline as everything else, so the machine source decides its theme:

<fsl-viz fsl="theme: ocean; Off -> On -> Off;"></fsl-viz>

Separately, <fsl-instance> themes its own chrome — panels, toolbar, JSON inspector — through a named light/dark palette registry. Default and Solarized ship built in; hosts can add their own, and every registered name appears in the toolbar's theme pulldown:

<fsl-instance theme="system" theme-name="Solarized">
  Off -> On -> Off;
</fsl-instance>

theme here is the light/dark mode (system follows the OS preference); theme-name picks the palette. The full registry API is documented {@page WebComponents.md on the web components page}.

 

 

Not yet

  • A fence-info theme= token and a CLI --theme flag (the header directive covers both surfaces today).
  • Render-time theme overrides as an alternative to compile-time merging.
  • A public registration API for user-defined diagram themes (the web-component chrome registry is already user-extensible).