Author a custom style
This guide shows you how to build your own style when none of the nine
built-ins is quite right. A style is the public BeckStyle record, so you
compose one by deriving from the closest built-in with a with expression, then registering it so a
document's meta.style can name it. You do not build a BeckStyle from scratch — every field is
required, and a built-in already fills them all.
Derive from the closest built-in
Start from the built-in that is nearest to what you want and change only the fields that differ.
Reach into BeckStyles.ByName for the base, and give your style a name that matches [a-z0-9-]+
(non-empty, lowercase letters, digits, hyphens):
using Beck;
BeckStyle ocean = BeckStyles.ByName["minimal"] with
{
Name = "ocean",
Mix = BeckStyles.ByName["minimal"].Mix with { NodeStroke = 48 },
};
Sub-records are records too, so nest with expressions to change one field without restating the
rest. Pick the base by what you are keeping: minimal for a flat token-only look, glow for its
motion character, or an artwork style (sketch, circuit, …) when you want its chrome.
BeckStyle.Classic is the safe baseline if you want to change one thing about the default.
Register it and render
A style reaches a diagram one of two ways. To make it the site-wide default, pass it as
SvgRenderOptions.Style — every document then renders in it unless its own meta.style says
otherwise:
using Beck.Rendering;
string svg = BeckSvg.Render(yaml, new SvgRenderOptions { Style = ocean });
To let individual documents opt in by name, put it in the Styles registry and select it from
YAML with meta.style:
var options = new SvgRenderOptions
{
Styles = new Dictionary<string, BeckStyle> { ["ocean"] = ocean },
};
meta: { style: ocean }
The registry key and the token in meta.style must match. Built-in names are resolved first, so a
custom style cannot reuse a built-in name — if you name yours sketch, the built-in wins and yours is
never reached. An unknown token warns and falls back to Style (or classic). For the exact
resolution order and precedence, see the style-system reference.
Retint the colours
Colours live in LightTokens and DarkTokens as an ordered --beck-* table. Keep every value a
three-tier var(--color-…, literal) chain: point the role at a host --color-* ramp first, with a
literal only as the last-resort fallback. This is what lets your style still adopt the host palette
and flip with light and dark — never write a resolved hex into a token.
LightTokens = new StyleTokens(new (string, string)[]
{
// …carry the other entries from the base style…
("--beck-accent", "var(--color-sky-500, #0ea5c4)"),
("--beck-packet", "var(--beck-accent)"),
}),
Change only the entries you mean to; the easiest path is to copy the base style's array and edit the handful of roles you care about. The dark table need only hold the entries that differ. For the token list and what each paints, see the theme tokens.
Choose a metrics font
If you change Typography.SansFamily, set Typography.MetricsFont to the embedded table that
matches it, so the built-in measurer sizes cards correctly with no font dependency. The choices are
Inter, SourceSerif, Archivo, and ShantellSans; mono roles always use IBM Plex Mono. If you
run an exact SvgRenderOptions.Measurer (Skia over your real font files), it overrides this key and
the metrics table is irrelevant.
Typography = BeckStyle.Classic.Typography with
{
SansFamily = "'Source Serif 4', Georgia, serif",
MetricsFont = MetricsFont.SourceSerif,
},
Either way the SVG only names fonts through --beck-font/--beck-font-mono; the host page loads
the actual webfonts, and a missing font degrades to a textLength squeeze rather than a broken
layout.
Compose an artwork
Chrome that cannot be expressed in CSS — the brutalist shadow, sketch wobble, extrude faces, circuit
pins, transit station dots, blueprint dimension lines — comes from the Artwork field plus its
StyleGeometry offset. You compose an existing one; you cannot supply new geometry. Set both the
enum value and the offset that gates it (a 0 offset draws nothing):
BeckStyle flatSlab = BeckStyles.ByName["minimal"] with
{
Name = "flat-slab",
Artwork = StyleArtwork.Extruded,
Geometry = BeckStyles.ByName["minimal"].Geometry with { DepthOffset = 7 },
};
Each artwork reads its own offset — Brutalist/ShadowOffset, Extruded/DepthOffset,
Circuit/PinLength, Metro/StationRadius, Blueprint/DimensionTick. Sketch needs only the
enum. The reference lists every pairing.
What you cannot do
There is no seam for injecting raw markup or arbitrary CSS. A style is data: token strings, numbers,
dash patterns, and a choice from a fixed artwork vocabulary. Because a custom style may come from
less-trusted config, every CSS-bound string on it is scrubbed of </, <!, @import, url(, {,
and } before it reaches the <style> block, so a token value must not rely on those characters.
This is deliberate — it is what keeps output deterministic and free of injection. If you need chrome
the vocabulary does not cover, the answer is a new built-in in the engine, not a token hack. For the
full sanitizer rules see the reference.
For the complete field list of BeckStyle and its sub-records, the built-in registry, and the
resolution rules, see the style-system reference. For a visual tour of the
built-ins, see Pick a built-in style.