Skip to main content
Version: Canary 🚧

Theming API

warning

Unstable API

The theming API is currently in a foundational state and is considered highly unstable. It will be fully finalized and locked in an upcoming release. Proceed with caution when building highly custom visual layouts, as breaking changes to the rendering pipeline are expected.

The invoice-pro theming engine provides a Cascading approach to document styling. Themes control the visual layout, typographic choices, and structural positioning of the underlying Normalized data objects evaluated during the invoice compilation process.

Currently, the package ships with a blank theme for fully custom layouts and a standard DIN-5008 implementation.

The DIN-5008 Theme​

The DIN-5008 theme is the default implementation out-of-the-box. It constructs a business letter layout compliant with the German DIN 5008 standard, ensuring that recipient addresses align perfectly with standardized window envelopes.

info

By default, the theme is configured for Form A, but Form B can easily be selected via parameters.

Component Parameters​

KeyTypeDescription
formstrDetermines the letter form layout. Accepts "A" or "B". Defaults to "A".
fontstrThe primary font family applied to the document. Defaults to "Liberation Sans".
hole-markboolToggles the rendering of the punch hole indicator mark on the left margin. Defaults to true.
folding-marksboolToggles the rendering of folding marks on the left margin. Defaults to true.
color-row-oddcolor | noneBackground fill for odd line item rows (1st, 3rd, ...). Defaults to none.
color-row-evencolor | noneBackground fill for even line item rows (2nd, 4th, ...). Defaults to rgb("e2e8f0").
margindictionaryOverrides the default document margins. Defaults to (:).
info

The row fill is applied per line item, not per physical table row: description, discount, and subtotal rows always share the fill of the item they belong to, and page breaks do not shift the alternation. The table header and the totals block are not affected by these parameters.

Example Usage​

#import "@preview/invoice-pro:0.5.0": invoice, themes

// Initialize the invoice with a customized DIN-5008 theme
#show: invoice.with(
// We configure the theme function and pass it to the invoice
theme: themes.DIN-5008(
form: "B", // Form B pushes the address block further down
font: "Arial", // Replacing the default Liberation Sans
hole-mark: false, // Disabling the punch hole mark for digital-only PDFs
color-row-even: none // Disabling the default row striping
),
sender: (name: "Acme Corp"),
recipient: (name: "Jane Doe"),
)

// Document body goes here

The Blank Theme​

The blank theme is the absolute barebones architectural primitive provided by the engine. It acts as an unstyled, empty canvas that applies zero visual formatting out-of-the-box. It maps directly to the internal base layout structure.

tip

Document-Level Styling

Because the blank theme applies strictly no document-level theming, the output of the invoice function can be safely wrapped with standard native Typst styling rules. This allows you to define your page layout globally before initializing the invoice context.

Example Usage​

#import "@preview/invoice-pro:0.5.0": invoice, themes

// 1. Apply native Typst document-level formatting
#set page("a5", margin: (left: 0.5cm, right: 4cm))

// 2. Initialize the invoice with the blank theme
#show: invoice.with(
theme: themes.blank, // Bypasses internal layout styles
sender: (:),
recipient: (:),
)

// 3. Document body goes here

Data for Custom Layouts​

A theme consists of one layout function per component, which can be replaced with .with(..), e.g. themes.blank.with(bank-details: (ctx, view) => ..). The components prepare their data before they call the layout, so a layout only draws: it neither normalizes nor checks values. Like the rest of the theming API, these data are not stable yet.

Bank Details​

The bank-details layout, (ctx, view) => content, receives:

