Styling Best Practices
Master professional styling patterns, performance optimization, and common gotchas for PDF generation.
Learning Objectives
By the end of this article, you’ll be able to:
- Apply performance optimization techniques
- Understand PDF vs browser rendering differences
- Avoid common styling pitfalls
- Troubleshoot styling issues effectively
- Follow maintainable styling patterns
- Build professional, production-ready documents
Performance Best Practices
Keep Selectors Simple
❌ Complex, slow selectors:
body div.container div.content div.section div.box p.text {
color: blue;
}
✅ Simple, fast selectors:
.text {
color: blue;
}
Why? Simple selectors are faster to match and easier to maintain.
Minimize Specificity Wars
❌ Escalating specificity:
.card { background: white; }
.card.highlighted { background: yellow; }
div.card.highlighted { background: yellow !important; }
✅ Controlled specificity:
.card { background: white; }
.card-highlighted { background: yellow; }
Use Efficient Properties
❌ Expensive:
.element {
box-shadow: 0 10pt 20pt rgba(0, 0, 0, 0.5); /* Complex rendering */
filter: blur(5pt); /* May not be supported */
}
✅ Efficient:
.element {
border: 1pt solid #d1d5db; /* Simple, fast */
background-color: #f9fafb; /* Solid colors are fast */
}
PDF vs Browser Differences
Key Differences
| Feature | Browser | PDF/Scryber |
|---|---|---|
| Layout | Continuous scroll | Fixed pages |
| Interactive | Yes | Limited |
| Fonts | Web fonts cached | Embedded in PDF |
| Units | Pixels (device-dependent) | Points (absolute) |
| Media queries | Yes | Limited |
| Flexbox | Full support | No support |
| Grid | Full support | No support |
| JavaScript | Full support | No support |
Layout Differences
Browser approach (doesn’t work in PDF):
/* Flexbox - NOT supported */
.container {
display: flex;
justify-content: space-between;
}
/* CSS Grid - NOT supported */
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
PDF approach (works correctly):
/* Table-based layout */
.container {
display: table;
width: 100%;
}
.column {
display: table-cell;
width: 33.33%;
}
/* Float-based layout */
.column {
float: left;
width: 33.33%;
}
Supported CSS Features
✅ Well-supported:
- Typography (font-size, font-weight, font-family, color)
- Box model (margin, padding, border, width, height)
- Positioning (static, relative, absolute)
- Display (block, inline, inline-block, table, none)
- Colors (hex, RGB, RGBA, named)
- Text alignment and decoration
- Lists and tables
- Page breaks
- Background colors and images
⚠️ Limited/Not supported:
- Flexbox
- CSS Grid
- Transformations (rotate, scale, skew)
- Animations and transitions
- Some pseudo-elements
- Advanced filters
- Media queries (limited)
- Viewport units (vw, vh)
- calc() with complex expressions
Common Pitfalls and Solutions
Pitfall 1: Expecting Browser Behavior
❌ Problem:
/* Expecting smooth scrolling, animations */
.element {
transition: all 0.3s ease;
animation: fadeIn 1s;
}
✅ Solution:
/* Design for static, page-based layout */
.element {
/* Static styles only */
opacity: 1;
}
Pitfall 2: Using Unsupported Units
❌ Problem:
.element {
width: 50vw; /* Viewport width - not supported */
height: 100vh; /* Viewport height - not supported */
}
✅ Solution:
.element {
width: 50%; /* Percentage works */
height: 400pt; /* Points work */
}
Pitfall 3: Complex Backgrounds
❌ Problem:
.element {
background: linear-gradient(45deg, #667eea 0%, #764ba2 100%),
url('pattern.png'),
radial-gradient(circle, #fff 0%, #000 100%);
}
✅ Solution:
/* Simpler background */
.element {
background-color: #667eea;
background-image: url('pattern.png');
}
Pitfall 4: Forgetting Page Breaks
❌ Problem:
/* No control over page breaks */
.section {
margin-bottom: 40pt;
}
✅ Solution:
/* Control page breaks explicitly */
.section {
margin-bottom: 40pt;
page-break-inside: avoid; /* Don't break inside */
}
.chapter {
page-break-before: always; /* Always start new page */
}
Pitfall 5: Using External Resources
❌ Problem:
/* Remote resources may fail to load */
@import url('https://fonts.googleapis.com/css2?family=Roboto');
.element {
background-image: url('https://example.com/slow-loading-image.jpg');
}
✅ Solution:
/* Use local resources or embed */
@font-face {
font-family: 'Roboto';
src: url('./fonts/Roboto-Regular.ttf');
}
.element {
background-image: url('./images/image.jpg');
/* Or use base64 for small images */
}
Maintainable Patterns
Pattern 1: Consistent Spacing Scale
❌ Inconsistent:
.element1 { margin: 11pt; }
.element2 { margin: 17pt; }
.element3 { margin: 23pt; }
✅ Consistent scale:
/* Use scale: 10pt, 20pt, 30pt, 40pt */
.element1 { margin: 10pt; }
.element2 { margin: 20pt; }
.element3 { margin: 30pt; }
Pattern 2: Consistent Color Palette
❌ Random colors:
.heading1 { color: #2563eb; }
.heading2 { color: #3b70f4; }
.heading3 { color: #4a82f7; }
✅ Defined palette:
/* Define colors */
:root {
--color-primary: #2563eb;
--color-secondary: #3b82f6;
--color-accent: #60a5fa;
}
/* Use consistently */
.heading1 { color: var(--color-primary); }
.heading2 { color: var(--color-secondary); }
Pattern 3: Reusable Components
❌ Duplicated styles:
.box1 {
border: 1pt solid #d1d5db;
padding: 20pt;
border-radius: 5pt;
}
.box2 {
border: 1pt solid #d1d5db;
padding: 20pt;
border-radius: 5pt;
}
✅ Component class:
/* Base component */
.card {
border: 1pt solid #d1d5db;
padding: 20pt;
border-radius: 5pt;
}
/* Variants */
.card-large {
padding: 30pt;
}
.card-highlighted {
border-color: #2563eb;
background-color: #eff6ff;
}
Pattern 4: Mobile-First Thinking
❌ Desktop-first:
.element {
width: 800pt; /* Fixed desktop width */
}
✅ Flexible approach:
.element {
width: 100%; /* Flexible */
max-width: 800pt; /* Constrained */
}
Troubleshooting Guide
Issue: Styles Not Applying
Checklist:
- ✅ Check specificity (use browser dev tools simulation)
- ✅ Verify CSS syntax (missing semicolons, braces)
- ✅ Check property names (correct spelling)
- ✅ Ensure stylesheet is loaded (check file path)
- ✅ Verify element exists in HTML
- ✅ Check for typos in class names
Debug technique:
/* Add temporary high-visibility style */
.debug {
border: 5pt solid red !important;
background-color: yellow !important;
}
Issue: Layout Not Working
Checklist:
- ✅ Verify box-sizing is set
- ✅ Check for flexbox/grid (not supported)
- ✅ Use table or float layout instead
- ✅ Check for width overflow
- ✅ Verify margins aren’t collapsing
- ✅ Test with simpler layout first
Issue: Text Not Visible
Checklist:
- ✅ Check color contrast
- ✅ Verify font-size is set
- ✅ Check if element has display: none
- ✅ Verify text color vs background color
- ✅ Check for overflow: hidden
- ✅ Verify font is loaded
Issue: Images Not Showing
Checklist:
- ✅ Verify file path is correct
- ✅ Check if file exists
- ✅ Use absolute paths when possible
- ✅ Verify image format is supported (PNG, JPEG, GIF)
- ✅ Check file size (very large images may fail)
- ✅ Test with local file first
Production Checklist
Before deploying PDF generation:
Styling Checklist
- All colors have sufficient contrast
- Consistent spacing scale used throughout
- Typography hierarchy is clear
- No flexbox or grid layout
- All fonts are loaded/embedded
- All images load correctly
- External resources are local or cached
- Page breaks are controlled
- Styles are organized and commented
- No !important unless necessary
Testing Checklist
- Test with sample data
- Test with edge cases (empty, null, very long)
- Test all conditional branches
- Verify page breaks
- Check file size
- Test on different page sizes
- Verify metadata (title, author, etc.)
- Check for console errors/warnings
- Validate generated PDF opens correctly
- Test print output if applicable
Performance Checklist
- Images are optimized
- External resources are minimized
- CSS is efficient (simple selectors)
- No unnecessary complex calculations
- Fonts are subsetted if possible
- Large tables have explicit widths
- Templates are cached when reused
- Error handling is in place
Quick Reference
Do’s ✅
- Use points (pt) for measurements
- Keep selectors simple (max 3 levels)
- Use table-cell for multi-column layouts
- Control page breaks explicitly
- Test with real data early and often
- Organize styles logically
- Use consistent spacing scale
- Define color palette upfront
- Comment complex styles
- Test edge cases
Don’ts ❌
- Don’t use flexbox or grid
- Don’t rely on viewport units
- Don’t use animations/transitions
- Don’t use !important excessively
- Don’t forget page breaks
- Don’t use remote resources without caching
- Don’t overcomplicate selectors
- Don’t forget to test
- Don’t ignore file size
- Don’t skip error handling
Real-World Example
Professional Invoice with Best Practices:
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Invoice</title>
<style>
/* ==============================================
BASE STYLES - Use points, simple selectors
============================================== */
@page {
size: Letter;
margin: 1in;
}
* {
box-sizing: border-box; /* Predictable sizing */
}
body {
font-family: Helvetica, sans-serif;
font-size: 11pt;
line-height: 1.6;
color: #333;
margin: 0;
}
/* ==============================================
TYPOGRAPHY - Consistent hierarchy
============================================== */
h1 {
font-size: 24pt;
color: #1e40af;
margin: 0 0 10pt 0;
}
h2 {
font-size: 18pt;
color: #2563eb;
margin: 20pt 0 10pt 0;
}
/* ==============================================
COMPONENTS - Reusable, well-organized
============================================== */
.header {
border-bottom: 2pt solid #2563eb;
padding-bottom: 20pt;
margin-bottom: 30pt;
}
.invoice-details {
margin-bottom: 30pt;
}
.info-row {
margin-bottom: 5pt;
}
.label {
font-weight: bold;
display: inline-block;
width: 150pt;
}
/* Table with explicit structure */
table {
width: 100%;
border-collapse: collapse;
margin: 20pt 0;
}
thead {
background-color: #2563eb;
color: white;
}
th {
padding: 10pt;
text-align: left;
font-weight: bold;
}
td {
padding: 8pt;
border-bottom: 1pt solid #e5e7eb;
}
tfoot {
background-color: #eff6ff;
font-weight: bold;
}
/* ==============================================
UTILITIES - Consistent spacing
============================================== */
.text-right {
text-align: right;
}
.text-bold {
font-weight: bold;
}
.mb-10 {
margin-bottom: 10pt;
}
.mb-20 {
margin-bottom: 20pt;
}
/* ==============================================
PAGE BREAKS - Controlled pagination
============================================== */
.no-break {
page-break-inside: avoid;
}
</style>
</head>
<body>
<div class="header no-break">
<h1>INVOICE</h1>
<p>Invoice #: INV-2025-001</p>
<p>Date: January 15, 2025</p>
</div>
<div class="invoice-details no-break">
<h2>Bill To</h2>
<div class="info-row">
<span class="label">Company:</span>
<span>Acme Corporation</span>
</div>
<div class="info-row">
<span class="label">Address:</span>
<span>123 Main St, City, ST 12345</span>
</div>
</div>
<table class="no-break">
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Rate</th>
<th class="text-right">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>Consulting Services</td>
<td>10</td>
<td>$150.00</td>
<td class="text-right">$1,500.00</td>
</tr>
<tr>
<td>Development</td>
<td>20</td>
<td>$200.00</td>
<td class="text-right">$4,000.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Subtotal</td>
<td class="text-right">$5,500.00</td>
</tr>
<tr>
<td colspan="3">Tax (8%)</td>
<td class="text-right">$440.00</td>
</tr>
<tr style="font-size: 14pt;">
<td colspan="3">TOTAL</td>
<td class="text-right">$5,940.00</td>
</tr>
</tfoot>
</table>
<div style="margin-top: 40pt; padding-top: 20pt; border-top: 1pt solid #d1d5db; font-size: 9pt; color: #666;">
<p class="text-bold">Payment Terms:</p>
<p>Payment due within 30 days. Thank you for your business!</p>
</div>
</body>
</html>
Next Steps
You’ve completed the Styling & Appearance series! Continue your journey:
- Layout & Positioning - Master page layout and positioning
- Typography & Fonts - Custom fonts and advanced typography
- Practical Applications - Build real-world documents
Continue learning → Layout & Positioning**