Best Practices
Quick Reference
| Category | Recommended | Use with Caution | Avoid |
|---|---|---|---|
| HTML | Semantic HTML | Generic <div> | Deep nested <div> structures |
| Layout | Block layout | Simple Flexbox | Complex Grid or deeply nested Flexbox |
| Tables | Native <table> | Small Flex layouts | Recreating tables using <div> |
| Header/Footer | Running elements | Margin box generated content | Duplicated HTML on every page |
| Pagination | break-before, break-after, break-inside | Manual page breaks | Spacer elements for page positioning |
| Positioning | Normal document flow | Small position: relative adjustments | position: absolute for page layout |
| Heights | Auto / min-height | Fixed height for known content | Fixed height with dynamic content |
| Images | SVG | High-resolution PNG | Low-resolution images |
| Fonts | Embedded fonts | System fonts | Assuming fonts exist on the server |
| Effects | Borders, background colors | Border radius | filter, clip-path, backdrop-filter |
1. HTML Structure
Recommended
<body>
<header class="report-header"></header>
<main>
<section>...</section>
</main>
<footer class="report-footer"></footer>
</body>
Guidelines
-
Use semantic elements.
-
Group content into logical sections.
-
Keep nesting shallow.
Prefer
body
├── header
├── main
│ ├── section
│ └── section
└── footer
Avoid
div
└── div
└── div
└── div
└── div
2. Layout Guidelines
Layout Decision Order
When implementing a layout, choose the first option that satisfies the design.
Need a layout?
│
├── Normal document?
│ │
│ └── Block Layout
│
├── Horizontal alignment?
│ │
│ └── Simple Flexbox
│
├── Tabular data?
│ │
│ └── HTML Table
│
├── Dashboard/Grid?
│ │
│ └── Grid (only after testing)
│
└── Complex overlapping layout?
│
└── Redesign the layout
3. Layout
Block Layout
Preferred Most documents can be built using normal block flow.
Example
Heading
Paragraph
Paragraph
Table
Summary
Advantages
- Predictable pagination
- Easier maintenance
- Better rendering consistency
Flexbox
Use with caution Flexbox support has improved significantly in recent versions of WeasyPrint, but layouts involving pagination or nested flex containers should always be tested.
Recommended
Logo Company
Label Value
Customer Invoice
Good use cases
- Header
- Footer
- Two-column rows
- Label-value pairs
- Navigation-like horizontal alignment
Avoid multi-level nested flexbox
Flex
└── Flex
└── Flex
└── Flex
Grid
Avoid complex grid, use only when necessary Grid support continues to improve, but it is still less mature than block layout.
Grid is acceptable when
- Layout is static.
- No pagination occurs inside grid items.
- The layout has been thoroughly tested.
Avoid Grid for
- Dashboard pages
- Complex reports
- Nested grids
- Responsive-style layouts
If Flexbox or block layout can achieve the same result, prefer those approaches.
4. Page Layout
Always define page settings.
@page {
size: A4;
margin: 20mm;
}
Never rely on browser defaults.
More details about @page layout can be found here.
5. Headers & Footers
Recommended
.report-header {
position: running(header);
}
.report-footer {
position: running(footer);
}
@page {
@top-center {
content: element(header);
}
@bottom-center {
content: element(footer);
}
}
Benefits
- Single source of truth
- Easier maintenance
- No duplicated HTML
More details about configuring header and footer can be found here.
6. Margin Boxes
Suitable for lightweight generated content.
@page {
@top-left {
content: 'Monthly Report';
}
}
Typical uses
- Document title
- Page number
- Company name
Avoid placing complex layouts inside margin boxes.
More details about configuring margin boxes can be found here.
7. Named Strings
Named strings are recommended for dynamic running headers.
/* 1. Extract the text from the heading and save it into a variable named "chapter" */
h1 {
string-set: chapter content();
}
/* 2. Retrieve that text and display it in the top-right corner of every printed page */
@page {
@top-right {
content: string(chapter);
}
}
Result
Page 1
Customer
-----------------
Page 2
Inventory
-----------------
Page 3
Financial Summary
8. Counters
Use counters for automatic numbering.
content: counter(page);
Page X of Y
content: counter(page) ' / ' counter(pages);
Section numbering
h2 {
counter-increment: chapter;
}
h2::before {
content: counter(chapter) '. ';
}
More details about counter can be found here.
9. Pagination
Recommended
.section {
break-before: page;
}
.card {
break-inside: avoid;
}
Keep together
- Heading + paragraph
- Table header + first row
- Card content
- Images with captions
More details about pagination can be found here.
10. Tables
Always use HTML tables.
<table>
<thead></thead>
<tbody></tbody>
</table>
Repeat headers
thead {
display: table-header-group;
}
Avoid splitting rows
tr {
break-inside: avoid;
}
Avoid fixed row heights.
11. Images
Preferred
- SVG
- Vector logos
Acceptable
- PNG (300 DPI)
Avoid
- Low-resolution images
- Images larger than necessary
Always define
img {
max-width: 100%;
}
12. Typography
Embed fonts.
@font-face {
...
}
Always test
- Bold
- Italic
- Currency
- Unicode
- Symbols
Avoid relying on fonts installed on the rendering server.
13. Common Mistakes
-
Using CSS Grid everywhere
-
Fixed heights
-
Absolute positioning for layout
-
Empty
<div>for spacing -
Browser-only CSS features
-
Very deep HTML hierarchy
-
Duplicating headers manually
-
Using
<div>instead of<table> -
Testing only with small datasets
14. Checklist
Layout
- Page size defined
- Margins configured
- Flexbox used where appropriate
- No unnecessary absolute positioning
Pagination
- Sections break correctly
- Cards are not split
- Tables paginate correctly
- Table headers repeat
Header & Footer
- Running elements used
- Page numbers correct
- Header/Footer rendered on every page
Assets
- Fonts embedded
- SVG preferred
- Images scaled correctly
Final Verification
- Rendered with realistic data
- No overflow
- No unexpected page breaks
- Matches the design specification
Rules
- Design for printed pages, not browsers.
- Prefer block layout whenever possible.
- Use semantic HTML.
- Keep CSS simple and maintainable.
- Use Flexbox only for simple alignment.
- Use Grid only when necessary and after testing.
- Use native HTML tables for tabular data.
- Configure page settings with @page.
- Use running elements for headers and footers.
- Design pagination intentionally using page-break properties.
- Always test with realistic production data.