Positioning
Master CSS positioning for precise element placement in PDF documents.
Learning Objectives
By the end of this article, you’ll be able to:
- Understand positioning types (static, relative, absolute)
- Use absolute positioning for precise placement
- Apply relative positioning for offset adjustments
- Position elements relative to containers
- Create overlays and watermarks
- Position elements on specific pages
- Avoid common positioning pitfalls
Position Types
static (Default)
Normal document flow - elements appear in order.
.element {
position: static; /* Default, not usually specified */
}
Characteristics:
- Elements appear in normal document flow
- top, right, bottom, left properties have no effect
- Cannot be used as positioning context for absolute children
relative
Offset from normal position, but space preserved.
.element {
position: relative;
top: 10pt; /* Move down 10pt */
left: 20pt; /* Move right 20pt */
}
Characteristics:
- Element offset from where it would normally be
- Original space is preserved
- Can be positioning context for absolute children
- Useful for small adjustments
absolute
Positioned relative to nearest positioned ancestor.
.element {
position: absolute;
top: 50pt; /* 50pt from top */
right: 50pt; /* 50pt from right */
}
Characteristics:
- Removed from normal document flow
- No space preserved
- Positioned relative to nearest positioned (non-static) ancestor
- If no positioned ancestor, positioned relative to page
Relative Positioning
Offset elements from their normal position.
Basic Offset
.nudge-down {
position: relative;
top: 5pt; /* Move down 5pt */
}
.nudge-right {
position: relative;
left: 10pt; /* Move right 10pt */
}
.nudge-up {
position: relative;
top: -5pt; /* Move up 5pt (negative) */
}
Creating Positioning Context
.container {
position: relative; /* Creates context for absolute children */
}
.child {
position: absolute; /* Positioned relative to .container */
top: 0;
right: 0;
}
Absolute Positioning
Precise element placement.
Positioning from Corners
/* Top-left corner */
.top-left {
position: absolute;
top: 20pt;
left: 20pt;
}
/* Top-right corner */
.top-right {
position: absolute;
top: 20pt;
right: 20pt;
}
/* Bottom-left corner */
.bottom-left {
position: absolute;
bottom: 20pt;
left: 20pt;
}
/* Bottom-right corner */
.bottom-right {
position: absolute;
bottom: 20pt;
right: 20pt;
}
Centering with Absolute Position
.centered {
position: absolute;
width: 200pt;
height: 100pt;
/* Center horizontally */
left: 50%;
margin-left: -100pt; /* Half of width */
/* Center vertically */
top: 50%;
margin-top: -50pt; /* Half of height */
}
/* Alternative with calc() */
.centered-calc {
position: absolute;
width: 200pt;
height: 100pt;
left: calc(50% - 100pt);
top: calc(50% - 50pt);
}
Stretching to Fill Container
.fill-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* Stretches to fill entire container */
}
/* With padding/margins */
.fill-with-spacing {
position: absolute;
top: 20pt;
right: 20pt;
bottom: 20pt;
left: 20pt;
}
Positioning Context
Control what elements are positioned relative to.
Page as Context (Default)
/* No positioned ancestors - relative to page */
.watermark {
position: absolute;
bottom: 50pt;
right: 50pt;
opacity: 0.3;
}
Container as Context
/* Container creates positioning context */
.card {
position: relative; /* Creates context */
padding: 20pt;
border: 1pt solid #d1d5db;
}
/* Badge positioned relative to card */
.badge {
position: absolute;
top: -10pt; /* Relative to .card */
right: -10pt;
background-color: #dc2626;
color: white;
padding: 5pt 10pt;
border-radius: 10pt;
font-size: 9pt;
font-weight: bold;
}
Z-Index (Stacking Order)
Control which elements appear on top.
.layer-bottom {
position: absolute;
z-index: 1; /* Lower number = further back */
}
.layer-middle {
position: absolute;
z-index: 10; /* Higher than layer-bottom */
}
.layer-top {
position: absolute;
z-index: 100; /* Highest - appears on top */
}
Default stacking:
- Elements later in HTML appear on top
- z-index only works on positioned elements (not static)
- Higher z-index = appears on top
Practical Examples
Example 1: Watermark
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Document with Watermark</title>
<style>
@page {
size: Letter;
margin: 1in;
}
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, sans-serif;
font-size: 11pt;
line-height: 1.6;
margin: 0;
position: relative; /* Create positioning context */
}
/* ==============================================
WATERMARK
============================================== */
.watermark {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-45deg);
font-size: 72pt;
color: #e5e7eb;
opacity: 0.3;
font-weight: bold;
z-index: 0; /* Behind content */
pointer-events: none; /* Don't interfere with text selection */
}
/* ==============================================
CONTENT
============================================== */
.content {
position: relative;
z-index: 1; /* Above watermark */
}
h1 {
color: #1e40af;
margin-top: 0;
margin-bottom: 20pt;
}
p {
margin-bottom: 12pt;
}
</style>
</head>
<body>
<div class="watermark">CONFIDENTIAL</div>
<div class="content">
<h1>Confidential Report</h1>
<p>This document contains confidential information and is intended only for authorized personnel.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</div>
</body>
</html>
Example 2: Badge on Cards
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Cards with Badges</title>
<style>
@page {
size: Letter;
margin: 1in;
}
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, sans-serif;
font-size: 11pt;
margin: 0;
}
h1 {
text-align: center;
color: #1e40af;
margin-top: 0;
margin-bottom: 30pt;
}
/* ==============================================
CARD CONTAINER
============================================== */
.cards-container {
display: table;
width: 100%;
border-spacing: 20pt 20pt;
}
.card-row {
display: table-row;
}
.card-cell {
display: table-cell;
width: 50%;
vertical-align: top;
}
/* ==============================================
CARD
============================================== */
.card {
position: relative; /* Creates positioning context */
border: 1pt solid #d1d5db;
border-radius: 8pt;
padding: 20pt;
background-color: white;
}
.card-title {
font-size: 16pt;
font-weight: bold;
color: #1e40af;
margin: 0 0 10pt 0;
}
.card-content {
font-size: 10pt;
color: #666;
margin: 0;
}
/* ==============================================
BADGES
============================================== */
.badge {
position: absolute;
top: -10pt;
right: -10pt;
padding: 5pt 12pt;
border-radius: 15pt;
font-size: 9pt;
font-weight: bold;
color: white;
z-index: 10;
}
.badge-new {
background-color: #059669;
}
.badge-sale {
background-color: #dc2626;
}
.badge-popular {
background-color: #2563eb;
}
.badge-limited {
background-color: #f59e0b;
}
/* ==============================================
PRICE TAG
============================================== */
.price {
position: absolute;
bottom: 15pt;
right: 15pt;
font-size: 20pt;
font-weight: bold;
color: #2563eb;
}
</style>
</head>
<body>
<h1>Featured Products</h1>
<div class="cards-container">
<div class="card-row">
<div class="card-cell">
<div class="card">
<span class="badge badge-new">NEW</span>
<h2 class="card-title">Premium Widget</h2>
<p class="card-content">
Our flagship product with advanced features and premium build quality. Perfect for professional use.
</p>
<div class="price">$299</div>
</div>
</div>
<div class="card-cell">
<div class="card">
<span class="badge badge-sale">SALE</span>
<h2 class="card-title">Standard Widget</h2>
<p class="card-content">
Reliable performance at an affordable price. Great value for everyday use.
</p>
<div class="price">$149</div>
</div>
</div>
</div>
<div class="card-row">
<div class="card-cell">
<div class="card">
<span class="badge badge-popular">POPULAR</span>
<h2 class="card-title">Deluxe Widget</h2>
<p class="card-content">
Enhanced features with excellent value. Our most popular choice among customers.
</p>
<div class="price">$199</div>
</div>
</div>
<div class="card-cell">
<div class="card">
<span class="badge badge-limited">LIMITED</span>
<h2 class="card-title">Special Edition</h2>
<p class="card-content">
Exclusive limited edition with unique features. Only available while supplies last.
</p>
<div class="price">$399</div>
</div>
</div>
</div>
</div>
</body>
</html>
Example 3: Header with Logo Positioning
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Positioned Header</title>
<style>
@page {
size: Letter;
margin: 1in;
}
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, sans-serif;
font-size: 11pt;
line-height: 1.6;
margin: 0;
}
/* ==============================================
HEADER
============================================== */
.page-header {
position: relative; /* Creates positioning context */
height: 80pt;
margin-bottom: 30pt;
border-bottom: 2pt solid #2563eb;
padding: 15pt 0;
}
.logo {
position: absolute;
top: 15pt;
left: 0;
width: 60pt;
height: 60pt;
background-color: #2563eb;
border-radius: 5pt;
color: white;
font-size: 24pt;
font-weight: bold;
display: table;
}
.logo-text {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.company-info {
position: absolute;
top: 15pt;
left: 80pt; /* After logo */
}
.company-name {
font-size: 20pt;
font-weight: bold;
color: #1e40af;
margin: 0 0 5pt 0;
}
.company-tagline {
font-size: 11pt;
color: #666;
margin: 0;
}
.contact-info {
position: absolute;
top: 15pt;
right: 0;
text-align: right;
font-size: 9pt;
color: #666;
}
.contact-info p {
margin: 0 0 3pt 0;
}
/* ==============================================
CONTENT
============================================== */
h1 {
color: #1e40af;
font-size: 24pt;
margin-top: 0;
margin-bottom: 20pt;
}
p {
margin-bottom: 12pt;
}
</style>
</head>
<body>
<div class="page-header">
<div class="logo">
<div class="logo-text">A</div>
</div>
<div class="company-info">
<div class="company-name">Acme Corporation</div>
<p class="company-tagline">Excellence in Innovation</p>
</div>
<div class="contact-info">
<p>123 Business Street, Suite 100</p>
<p>New York, NY 10001</p>
<p>(555) 123-4567</p>
</div>
</div>
<h1>Business Proposal</h1>
<p>This proposal outlines our comprehensive solution for your business needs.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</body>
</html>
Try It Yourself
Exercise 1: Corner Badges
Create a document with:
- Multiple cards or boxes
- Different colored badges in corners (NEW, SALE, etc.)
- Badges positioned with absolute positioning
- Various badge positions (top-left, top-right, etc.)
Exercise 2: Watermark
Create a watermarked document:
- Diagonal “DRAFT” or “CONFIDENTIAL” watermark
- Semi-transparent overlay
- Watermark behind content (z-index)
- Content remains readable
Exercise 3: Complex Header
Create a header with:
- Logo on the left (absolute position)
- Company name in center
- Contact info on the right (absolute position)
- All within relative container
Common Pitfalls
❌ Forgetting Positioning Context
/* Parent has no position */
.parent {
/* position: static (default) */
}
.child {
position: absolute;
top: 0; /* Positioned relative to page, not parent! */
}
✅ Solution: Create positioning context
.parent {
position: relative; /* Creates context */
}
.child {
position: absolute;
top: 0; /* Now relative to .parent */
}
❌ Overlapping Content Unintentionally
.element {
position: absolute;
top: 100pt;
left: 100pt;
/* May overlap with other content */
}
✅ Solution: Use z-index and plan layout
.background-element {
position: absolute;
z-index: 0; /* Behind */
}
.content {
position: relative;
z-index: 1; /* On top */
}
❌ Using Position Without Offset
.element {
position: absolute;
/* No top/right/bottom/left - appears at default position */
}
✅ Solution: Specify position
.element {
position: absolute;
top: 50pt;
left: 100pt;
}
❌ Centering Without Width/Height
.centered {
position: absolute;
left: 50%;
/* No width specified - can't calculate offset */
}
✅ Solution: Specify dimensions
.centered {
position: absolute;
width: 200pt; /* Known width */
left: calc(50% - 100pt); /* Center */
}
❌ Position: Fixed (Not Supported)
.element {
position: fixed; /* Not supported in PDF */
}
✅ Solution: Use absolute positioning
.element {
position: absolute;
top: 0;
right: 0;
}
Positioning Checklist
- Positioning context created (position: relative on parent)
- Offset values specified (top, right, bottom, left)
- z-index set if stacking elements
- Width/height specified for centered elements
- Doesn’t overlap important content unintentionally
- Works across page breaks (test multi-page documents)
- Avoid position: fixed (not supported)
Best Practices
- Create positioning context - Use
position: relativeon containers - Use absolute for overlays - Watermarks, badges, decorative elements
- Use relative for adjustments - Small offsets from normal position
- Control stacking with z-index - Plan layer order intentionally
- Specify dimensions - Required for centering calculations
- Test with content - Positioning may behave differently with real data
- Avoid excessive nesting - Keep positioning contexts simple
- Document complex positioning - Comment why elements are positioned
Next Steps
Now that you master positioning:
- Tables - Advanced table layouts
- Headers & Footers - Repeating page elements
- Layout Best Practices - Professional patterns
Continue learning → Tables