Skip to content

Template language 2.0 ​

The v2 template language compiles .folio files into executable PHP. It is explicit, declarative and whitespace-insensitive.

folio
@use "partials/logo.folio"

prop customerName = ""
prop customerEmail = ""
prop invoiceNumber = ""
prop invoiceDate = ""
prop items = []
prop total = "$0.00"

page(background="#ffffff") {
    column(width="100%", padding=48, gap=24) {
        row(gap=24) {
            column(grow=1, gap=4) {
                heading(color="#0f172a", fontSize=24) "Acme Corporation"
                text(color="#64748b", fontSize=10) "123 Business Street, New York, NY 10001"
            }
            column(gap=4, align="right") {
                text(color="#64748b", fontSize=10, fontWeight="bold") "INVOICE"
                heading(color="#0f172a", fontSize=24, align="right") invoiceNumber
            }
        }

        text(color="#334155", fontSize=11) "Date: {invoiceDate}"
        text(color="#334155", fontSize=11) "Customer: {customerName}"
        text(color="#64748b", fontSize=10) customerEmail

        table(padding=12, background="#f8fafc", width="100%") {
            header(background="#0f172a", color="#ffffff", fontSize=10, fontWeight="bold") {
                th "Item"
                th(align="right") "Qty"
                th(align="right") "Price"
                th(align="right") "Total"
            }
            foreach items as item {
                tr(background="#ffffff", fontSize=10) {
                    td item.name
                    td(align="right") item.quantity
                    td(align="right") item.price
                    td(align="right") item.total
                }
            }
        }

        if total != "" {
            text(align="right", color="#0f172a", fontSize=14, fontWeight="bold") "Total: {total}"
        }
    }
}

Directives ​

@use partial inlining ​

Inline a partial template. The path is resolved relative to the current template.

folio
@use "partials/logo.folio"

page {
    column {
        text "Main content"
    }
}

@theme design-token loading ​

Load a JSON theme file that defines design tokens and named styles.

folio
@theme "modern"

page {
    column {
        text(class="brand") "Hello"
    }
}

The theme file is searched next to the template:

text
modern.json
modern.theme.json
themes/modern.json

@style scoped style blocks ​

Write CSS-like rules that apply to elements by class or element type.

folio
@style {
    .brand {
        color: #1e3a8a;
        fontSize: 20;
    }

    .card {
        background: #f8fafc;
        padding: 16;
        radius: 4;
    }
}

page {
    column {
        text(class="brand") "Branded title"
        column(class="card") { ... }
    }
}

Token references from the loaded theme work inside @style blocks:

folio
@style {
    .brand {
        color: {colors.brand};
        fontSize: {fontSizes.2xl};
    }
}

Partials and reusable headers ​

@use inlines the contents of another .folio file, so you can share a header or footer across documents. Variables in the partial are resolved from the main template scope.

folio
// partials/pro-header.folio
column(width="100%", background="{colors.brand}", padding="{space.6}") {
    row {
        column(grow=1) {
            heading(color="{colors.paper}", fontSize="{fontSizes.2xl}") companyName
            text(color="{colors.subtle}", fontSize="{fontSizes.sm}") companyAddress
        }
        column(align="right") {
            text(color="{colors.paper}", fontWeight="bold") documentLabel
            heading(color="{colors.paper}") documentNumber
        }
    }
}
folio
// invoice.folio
prop companyName = ""
prop companyAddress = ""
prop documentLabel = ""
prop documentNumber = ""

@use "partials/pro-header.folio"
@theme "pro"

page {
    ...
}

Putting it together ​

A full template combines @use partials, @theme tokens and @style rules:

folio
@use "partials/pro-header.folio"
@theme "pro"

@style {
    .card {
        background: {colors.surface};
        padding: {space.6};
        radius: {radii.lg};
    }
}

page(background="{colors.paper}") {
    column(class="card") {
        heading(class="brand") "Hello, Folio 2.0"
    }
}

Props ​

Props declare data the template expects. They have an identifier, an optional type and an optional default value.

folio
prop title = "Folio"
prop items = []
prop config = {}

Elements ​

ElementPurpose
pageA page in the document. Use size and background attributes.
columnVertical flex container. Use gap, padding, align, width, grow.
rowHorizontal flex container. Use gap, padding, align, width.
textA paragraph of text. Supports color, fontSize, fontWeight, align.
headingA heading. Level is set by level=N (default 1).
tableA table with header/tr/td/th children. Use padding, background, width.
headerA table header row.
trA table data row.
td / thTable cells. Use colspan, align, background.

String interpolation ​

Text and heading literals may contain {variable} or {object.property} placeholders:

folio
text "Customer: {customer.name}"
text "Date: {invoiceDate}"

Control flow ​

if ​

folio
if customer.vip {
    text "VIP customer"
} else {
    text "Standard customer"
}

foreach ​

folio
foreach items as item {
    row {
        text item.name
        text(align="right") item.price
    }
}

Styling ​

Style attributes are passed directly on elements. The value can be a number (points), a string color hex code, or one of the supported keywords such as left / right / center / justify for align and bold / normal for fontWeight.

folio
column(padding=24, gap=12, background="#f8fafc", width="100%") {
    text(color="#0f172a", fontSize=12, fontWeight="bold") "Section title"
    text(color="#64748b", fontSize=10) "Section body"
}

Known limitations ​

  • Components with slots are not yet implemented.
  • Filter expressions such as {total | money} are not yet implemented.
  • Gradients and advanced CSS effects are not yet supported.

Engineered for teams that care about predictable PDF output.