CSS @pageルール完全ガイド|PDF用紙サイズ・余白・ヘッダー設定
HTMLからPDFを生成するとき、用紙サイズや余白、ヘッダー・フッターの配置はどうやって制御するのか。その答えがCSS @page ルールです。
@page はCSS Paged Mediaモジュールの中核で、印刷やPDF出力に特化したスタイル指定ができます。この記事では @page の基本構文からページ擬似クラス、名前付きページ、マージンボックスまで、PDF生成に必要な知識を体系的に解説します。
FUNBREW PDFのPlaygroundを使えば、この記事のコード例をすぐに試せます。
@page の基本構文
@page ルールは、ページ全体に適用されるスタイルを定義します。最もよく使うのは size と margin です。
用紙サイズの指定
/* A4縦 */
@page {
size: A4;
}
/* A4横(landscape) */
@page {
size: A4 landscape;
}
/* 具体的なサイズを指定 */
@page {
size: 210mm 297mm;
}
/* レター(US Letter) */
@page {
size: letter;
}
size プロパティで使えるキーワードは A3, A4, A5, B4, B5, letter, legal などです。幅と高さを直接 mm, cm, in で指定することもできます。
余白(margin)の設定
@page {
size: A4;
margin: 20mm;
}
/* 上下左右を個別に設定 */
@page {
size: A4;
margin: 25mm 20mm 30mm 20mm; /* 上 右 下 左 */
}
余白の設定はPDFのレイアウトに大きく影響します。請求書なら上下左右20mm程度、レポートなら上下25mm・左右20mmが一般的です。
ヒント:
@pageのmarginはHTML要素のmarginとは独立しています。ページ余白の内側にコンテンツ領域が作られ、その中で通常のCSSが適用されます。
ページ擬似クラス ― :first, :left, :right, :blank
@page にはページの種類に応じたスタイルを適用できる擬似クラスがあります。
:first ― 最初のページだけ別レイアウト
表紙や請求書のヘッダー部分を特別扱いしたい場合に使います。
/* 全ページの基本設定 */
@page {
size: A4;
margin: 25mm 20mm;
}
/* 表紙は余白を大きく */
@page :first {
margin: 40mm 30mm;
}
:left / :right ― 見開き(左右)ページ
冊子や書籍のように、左ページと右ページで余白を変えたいときに使います。
/* 左ページ(偶数) */
@page :left {
margin-left: 30mm;
margin-right: 20mm;
}
/* 右ページ(奇数) */
@page :right {
margin-left: 20mm;
margin-right: 30mm;
}
綴じ代(のどの余白)を確保するために内側の余白を広くとるのが一般的です。
:blank ― 空白ページ
break-before: left や break-before: right によって挿入された空白ページに適用されます。
@page :blank {
@top-center {
content: ""; /* ヘッダーを非表示 */
}
}
名前付きページ ― 1つのドキュメントに複数レイアウト
page プロパティを使うと、要素ごとに異なるページ設定を割り当てられます。
/* 表紙用のページ定義 */
@page cover {
size: A4;
margin: 0;
}
/* 本文用のページ定義 */
@page content {
size: A4;
margin: 25mm 20mm;
}
/* 横向きの図表ページ */
@page landscape-chart {
size: A4 landscape;
margin: 15mm;
}
HTMLからは page プロパティで指定します。
.cover-page {
page: cover;
}
.main-content {
page: content;
}
.chart-section {
page: landscape-chart;
}
<div class="cover-page">
<h1>年次レポート 2026</h1>
</div>
<div class="main-content">
<h2>第1章 概要</h2>
<p>...</p>
</div>
<div class="chart-section">
<img src="chart.png" alt="売上推移" />
</div>
名前付きページを使うと、表紙は余白なし、本文はA4縦、図表ページはA4横、というように1つのHTMLから複数のレイアウトを持つPDFを生成できます。
マージンボックス ― ヘッダー・フッター・ページ番号
@page ルールの中にマージンボックスを定義すると、ページの余白領域にコンテンツを配置できます。これがPDFのヘッダーやフッター、ページ番号を実現する仕組みです。
マージンボックスの位置
@top-left @top-center @top-right
┌──────────────────────────────────────┐
│ │
@left-top @right-top
│ │
@left-middle @right-middle
│ コンテンツ領域 │
@left-bottom @right-bottom
│ │
└──────────────────────────────────────┘
@bottom-left @bottom-center @bottom-right
全部で16箇所のマージンボックスが定義されています。
ページ番号の追加
@page {
size: A4;
margin: 25mm 20mm 30mm 20mm;
@bottom-center {
content: counter(page);
font-size: 10pt;
color: #666;
}
}
/* 「3 / 12」形式 */
@page {
@bottom-right {
content: counter(page) " / " counter(pages);
font-size: 9pt;
}
}
counter(page) は現在のページ番号、counter(pages) は総ページ数を返します。
ヘッダーとフッターの例
@page {
size: A4;
margin: 30mm 20mm 25mm 20mm;
@top-left {
content: "株式会社サンプル";
font-size: 8pt;
color: #999;
}
@top-right {
content: "社外秘";
font-size: 8pt;
color: #c00;
font-weight: bold;
}
@bottom-left {
content: "2026年4月発行";
font-size: 8pt;
color: #999;
}
@bottom-right {
content: counter(page) " / " counter(pages);
font-size: 8pt;
}
}
/* 表紙にはヘッダー・フッターを出さない */
@page :first {
@top-left { content: ""; }
@top-right { content: ""; }
@bottom-left { content: ""; }
@bottom-right { content: ""; }
}
実践例 ― 表紙付き請求書
ここまでの知識を組み合わせて、表紙付きの請求書PDFを作ってみましょう。
/* === ページ設定 === */
@page {
size: A4;
margin: 20mm;
@bottom-center {
content: counter(page);
font-size: 9pt;
color: #888;
}
}
@page cover {
margin: 0;
@bottom-center { content: ""; }
}
@page :first {
@bottom-center { content: ""; }
}
/* === レイアウト === */
body {
font-family: "Noto Sans JP", sans-serif;
font-size: 10pt;
line-height: 1.6;
color: #333;
}
.cover {
page: cover;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: linear-gradient(135deg, #1a365d, #2563eb);
color: #fff;
text-align: center;
}
.cover h1 {
font-size: 28pt;
margin-bottom: 10mm;
}
/* 明細テーブル */
.invoice-table {
width: 100%;
border-collapse: collapse;
break-inside: avoid;
}
.invoice-table th,
.invoice-table td {
border: 1px solid #ddd;
padding: 8px 12px;
}
.invoice-table th {
background: #f5f5f5;
text-align: left;
}
/* 合計行 */
.total-row {
font-weight: bold;
font-size: 12pt;
break-before: avoid;
}
<div class="cover">
<div>
<h1>請求書</h1>
<p>2026年4月度</p>
</div>
</div>
<section class="invoice-body">
<h2>ご請求明細</h2>
<table class="invoice-table">
<thead>
<tr>
<th>項目</th>
<th>数量</th>
<th>単価</th>
<th>金額</th>
</tr>
</thead>
<tbody>
<tr><td>PDF生成API(Standardプラン)</td><td>1</td><td>¥5,000</td><td>¥5,000</td></tr>
<tr><td>追加リクエスト(100件)</td><td>2</td><td>¥1,000</td><td>¥2,000</td></tr>
</tbody>
<tfoot>
<tr class="total-row"><td colspan="3">合計(税込)</td><td>¥7,700</td></tr>
</tfoot>
</table>
</section>
このHTMLとCSSをFUNBREW PDFのAPIに送信すれば、表紙付きの請求書PDFが生成されます。
複数セクションレポートの例
レポートのように、表紙・目次・本文・付録でレイアウトを変えたい場合の例です。
@page cover {
size: A4;
margin: 0;
}
@page toc {
size: A4;
margin: 30mm 25mm;
@top-center {
content: "目次";
font-size: 9pt;
color: #666;
}
}
@page main {
size: A4;
margin: 25mm 20mm;
@top-left {
content: "月次レポート";
font-size: 9pt;
color: #666;
}
@bottom-right {
content: counter(page);
font-size: 9pt;
}
}
@page appendix {
size: A4 landscape;
margin: 15mm;
@bottom-center {
content: "付録 - " counter(page);
font-size: 8pt;
}
}
.cover { page: cover; }
.toc { page: toc; }
.main { page: main; }
.appendix { page: appendix; }
エンジンによる互換性の違い
@page ルールのサポート状況は、PDF生成エンジンによって大きく異なります。
| 機能 | Chromium (Headless) | wkhtmltopdf |
|---|---|---|
size |
○ | △(一部) |
margin |
○ | ○ |
:first |
○ | × |
:left / :right |
○ | × |
| 名前付きページ | ○ | × |
| マージンボックス | △(部分的) | × |
counter(page) |
△ | × |
counter(pages) |
△ | × |
Chromium ベースのエンジン
Chromium(Puppeteer、Playwright、FUNBREW PDFが使用)は @page の基本プロパティを幅広くサポートしています。ただし、マージンボックスのサポートは完全ではなく、Chrome 131以降で段階的に改善されています。
注意点: Chromiumの page.pdf() で format や margin オプションを指定すると、CSSの @page 設定が上書きされることがあります。@page で制御する場合は、API側のオプションを省略するか preferCSSPageSize: true を設定してください。
wkhtmltopdf
wkhtmltopdfはWebKitベースの古いエンジンで、@page のサポートは限定的です。size は --page-size コマンドラインオプション、ヘッダー・フッターは --header-html / --footer-html オプションで代替するのが一般的です。
エンジンの違いについて詳しくは「wkhtmltopdf vs Chromium 徹底比較」をご覧ください。
よくある間違いと対処法
1. 余白が二重になる
/* NG: @page の margin と body の margin が加算される */
@page { margin: 20mm; }
body { margin: 20mm; }
/* OK: body の margin をリセット */
@page { margin: 20mm; }
body { margin: 0; padding: 0; }
@page の margin がページ余白を確保するので、body に追加の margin を設定すると二重になります。
2. size が効かない
Chromiumベースのエンジンでは、APIの format オプションが @page { size } より優先されることがあります。
// NG: format 指定が @page を上書き
await page.pdf({ format: 'A4' });
// OK: CSS の @page に制御を任せる
await page.pdf({ preferCSSPageSize: true });
FUNBREW PDFでは、リクエストパラメータで preferCSSPageSize を指定できます。詳細はAPIドキュメントを確認してください。
3. ヘッダー・フッターが表示されない
マージンボックスはまだブラウザのサポートが限定的です。表示されない場合は以下の代替手段を検討してください。
/* 代替: position: fixed でヘッダー・フッターを実現 */
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 15mm;
font-size: 8pt;
color: #999;
border-bottom: 0.5pt solid #ddd;
}
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 10mm;
font-size: 8pt;
text-align: center;
color: #999;
}
@page {
margin-top: 25mm; /* ヘッダー分の余白を確保 */
margin-bottom: 20mm; /* フッター分の余白を確保 */
}
position: fixed はChromiumで安定して動作し、すべてのページに繰り返し表示されます。
4. 名前付きページで改ページされない
名前付きページを切り替える要素は、フローの中で兄弟要素である必要があります。
<!-- NG: ネストされていると改ページが発生しないことがある -->
<div class="wrapper">
<div class="cover-page">...</div>
<div class="main-content">...</div>
</div>
<!-- OK: トップレベルで並べる -->
<div class="cover-page">...</div>
<div class="main-content">...</div>
まとめ
CSS @page ルールは、PDFの物理的なページを制御するための専用仕様です。
- 基本:
sizeで用紙サイズ、marginで余白を設定 - 擬似クラス:
:first,:left,:rightでページごとに異なるスタイルを適用 - 名前付きページ: 1つのドキュメント内でレイアウトを切り替え
- マージンボックス: ヘッダー・フッター・ページ番号をCSS で配置
Chromiumベースのエンジンなら @page の主要機能がサポートされているため、CSSだけで高度なPDFレイアウトが実現できます。実際のCSSを試すにはPlaygroundが便利です。
PDFのCSS設計全般については「HTML→PDF CSS設計テクニック集」、PDF生成の全体像については「HTML to PDF完全ガイド」も参考にしてください。問題が起きた場合は「PDF生成トラブルシューティング」をご覧ください。