Typography
This chapter defines the typographic system for MarkSpec rendered output (PDF
documents, HTML books, slide decks). All values are defaults — authors may
override them in project.yaml.
Foundations
Fonts
IBM Plex is chosen for its technical clarity, broad Unicode coverage, and open license. It ships in Sans, Mono, and Serif variants — covering all document roles without mixing font families.
Figure: IBM Plex font roles
The font family is IBM Plex — a typeface designed for technical documentation with broad Unicode coverage, multiple weights, and native support for tabular figures.
| Role | Family | Weight | Usage |
|---|---|---|---|
| Body text | IBM Plex Sans | Regular (400) | Paragraphs, list items, captions |
| Body emphasis | IBM Plex Sans | Italic (400) | Emphasized text |
| Body strong | IBM Plex Sans | SemiBold (600) | Strong text, headings |
| Monospace | IBM Plex Mono | Regular (400) | Code blocks, inline code, entry IDs |
| Long prose | IBM Plex Serif | Regular (400) | Optional — for narrative-heavy documents |
Only Sans and Mono are required. Serif is available for projects that prefer it for body text.
Font fallback chain
IBM Plex is open-source (OFL) and bundled in CI. For contexts where Plex is not installed (local preview, SVGs viewed on GitHub), use this fallback chain:
| Role | Fallback chain |
|---|---|
| Sans | "IBM Plex Sans", "Segoe UI", "Helvetica Neue", "DejaVu Sans", sans-serif |
| Mono | "IBM Plex Mono", "Cascadia Mono", "SF Mono", "DejaVu Sans Mono", monospace |
| Serif | "IBM Plex Serif", "Georgia", "DejaVu Serif", serif |
The chain picks the best match per platform: Segoe UI on Windows, Helvetica Neue
on macOS, DejaVu Sans on Linux. The final generic keyword (sans-serif,
monospace, serif) ensures a last resort on any system.
PlantUML only accepts a single font name — no fallback chain. Use IBM Plex Sans and ensure the font is installed on the build machine. When Plex is not available, PlantUML falls back to its built-in SansSerif.
Type scale
The minor third ratio (1.2) produces a compact scale that works for technical documents where space is at a premium. Larger ratios (e.g., major third 1.25) waste vertical space; smaller ratios lack visual hierarchy.
Figure: Type scale hierarchy
Sizes follow a minor third ratio (1.2) rounded to half-points. The base size is 10pt for A4 documents.
| Element | Size | Weight | Leading |
|---|---|---|---|
| H1 | 20pt | SemiBold | 24pt |
| H2 | 16.5pt | SemiBold | 20pt |
| H3 | 14pt | SemiBold | 17pt |
| H4 | 12pt | SemiBold | 15pt |
| Body | 10pt | Regular | 14pt |
| Small / caption | 8.5pt | Regular | 12pt |
| Code block | 9pt | Regular (Mono) | 13pt |
| Inline code | 9pt | Regular (Mono) | — |
| Footer / header | 8pt | Regular | 10pt |
Spacing
A 4pt base unit keeps spacing consistent and proportional. All values are multiples of 4 — no magic numbers, easy to reason about.
Figure: Spacing scale applied to document structure
A 4pt base unit defines the spacing scale. All vertical and horizontal spacing derives from multiples of this unit.
| Token | Value | Usage |
|---|---|---|
space-1 | 4pt | Inline padding, tight gaps |
space-2 | 8pt | Between list items, cell padding |
space-3 | 12pt | Between paragraphs within a section |
space-4 | 16pt | Between sections (below headings) |
space-6 | 24pt | Between major sections (below H2) |
space-8 | 32pt | Page-level separation |
Color palette
Document palette (default)
Monochrome with one accent ensures B&W printability — critical for auditor handoffs and archived compliance documentation.
Figure: Document palette swatches
The document palette is monochrome with a single accent color. All content remains legible in greyscale print and B&W photocopy.
| Token | Hex | Usage |
|---|---|---|
text | #1a1a1a | Body text, headings |
secondary | #6b6b6b | Captions, metadata, footer |
muted | #999999 | Disabled, placeholder |
accent | #0072B2 | Links, cross-references, active elements |
accent-dark | #005580 | Visited links, hover state |
bg-code | #f5f5f5 | Code block background |
bg-alert | #f0f4f8 | Alert/admonition background |
border | #d4d4d4 | Table rules, dividers, code block border |
white | #ffffff | Page background |
Diagram palette (categorical)
The Paul Tol qualitative palettes are designed for scientific publishing and tested under protanopia, deuteranopia, and tritanopia. Two sub-palettes are used, selected by output target:
- Screen / HTML (default, ~90% of usage) — Tol vibrant: more saturated, designed for data visualization on screens.
- Print / PDF — Tol bright: softer tones, designed for print documents.
Figure: Paul Tol categorical palettes — vibrant (screen) and bright (print)
When a diagram requires categorical distinction (e.g., component types, data flows, requirement layers), use the appropriate Tol palette. All colors are distinguishable under protanopia, deuteranopia, and tritanopia.
| Name | Screen (Tol vibrant) | Print (Tol bright) |
|---|---|---|
| Blue | #0077BB | #4477AA |
| Cyan | #33BBEE | #66CCEE |
| Teal | #009988 | #228833 |
| Orange | #EE7733 | #CCBB44 |
| Red | #CC3311 | #EE6677 |
| Magenta | #EE3377 | #AA3377 |
| Grey | #BBBBBB | #BBBBBB |
Note: Tol vibrant has no yellow — orange (#EE7733) is the closest equivalent.
Tol bright has no magenta — purple (#AA3377) is used instead.
When categorical distinction is not needed, diagrams use the document palette (monochrome + accent).
Alert colors
Figure: Alert rendering — screen (full border + tint, Tol vibrant) vs print (full border only, Tol bright)
Alerts use a full rectangular border on all sides, distinguishing them from entry blocks (which use a left border only). Screen adds a light background tint; print uses an outline-only style to remain legible in greyscale and B&W photocopy.
| Alert | Screen border (Tol vibrant) | Screen bg | Print border (Tol bright) |
|---|---|---|---|
| NOTE | #0077BB (blue) | #eef5fc | #4477AA (blue) |
| TIP | #009988 (teal) | #eef7f5 | #228833 (green) |
| IMPORTANT | #EE3377 (magenta) | #fdeef5 | #AA3377 (purple) |
| WARNING | #EE7733 (orange) | #fdf4ee | #CCBB44 (yellow) |
| CAUTION | #CC3311 (vermillion) | #fdeeed | #EE6677 (red) |
Entry type colors
Entry blocks are colored by type (the nature of the artifact), not by layer. The layer is already encoded in the ID prefix and does not need redundant color signaling.
Two Paul Tol sub-palettes are used, selected by output target:
| Type | Prefixes | Print (Tol bright) | Screen (Tol vibrant) |
|---|---|---|---|
| req | STK, SYS, SWE, SRS | Blue #4477AA | Blue #0077BB |
| spec | ARC, SAD, ICD | Green #228833 | Teal #009988 |
| test | TST, VAL, SIT, SWT | Red #EE6677 | Orange #EE7733 |
- Print theme (Tol bright): default for PDF output. Softer tones, designed by Paul Tol for documents.
- Screen theme (Tol vibrant): default for HTML output. More saturated, designed by Tol for data visualization / dashboards.
Both palettes are colorblind-safe by design (tested for protanopia, deuteranopia, tritanopia). No red-green confusion at the chosen mappings.
The print palette uses three colors from the Paul Tol bright qualitative scheme (7 colors). The screen palette uses three colors from the Paul Tol vibrant qualitative scheme (7 colors). Both are single-scheme picks — no cross-scheme mixing.
The type color is applied to:
- The 2px left border of the entry block.
- The display ID text on the title line.
No other element uses the type color. Body text, metadata, and pills use the document’s standard text/background colors.
Design tokens
The entries: section of theme/tokens.yaml is the canonical source:
entries:
req: { print: "#4477AA", screen: "#0077BB" }
spec: { print: "#228833", screen: "#009988" }
test: { print: "#EE6677", screen: "#EE7733" }
Running just tokens regenerates all downstream files from this source:
| Output file | Token names emitted |
|---|---|
themes/light.typ (Typst) | entry-req, entry-spec, entry-test |
themes/dark.typ (Typst) | entry-req, entry-spec, entry-test |
theme/markspec.css | --ms-entry-req, --ms-entry-spec, --ms-entry-test |
Palette selection by output target:
- PDF output — uses the Typst light theme (
themes/light.typ), which takes theprintvalue (Tol bright) for each type. - HTML output — uses the CSS
--ms-entry-*custom properties, which always take thescreenvalue (Tol vibrant). - Dark PDF theme (
themes/dark.typ) takes thescreenvalue (Tol vibrant).
The selection is implemented in scripts/gen_theme.ts as
palette = name === "light" ? "print" : "screen". Edit tokens.yaml and run
just tokens to change any color — never edit theme files directly.
Document elements
Links
Figure: Resolved references vs inline code
| Context | Font | Color | Underline | Clickable |
|---|---|---|---|---|
[text](url) in prose | Surrounding (Sans) | accent | PDF: always, HTML: on hover | Yes |
<url> autolink | Mono | accent | PDF: always, HTML: on hover | Yes |
`code` inline | Mono | text | None | No |
{{ref.ID}} resolved | Sans | accent | PDF: always, HTML: on hover | Yes |
| URL inside code fence | Mono | text | None | No |
| Footnote marker | Sans, superscript | accent | None | Yes |
Mustache references ({{namespace.id}}) are resolved by tooling before
rendering. In the output they appear as regular cross-reference links, not as
code. URLs and code literals inside fenced code blocks are never linked — code
blocks are inert.
Tables
Figure: Table rendering with caption, header, and borders
| Property | Value |
|---|---|
| Header background | #f5f5f5 |
| Header weight | SemiBold |
| Cell padding | 4pt vertical, 8pt horizontal |
| Border | #d4d4d4, 0.5pt solid |
| Alternating rows | None (clean default) |
| Caption position | Above table, italic, text color |
Code blocks
Figure: Monochrome syntax highlighting
| Property | Value |
|---|---|
| Font | IBM Plex Mono, 9pt |
| Background | #f5f5f5 |
| Border | #d4d4d4, 0.5pt solid |
| Border radius | 3pt |
| Padding | 12pt |
| Line numbers | Off by default |
| Syntax highlighting | Monochrome — keywords bold, strings italic, comments secondary |
Syntax highlighting uses weight and style variation rather than color to remain legible in B&W.
Captions
| Property | Value |
|---|---|
| Font | IBM Plex Sans, 8.5pt, italic |
| Color | secondary (#6b6b6b) |
| Table caption | Above the table |
| Figure caption | Below the image |
| Spacing | space-2 (8pt) between caption and content |
Entry blocks
Entry blocks use admonition-style rendering: a 2px colored left border provides visual distinction from surrounding prose, with no background tint or horizontal rules.
Figure: Entry block anatomy — admonition-style rendering
Block layout
Each entry block is laid out top-to-bottom:
- Title line — display ID (body size, weight 500, type color) + title text (body size, weight 500, primary text color) + pill group (inline, wraps as block).
- Body — body size, regular weight, primary text color. Margin-top:
space-1(4pt) from title line. - Metadata line — small size, italic, secondary text color. Margin-top:
space-2(8pt) from body. Attributes separated by·(middle dot with spaces). Attribute order:Id, then traceability (Satisfies/Verifies/Derived-from), then any other key-value attributes.
The 2px left border starts flush with the first line of the title — no top
padding above the title. Inter-block margin is space-3 (12pt).
Label pills
Labels (e.g., ASIL-B, performance, real-time) are rendered as pills
(rounded badges):
- Font size: small (8.5pt), weight 500.
- Padding: 1pt vertical, 7pt horizontal.
- Border-radius: 9pt (fully rounded).
- Background: code background color (neutral — no type color).
- Text color: secondary text color.
- Each pill has
white-space: nowrap(a single label never breaks).
All pills for an entry are wrapped in a pill group:
- Inline flex container with
gap: space-1(4pt) andflex-wrap: wrap. - The pill group sits on the title line after the title text.
- If the group fits on the title line, it renders inline.
- If it doesn’t fit, the entire group wraps to the next line as a block.
- No labels = no pill group rendered.
Cross-reference links
Attribute values that reference other entries (Satisfies, Verifies,
Derived-from) are rendered as links:
- Dashed underline — underline color is lighter than the text (border secondary or equivalent, ~0.3 alpha).
- No link color — the text stays in the same italic/secondary style as the rest of the metadata line.
- Underline offset: 2pt.
- In HTML:
cursor: pointer, link target is the anchor of the referenced entry. - In PDF: dashed-underline style only. Typst internal link navigation
(
#link(<target>)) is deferred untilreq-blockemits labeled anchors (see issue #181).
Non-reference values (Id value, other key-value pairs) have no underline —
they are plain italic text.
PDF rendering (Typst)
Entry blocks in PDF output are rendered by req-block() in
packages/markspec-typst/entry.typ. The block geometry matches the spec above:
- Border:
stroke: (left: 2pt + color)— left side only. - Inset:
left: 12pt, top: 0pt, bottom: 4pt, right: 0pt.
The function signature:
#req-block(
type: "req", // "req" | "spec" | "test"
display-id: "…",
title: "…",
body: render("…"), // entry body rendered via cmarker
attrs: (("Key", "Value"),), // (key, value) pairs; trailing comma required
labels: ("ASIL-B",), // label strings; trailing comma required
theme: theme,
)
The trailing comma on single-element arrays is required: (expr) is a
parenthesised expression in Typst, not a 1-element array.
The body argument receives already-rendered Typst content: render("…") calls
cmarker’s render to convert the entry’s Markdown body to Typst. The pipeline
that builds this call lives in packages/markspec/render/typst/template.ts
(renderEntryTypst).
Document layout
Page layout
A4 with 25 mm margins gives a 160 mm text width — comfortable for 10pt body text at roughly 80 characters per line, matching the Markdown source line width.
Figure: A4 page layout with margins, header, and footer
Default page size is A4 (210 × 297 mm), portrait orientation.
| Property | Value |
|---|---|
| Page size | A4 (210 × 297 mm) |
| Top margin | 25 mm |
| Bottom margin | 25 mm |
| Left margin | 25 mm |
| Right margin | 25 mm |
| Header | Document title (left), page number (right) |
| Footer | Project name and version (left), date (right) |
| Column count | 1 |
| Paragraph spacing | 6pt after |
| Paragraph indent | None (block style) |
Cover page
Figure: Cover page layout
The cover page is generated from project.yaml metadata. Layout:
| Element | Position | Style |
|---|---|---|
| Project name | Upper third, centered | 28pt, SemiBold |
| Document title | Center, centered | 20pt, SemiBold |
| Version | Below title | 12pt, Regular, secondary |
| Date | Below version | 10pt, Regular, secondary |
| Classification | Bottom third, centered | 10pt, SemiBold, accent |
The cover page has no header or footer.
Diagrams
Format and embedding
Diagrams are stored as SVG files alongside the documents that reference them. Embedded using standard Markdown image syntax with relative paths:

Always set the viewBox attribute. Omit fixed width/height — let the
container control display size. The same SVG works across PDF, HTML, and slide
output.
Set font-family on the root <svg> or on individual <text> elements using
the Sans fallback chain from the Fonts section:
Figure: SVG component with font fallback chain
SVG sizing for documents (A4, ~25 mm margins)
| Type | Ratio | viewBox (w × h) | Use |
|---|---|---|---|
| Full width | 16:9 | 700 × 400 | Architecture overviews, flow diagrams |
| Full width tall | 4:3 | 700 × 525 | Detailed system diagrams |
| Full width square | 1:1 | 700 × 700 | State machines, class diagrams |
| Half width | 4:3 | 340 × 250 | Inline diagrams, small illustrations |
| Full page | 3:4 | 700 × 900 | Complex diagrams needing a full page |
Visual style
Figure: Monochrome architecture diagram (document palette)
Figure: Categorical component diagram (Tol palette)
Guidelines:
- Monochrome preferred. Black, white, and shades of grey. Diagrams must be readable when printed in greyscale — color is decorative, not structural.
- Categorical color. When distinction is needed (component types, data flows), use the Tol palette. Otherwise use the document palette.
- High contrast. Black strokes on white background. Avoid light grey lines or low-contrast fills.
- Stroke weight. 1.5–2px for primary lines, 1px for secondary. No hairlines (< 1px).
- Text size. Minimum 12px for labels, 14px for titles.
- Font. Use the Sans fallback chain from the Fonts section.
- Whitespace. Generous padding between elements.
Tooling
Any tool that produces clean SVG (draw.io, Excalidraw, Inkscape, D2, Graphviz). The SVG output is what matters, not the authoring tool.
PlantUML is recommended for sequence and state machine diagrams — textual,
diffable source that lives in the repository. Source files use .puml
extension, generated SVG uses .plantuml.svg suffix.
PlantUML presets
Key sizing settings:
skinparam svgDimensionStyle false— removes fixedwidth/heightfrom the SVG, enabling proper scaling viaviewBox.scale— controls overall diagram size. Usescale 1for half-width diagrams,scale 2for full-width.skinparam ranksep/nodesep— vertical and horizontal spacing between elements. Scale proportionally withscale.
Monochrome (default) — uses the document palette:
Figure: PlantUML sequence diagram with monochrome preset
@startuml
skinparam svgDimensionStyle false
skinparam shadowing false
scale 2
skinparam ranksep 30
skinparam nodesep 35
skinparam defaultFontName "IBM Plex Sans"
skinparam defaultFontSize 12
skinparam backgroundColor #ffffff
skinparam ArrowColor #1a1a1a
skinparam BorderColor #1a1a1a
skinparam ParticipantBackgroundColor #f5f5f5
skinparam ParticipantBorderColor #d4d4d4
skinparam NoteBackgroundColor #f5f5f5
skinparam NoteBorderColor #d4d4d4
skinparam SequenceLifeLineBorderColor #6b6b6b
@enduml
Categorical — Tol vibrant palette for multi-category diagrams:
Figure: PlantUML component diagram with Tol categorical preset
@startuml
skinparam svgDimensionStyle false
skinparam shadowing false
scale 2
skinparam ranksep 30
skinparam nodesep 35
skinparam defaultFontName "IBM Plex Sans"
skinparam defaultFontSize 12
skinparam backgroundColor #ffffff
skinparam ArrowColor #1a1a1a
' Tol vibrant palette (screen default)
skinparam component {
BackgroundColor<<req>> #0077BB
BackgroundColor<<arch>> #33BBEE
BackgroundColor<<test>> #009988
BackgroundColor<<ext>> #EE7733
FontColor<<req>> #ffffff
FontColor<<arch>> #1a1a1a
FontColor<<test>> #ffffff
FontColor<<ext>> #ffffff
BorderColor #1a1a1a
}
@enduml
Slide decks
Figure: Title slide — light theme
Figure: Content slide — light theme
Figure: Title slide — dark theme
Figure: Content slide — dark theme
Slide decks use 16:9 aspect ratio (1920 × 1080 logical units), optimized for projectors, wide monitors, and video conferencing. Both light and dark themes are supported.
| Property | Value |
|---|---|
| Aspect ratio | 16:9 (1920 × 1080) |
| Margins | 60px top/bottom, 80px sides |
| Title slide font | 48pt, SemiBold |
| Slide heading (H2) | 36pt, SemiBold |
| Body text | 24pt, Regular |
| Code blocks | 20pt, Mono |
| Footer | Slide number (right), 14pt |
SVG sizing for slides (16:9)
| Type | Ratio | viewBox (w × h) | Use |
|---|---|---|---|
| Full slide | 16:9 | 1600 × 900 | Full bleed diagram |
| Content area | 16:9 | 1400 × 780 | With title and margins |
| Half slide | 9:10 | 700 × 780 | Diagram + text side by side |
| Quarter | 16:9 | 700 × 390 | Small inline diagram |
Themes
MarkSpec supports two themes for HTML books, web output, and slide decks. PDF documents always use the light theme for print consistency.
Light theme (default)
| Token | Hex |
|---|---|
background | #ffffff |
text | #1a1a1a |
secondary | #6b6b6b |
accent | #0072B2 |
bg-code | #f5f5f5 |
border | #d4d4d4 |
Dark theme
| Token | Hex |
|---|---|
background | #1a1a1a |
text | #e4e4e4 |
secondary | #999999 |
accent | #56b4e9 |
bg-code | #2a2a2a |
border | #404040 |
Figure: Light theme
Figure: Dark theme
The dark accent color (#56b4e9) is the Tol bright blue — colorblind-safe and
high contrast against dark backgrounds. Theme preference is detected from the
user’s system setting (prefers-color-scheme) for HTML/web output, or set
explicitly in project.yaml.
CSS custom properties
:root {
--ms-bg: #ffffff;
--ms-text: #1a1a1a;
--ms-secondary: #6b6b6b;
--ms-muted: #999999;
--ms-accent: #0072b2;
--ms-accent-dark: #005580;
--ms-bg-code: #f5f5f5;
--ms-bg-alert: #f0f4f8;
--ms-border: #d4d4d4;
}
@media (prefers-color-scheme: dark) {
:root {
--ms-bg: #1a1a1a;
--ms-text: #e4e4e4;
--ms-secondary: #999999;
--ms-muted: #666666;
--ms-accent: #56b4e9;
--ms-accent-dark: #3a8fbf;
--ms-bg-code: #2a2a2a;
--ms-bg-alert: #242424;
--ms-border: #404040;
}
}
Specimen
Figure: Specimen page — all typographic elements on a single page
References
- IBM Plex typeface — font family, specs, and downloads (OFL license)
- Paul Tol’s colour schemes — colorblind-safe palettes for scientific visualization
- Butterick’s Practical Typography — type scale, line length, and spacing principles
- Typographic scale calculator — interactive minor third (1.200) scale reference
- WCAG 2.2 contrast guidelines — minimum contrast ratios for text accessibility
- prefers-color-scheme — CSS media query for dark theme detection