KeyDescription
view.sender.nameThe account holder: the name of bank-details, else the name of the payee (not on a credit note), else the sender name on one line (the recipient's on a credit note or a self-billed invoice).
view.sender.bankThe name of the bank, "" if not given.
view.sender.ibanThe IBAN without spaces and in upper case, "" if missing. The built-in layout prints it in groups of four.
view.sender.iban-validWhether the IBAN is valid. It is only false for an e-invoice with zugferd-errors: "report", otherwise an invalid IBAN stops the compilation.
view.sender.bicThe BIC without spaces and in upper case, "" if not given.
view.qr-code.size, view.qr-code.displayThe size of the QR code, and whether the bank details show one.
view.qr-code.payloadThe EPC-QR code to draw: beneficiary, iban, bic, amount, reference and text, the arguments of epc-qr-code from the sepay package. none if no code is shown (it is hidden, or the currency is not EUR) or cannot be generated.
view.qr-code.problemsWhy the EPC-QR code cannot be generated, as (short: .., message: ..). Only for an e-invoice with zugferd-errors: "report", otherwise the compilation stops before the layout is called. The built-in layout shows a placeholder that names them.
view.reference, view.textThe payment reference, as structured reference or as text (at most one of them is set).
view.show-referenceWhether to print the payment reference.
view.report-problemsWhether problems are shown in the document (an e-invoice with zugferd-errors: "report") instead of stopping the compilation.
view.payment-amountThe amount to pay.

Payment Means​

The payment-means layout, (ctx, view) => content, draws direct-debit, card-payment and paid. It receives:

KeyDescription
view.kind"direct-debit", "card" or "paid".
view.textThe sentence to print first, e.g. that the invoice was paid, or none.
view.detailsThe details to print, in order, each as (label: .., value: ..), e.g. the mandate reference or the last digits of the card number. An identifier has valid as well, which is only false for an e-invoice with zugferd-errors: "report".

The payment-goal layout prints the payment sentence of the language, ctx.locale.strings.payment.text (or text-due with prepayments). The component sets it to the sentence of the payment means of the invoice, followed by the notes of the cash discounts, so a custom layout that prints it states the payment means as well. Its view has payment-means, the kind of payment means the sentence is for ("transfer", "direct-debit", "card" or none), and discounts, the cash discounts with days, percent, basis and the printed note.

Exemption Notes​

The line-items layout, (ctx, data, body) => content, receives the legal notes below the line items that give the reason for an exemption (exemption grounds, reverse charge, the small business clause) as data.exemption-notes, in the order to print them. Each note has these keys:

KeyDescription
kind"small-business" for the clause of tax-exempt-small-biz, "grounds" for an exemption ground.
markerThe marker ("*", "**", ...) that links the note to the VAT line of its category or to its items, none if neither of them shows it.
bodyThe text of the note.

The VAT lines carry the same markers (marker of each entry of data.taxes), as do the items of a VAT category with several exemption grounds (tax.marker of each entry of data.items). With exemption notes, the built-in layout leaves out the standard tax statement (e.g. "All items are excl. 19% Tax.").

data.exemption-notes also lists the notes of the invoice (invoice(notes: ..), kind "note") after the exemption notes. The built-in layout prints all of them even when data.layout-information.show-global-information is false (line-items(show-information: false)), which only hides the information about what the items share: the law requires the exemption notes on the invoice, and the e-invoice states them and the notes (BT-120, BT-22). A custom layout should print them as well.

What the Theme Prints​

A theme says what its document layout prints of the invoice data besides the components as prints, a dictionary of flags that are all false unless set:

KeyDescription
referencesIt prints the reference signs, ctx.references.
party-extraIt prints the extra details of the sender and the recipient.
page-contentIt prints content of its own on every page, e.g. a footer, whose text invoice-pro cannot read. A header or footer of the theme sets it.

For a theme that prints the reference signs, the e-invoice checks that the printed invoice shows the seller's tax number or VAT identifier (IP-PRINT-03) and the date of the supply (IP-PERIOD-03), see Printed Details. The DIN-5008 theme prints the reference signs and the extra of the parties; the blank theme says nothing, so nothing is checked. A document layout of your own that prints the reference signs can say so:

#import "@preview/invoice-pro:0.5.0": invoice, themes

#show: invoice.with(
theme: themes.blank.with(
document: (ctx, body) => {
for (title, value) in ctx.references [#title: #value \ ]
body
},
prints: (references: true),
),
sender: (name: "Acme Corp"),
recipient: (name: "Jane Doe"),
)

A document layout of your own in place of the one of the DIN-5008 theme that does not print them sets prints: (references: false).