Supported CSS
Style templates with the CSS you would write for Chrome’s print view; the renderer lays the HTML out as Chrome prints it. This page lists what works and the rules specific to typefold. For a walk-through of paper, header, footer, fonts and a logo, see build a template.
@page { size: A4 landscape; margin: 15mm 12mm;}
.totals { break-inside: avoid;}What works
Section titled “What works”- Block and inline layout, flexbox and grid, tables (including
border-collapse,<colgroup>and<col>widths), floats, andrelativeandabsolutepositioning. - Box model: margins, padding, borders,
border-radius,box-sizing,overflow. - Text: font properties,
line-height,letter-spacing,text-align,text-transform,text-decoration,white-space,word-break,overflow-wrap,text-overflow. - Colours and backgrounds, including linear, radial and conic gradients,
opacity,box-shadowandtext-shadow. transform, CSS custom properties (var(--x)) andcalc().- Lists, links (clickable in the PDF) and SVG images.
@pagesize and margins, and the page break properties.
What is not supported
Section titled “What is not supported”| Feature | What happens | Instead |
|---|---|---|
@page :first and other page selectors, named pages, margin boxes |
Ignored | Put page numbers in the header or footer. For content that changes per page, such as a first-page letterhead, combine templates. |
filter: blur() and filter: drop-shadow() |
The render fails | Prepare the effect as an image. |
backdrop-filter |
Ignored | Prepare the effect as an image. |
JavaScript in the document and fonts or images at external URLs are not loaded either; see what’s not supported.
Page size and margins
Section titled “Page size and margins”The paper and margins come from the template’s page.json (see
templates). A Reports API request or a preview can override them for one
render with its pdf options (see the API reference), and CSS @page rules
override both; see which value wins.
CSS @page rules
Section titled “CSS @page rules”| Property | Effect |
|---|---|
margin, margin-top, margin-right, margin-bottom, margin-left |
Replace the margins side by side. A side the rule leaves out keeps its value. Each 0 to 500 mm. |
size |
Turns the page to the rule’s orientation. The paper itself changes only when page.json or the request sets "preferCssPageSize": true. |
size accepts:
auto.- One or two lengths, such as
80mm 200mm. One length makes a square page. Each side above 0 and up to 2,000 mm. The orientation is the one the width and height imply. A3,A4,A5,B4,B5,JIS-B4,JIS-B5,letter,legalorledger, optionally withportraitorlandscape, or the orientation alone.
Lengths can use mm, cm, Q, in, pt, pc or px, or be a bare 0. A value that cannot
be read, such as a percentage or a size over the limits, is skipped. !important makes no
difference.
Which rules count:
- Rules in the template’s
<style>elements and its stylesheets, at the top level or inside@mediathat print matches (print,all, or no media type). Later rules win. - Only unnamed
@pagerules without a page selector. - Not
@pagerules inheader.htmlandfooter.html.
Which value wins
Section titled “Which value wins”Values apply in this order, each replacing the one before:
- The template’s
page.json. - The request’s
pdfoptions (Reports API and previews), field by field. - The document’s
@pagerules.
When an @page rule sets an orientation, the paper (pageSize, or the rule’s own size with
preferCssPageSize) is turned to it, and the orientation setting no longer applies.
Headers and footers
Section titled “Headers and footers”header.html and footer.html (the Page header and Page footer tabs in Studio) are
Handlebars, with the same data, helpers, partials and translations as the body. They work in
HTML and component templates, not in spreadsheet templates. Leave the
file empty for no band.
Page numbers
Section titled “Page numbers”An element with the class pageNumber or totalPages, anywhere in a header or footer, is
filled with the current page number or the total page count:
<span class="pageNumber"></span> / <span class="totalPages"></span>With pageRanges, the numbers still count the whole document.
Layout rules
Section titled “Layout rules”- The band is drawn inside the top or bottom margin, which does not grow to fit it. If it is cut off, make the margin larger.
- It spans the full page width.
- Its top-level elements sit in a flex row, aligned to the top of the margin in a header and the
bottom in a footer. The band’s own
htmlandbodytags are dropped. - Backgrounds are not printed unless
header.htmlorfooter.htmlsetsprint-color-adjust: exact. - Every page gets the same band. Only the page numbers change from page to page.
- Images resolve by path as in the body.
- The template’s stylesheets apply to the bands, and a
<style>element in a band applies to the whole document. Style the band with inlinestyleattributes to keep it separate.
Page breaks
Section titled “Page breaks”Supported: break-before: page, break-after: page, break-inside: avoid, and
page-break-before: always, page-break-after: always and page-break-inside: avoid.
A table that runs over several pages repeats its <thead> at the top of each page and prints its
<tfoot> once, after the last row. Add break-inside: avoid to tr to keep each row on one
page.
Text is set in fonts bundled with Typefold and the fonts you upload, never in fonts installed on the server or loaded from a URL.
font-family in your CSS |
What is used |
|---|---|
Arial |
GNU FreeSans (regular and bold). |
| A family you uploaded and declared | Your font. |
Anything else, including sans-serif, serif, Helvetica |
Geist, the built-in sans-serif font. |
- Formats. TTF, OTF and WOFF2.
- Declaring. An
@font-facerule for each file, withfont-family,src: url(...),font-weightandfont-style.url()paths start from the folder of the file they are written in; see how paths resolve. - Embedding.
src: url({#asset ../fonts/Inter-Regular.ttf @encoding=dataURI})embeds the font in the CSS. - Fallback. Characters the chosen font lacks fall back to the bundled fonts: Geist, FreeSans, FreeSerif Bold for symbols such as ★, and Noto Color Emoji for emoji. Uploaded fonts are never used as fallbacks for other families, so a monospace font you uploaded cannot replace text set in Arial.
- Failures. A character no font covers fails the render with
ASSET_INVALID(font_glyphs_missing): upload a font that covers it and use it in your CSS. A font file that cannot be read fails withASSET_INVALID(font_invalid). See errors.
Images
Section titled “Images”- Formats. PNG, JPEG and SVG.
- Referencing. Upload the image to the workspace and name it by path from
srcor CSSurl(), or embed it with{#asset path @encoding=dataURI}. - No network. Images at external
https://URLs are not fetched. Upload them, or send them in your data asdata:URLs. - Failures. An image that cannot be decoded fails with
ASSET_INVALID(image_invalid).
Chrome compatibility
Section titled “Chrome compatibility”The renderer lays out HTML as Chrome prints it, including in these cases:
- HTML parsing follows Chrome, which repairs unclosed tags and the like. Links are blue and underlined, and tables without a width shrink to their content, as in Chrome.
- Wide documents shrink to fit. A document wider than the page area is laid out on a larger page, up to twice the size, and scaled down onto the paper, header and footer included.
- Full-page sections. With
html, body { height: 100% }, a percentage height on a child ofbodyis a share of the page. - Grids with a fixed column. In a grid that mixes
autoand fixed-size tracks, the auto track may shrink so the fixed track stays on the page. Amin-widthon the grid’s items opts out. - Images. An image with
width: 100%is laid out as a block. An image alone in its container withmax-width(andmax-height) is scaled down with its aspect ratio kept. - Percentage widths on inline blocks and floats keep their size.
- Row heights. A
heighton a table row applies to its cells. - Line breaks. Spaces around
<br>and at the start or end of a block add no indentation.
See what the renderer received
Section titled “See what the renderer received”When the PDF of an HTML template looks different from what you expect, request a preview,
POST /v1/templates/{id}/preview, with "format": "html". It returns the filled HTML, with its
stylesheets, header and footer, as one page you can open in a browser. See the
API reference.