Back to invoice templates

Branding

Logo, brand colors and typography in BlockTemplate. Instead of repeating color values and font sizes in every block, you define design tokens once in page and reference them — changing a single value updates the whole template.

Color and typography tokens flowing into an invoice

Logo

image block with src (URL, Base64 or placeholder). fit: [width, height] scales proportionally into the box.

Colors

colorScheme defines named tokens. Blocks reference them via { token: '…' } — one value updates every usage.

Fonts

defaultFont sets the default family. fontSizes tokens keep the typography scale consistent (body, heading, small, total).

Embedding a logo

As an image block with URL, Base64 data URI or placeholder. fit scales proportionally into a target box without distorting the aspect ratio.

json
1{
2 "type": "image",
3 "data": {
4 "src": "https://cdn.example.com/logo.png",
5 "fit": [120, 60],
6 "alignment": "right"
7 }
8}
9// Alternative: Base64 data URI for guaranteed offline rendering
10// "src": "data:image/png;base64,iVBORw0KGgoAAAANS..."

Defining design tokens

page.colorScheme and page.fontSizes hold named values. Names are free — semantic tokens like primaryColor or headingSize work well.

json
1{
2 "page": {
3 "size": "A4",
4 "margins": { "top": 40, "right": 40, "bottom": 40, "left": 40 },
5 "colorScheme": {
6 "primaryColor": "#4f46e5",
7 "accentColor": "#8b5cf6",
8 "textColor": "#111111",
9 "mutedColor": "#6b7280",
10 "lineColor": "#e5e7eb"
11 },
12 "fontSizes": {
13 "bodySize": 10,
14 "smallSize": 8,
15 "headingSize": 18,
16 "totalSize": 12
17 }
18 }
19}

Referencing tokens

Anywhere a block expects a color or size value, you can pass { token: '…' } instead.

json
1{
2 "type": "text",
3 "data": {
4 "content": "INVOICE {{invoiceNumber}}",
5 "fontSize": { "token": "headingSize" },
6 "color": { "token": "primaryColor" },
7 "bold": true
8 }
9}
10 
11{
12 "type": "table",
13 "data": {
14 "dataSource": "{{items}}",
15 "headerFillColor": { "token": "primaryColor" },
16 "headerTextColor": "#FFFFFF",
17 "fontSize": { "token": "bodySize" },
18 "columns": [ /* ... */ ]
19 }
20}

Named styles for recurring elements

The styles object bundles font, color and margin presets. Blocks reference them via the style property. Design tokens are allowed here too.

json
1{
2 "styles": {
3 "h1": { "fontSize": 22, "bold": true, "color": { "token": "primaryColor" } },
4 "h2": { "fontSize": 14, "bold": true, "margin": [0, 0, 0, 6] },
5 "muted": { "color": { "token": "mutedColor" } },
6 "total": { "fontSize": 14, "bold": true, "alignment": "right" }
7 }
8}
9 
10// Blocks reference styles by name:
11{ "type": "text", "data": { "content": "INVOICE", "style": "h1" } }

Header & footer

header and footer are optional and their defaults differ: header.repeatOnAllPages defaults to true, footer.repeatOnAllPages defaults to false. Set to false, the section moves into the content flow and therefore appears at the start (header) or at the end of the content (footer) — not on the last page. Both accept the same 17 block types as body.

json
1{
2 "header": {
3 "height": 80,
4 "repeatOnAllPages": true,
5 "blocks": [
6 {
7 "type": "columns",
8 "data": {
9 "columns": [
10 { "width": "*", "blocks": [
11 { "type": "text", "data": { "content": "{{seller.name}}", "style": "h2" } }
12 ]},
13 { "width": "auto", "blocks": [
14 { "type": "image", "data": { "src": "https://cdn.example.com/logo.png", "fit": [120, 60] } }
15 ]}
16 ]
17 }
18 }
19 ]
20 },
21 "footer": {
22 "repeatOnAllPages": true,
23 "blocks": [
24 { "type": "line", "data": { "thickness": 0.5, "color": { "token": "lineColor" } } },
25 { "type": "text", "data": {
26 "content": "{{seller.name}} • USt-IdNr. {{seller.vatId}} • {{seller.email?}}",
27 "fontSize": { "token": "smallSize" },
28 "color": { "token": "mutedColor" },
29 "alignment": "center"
30 }}
31 ]
32 }
33}