2026/05/05

CSSプリントスタイルシート完全ガイド|@page・@media print・PDF出力

CSSPDF生成印刷スタイルシートレイアウトチュートリアル

HTMLからPDFを生成するとき、「背景色が消える」「改ページが意図しない場所に入る」「テーブルヘッダーが2ページ目以降に表示されない」「@pageのマージンが効かない」といった問題によく直面します。このガイドでは、ChromiumのプリントCSSに関するすべての技法を、コピペできるスニペット付きでまとめます。

FUNBREW PDF・Puppeteer・Playwright のいずれを使う場合も、CSSエンジンは同じChromiumのプリントパイプラインです。ここで紹介するルールはすべてに適用されます。

@pageとページ区切りのCSSに特化した問題についてはHTML to PDF CSSガイドを参照してください。CJKフォントの埋め込みは日本語フォントガイドで解説しています。レイアウト崩れや空白ページのトラブルシューティングはHTML to PDF トラブルシューティングをご覧ください。

2つのCSSコンテキスト:@media print vs @page

どのCSSルールがどこに適用されるかを理解することが最初のステップです。

@media print @page
用途 スタイルをPDF/印刷コンテキストにスコープ 用紙の物理的な設定
制御対象 フォント・色・表示・レイアウト サイズ・向き・マージン
CSS変数サポート あり なし
入れ子関係 @pageを含められる 単独または@media printの中
@media print { .nav { display: none } } @page { size: A4; margin: 20mm }

プリントスタイルシートは以下の順序で構造化するのが推奨パターンです。

/* 1. @media printですべてをスコープ */
@media print {

  /* 2. @pageで用紙の物理設定 */
  @page {
    size: A4 portrait;
    margin: 20mm 15mm 25mm 15mm; /* 上 右 下 左 */
  }

  /* 3. 画面用スタイルを印刷用に上書き */
  body {
    font-size: 11pt;
    color: #000;
  }

  /* 4. 印刷不要な要素を非表示 */
  .no-print,
  nav,
  .sidebar {
    display: none !important;
  }
}

@page:サイズ・向き・マージン

定義済み用紙サイズ

@page { size: A4 portrait; }     /* 210mm × 297mm 縦 */
@page { size: A4 landscape; }    /* 297mm × 210mm 横 */
@page { size: Letter portrait; } /* 8.5in × 11in */
@page { size: Legal; }           /* 8.5in × 14in */
@page { size: A3 landscape; }    /* 420mm × 297mm */

カスタムサイズ

@page {
  size: 210mm 297mm; /* 幅 高さ を明示 */
  margin: 20mm;
}

ページごとのマージン制御

@page          { margin: 20mm 15mm; }         /* 全ページ */
@page :first   { margin-top: 40mm; }          /* 表紙(1ページ目) */
@page :left    { margin-left: 25mm; }         /* 偶数ページ(製本余白) */
@page :right   { margin-right: 25mm; }        /* 奇数ページ(製本余白) */

注意: @pageルール内ではCSSカスタムプロパティ(var())は機能しません。ハードコードした値を使ってください。

改ページ:break-before・break-after・break-inside

モダンなbreak-*プロパティと、レガシーなpage-break-*の両方を書くのが基本です。

セクション前で強制改ページ

.chapter,
.report-section,
h1 {
  break-before: page;
  page-break-before: always; /* レガシーフォールバック */
}

ブロック内での改ページを禁止

.card,
.figure,
.keep-together {
  break-inside: avoid;
  page-break-inside: avoid; /* レガシーフォールバック */
}

表紙ページの後で強制改ページ

.cover {
  break-after: page;
  page-break-after: always;
  height: 100vh; /* 1ページ全体を埋める */
}

Flex・Gridレイアウトの落とし穴

break-inside: avoidをflex/gridの子要素に直接適用すると無視されることがあります。ブロック<div>でラップしてください。

<!-- OK: ブロック要素でラップ -->
<div class="grid-wrapper">
  <div class="card-wrapper"> <!-- break-inside: avoid をここに -->
    <div class="card">...</div>
  </div>
</div>
.card-wrapper {
  break-inside: avoid;
  page-break-inside: avoid;
}

背景色・背景画像

全要素にグローバル適用

@media print {
  * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

要素ごとに適用

.colored-header,
.status-badge,
.chart-background {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

背景画像

.banner {
  background-image: url('/img/logo.png');
  background-size: cover;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

FUNBREW PDFを使う場合は、APIオプションでprintBackground: trueを渡すことでCSSを書かずにグローバルに有効化できます。

{
  "options": {
    "printBackground": true
  }
}

テーブル:ヘッダーの繰り返しと行分割の防止

テーブルヘッダーを全ページで繰り返す

thead {
  display: table-header-group;
}

tfoot {
  display: table-footer-group;
}

tbody {
  display: table-row-group;
}
<table>
  <thead>
    <tr>
      <th>名前</th><th>金額</th><th>日付</th>
    </tr>
  </thead>
  <tbody>
    <!-- 複数ページにまたがる行 -->
  </tbody>
</table>

行の途中で改ページを防ぐ

tr {
  break-inside: avoid;
  page-break-inside: avoid;
}

安定したカラム幅のための固定テーブルレイアウト

table {
  table-layout: fixed;
  width: 100%;
  border-collapse: collapse;
}

th, td {
  word-wrap: break-word;
  overflow-wrap: break-word;
}

フォント:読み込みと埋め込み

システムフォントスタック(外部リクエストなし)

body {
  font-family: -apple-system, 'Segoe UI', Arial, sans-serif;
}

@font-faceでセルフホスティング(CDN不要)

@font-face {
  font-family: 'Noto Sans JP';
  src: url('/fonts/NotoSansJP-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: block; /* フォント読み込みまで待つ */
}

body {
  font-family: 'Noto Sans JP', sans-serif;
}

Base64埋め込みフォント(ネットワーク依存なし)

@font-face {
  font-family: 'MyFont';
  src: url('data:font/woff2;base64,AAABAA...') format('woff2');
}

Base64でフォントを埋め込むとCDN依存がなくなり、PDF生成で最も安定した方法です。日本語フォント(Noto Sans JP)の埋め込みは日本語フォントガイドで詳しく解説しています。

Google Fonts(CDN経由・ネットワークが必要)

<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=block" rel="stylesheet">

FUNBREW PDFを使う場合はwaitForNetworkIdle: trueを追加して、フォント読み込み完了後にPDFを生成します。

{
  "options": {
    "waitForNetworkIdle": true
  }
}

ページ番号とヘッダー・フッター

FUNBREW PDF の headerTemplate / footerTemplate

{
  "options": {
    "displayHeaderFooter": true,
    "headerTemplate": "<div style='font-size:9pt; color:#888; padding:0 15mm; width:100%; display:flex; justify-content:space-between;'><span>月次レポート</span><span class='date'></span></div>",
    "footerTemplate": "<div style='font-size:9pt; color:#888; padding:0 15mm; width:100%; text-align:center;'>ページ <span class='pageNumber'></span> / <span class='totalPages'></span></div>",
    "margin": { "top": "25mm", "bottom": "20mm" }
  }
}

テンプレート文字列で使えるChromium組み込みクラス:

クラス 出力
<span class='pageNumber'> 現在のページ番号
<span class='totalPages'> 総ページ数
<span class='date'> 現在の日付
<span class='title'> ドキュメントのtitle
<span class='url'> ドキュメントのURL

印刷時の要素非表示

よく非表示にする要素

@media print {
  /* ナビゲーションとサイドバー */
  nav,
  header.site-header,
  footer.site-footer,
  .sidebar,
  .breadcrumb,
  .pagination,

  /* インタラクティブ要素 */
  button,
  input,
  select,
  textarea,
  form,

  /* ユーティリティクラス */
  .no-print,
  .screen-only {
    display: none !important;
  }
}

印刷専用要素を表示

.print-only {
  display: none;
}

@media print {
  .print-only {
    display: block;
  }
}

PDFのためのレイアウトパターン

フルブリード表紙ページ

@page :first {
  margin: 0;
}

.cover-page {
  width: 210mm;
  height: 297mm;
  padding: 40mm 30mm;
  background: #1e3a5f;
  color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  break-after: page;
}

2カラムレイアウト(印刷対応)

.two-column {
  column-count: 2;
  column-gap: 10mm;
  column-rule: 1px solid #e2e8f0;
}

/* 見出しをカラムをまたいで分割させない */
h2, h3 {
  break-after: avoid;
  column-span: none;
}

請求書・データテーブルのレイアウト

.invoice-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 10pt;
}

.invoice-table th {
  background: #1e3a5f;
  color: #fff;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
  padding: 3mm 4mm;
  text-align: left;
}

.invoice-table td {
  padding: 2.5mm 4mm;
  border-bottom: 0.5px solid #e2e8f0;
}

.invoice-table tr:nth-child(even) td {
  background: #f8fafc;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

FUNBREW PDF APIとの組み合わせ

上記のCSSを組み合わせたHTMLをAPIに送信します。

import fs from 'fs';

const html = fs.readFileSync('report.html', 'utf-8');

const response = await fetch('https://pdf.funbrew.cloud/api/v1/generate', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.FUNBREW_PDF_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    html,
    options: {
      format: 'A4',
      printBackground: true,    // 背景色・画像を有効化
      waitForNetworkIdle: true, // Webフォント・CDNの読み込みを待つ
      displayHeaderFooter: true,
      headerTemplate: '<div style="font-size:9pt;padding:0 15mm;width:100%;color:#888;">月次レポート</div>',
      footerTemplate: '<div style="font-size:9pt;padding:0 15mm;width:100%;text-align:right;color:#888;">ページ <span class="pageNumber"></span></div>',
      margin: { top: '25mm', right: '15mm', bottom: '20mm', left: '15mm' },
    },
  }),
});

const pdf = Buffer.from(await response.arrayBuffer());
fs.writeFileSync('output.pdf', pdf);
import requests, os

with open('report.html', 'r') as f:
    html = f.read()

response = requests.post(
    'https://pdf.funbrew.cloud/api/v1/generate',
    headers={'Authorization': f'Bearer {os.environ["FUNBREW_PDF_API_KEY"]}'},
    json={
        'html': html,
        'options': {
            'format': 'A4',
            'printBackground': True,
            'waitForNetworkIdle': True,
            'displayHeaderFooter': True,
            'headerTemplate': '<div style="font-size:9pt;padding:0 15mm;width:100%;color:#888;">月次レポート</div>',
            'footerTemplate': '<div style="font-size:9pt;padding:0 15mm;width:100%;text-align:right;color:#888;">ページ <span class="pageNumber"></span></div>',
            'margin': {'top': '25mm', 'right': '15mm', 'bottom': '20mm', 'left': '15mm'},
        },
    },
)
response.raise_for_status()
with open('output.pdf', 'wb') as f:
    f.write(response.content)

実際のHTMLをコードを書く前にFUNBREW PDF Playgroundでテストできます。

クイックリファレンス:25のCSSプリントスニペット

問題 対処
背景色が消える print-color-adjust: exact
背景画像が消える 要素にprint-color-adjust: exact
セクション前で改ページ break-before: page; page-break-before: always
ブロック内の改ページ禁止 break-inside: avoid; page-break-inside: avoid
表紙後で改ページ break-after: page; page-break-after: always
テーブルヘッダーを全ページで繰り返す thead { display: table-header-group }
テーブルフッターを全ページで繰り返す tfoot { display: table-footer-group }
行を分割させない tr { break-inside: avoid }
カラム幅を固定 table { table-layout: fixed }
カスタム用紙サイズ @page { size: A4 landscape }
カスタムマージン @page { margin: 20mm 15mm }
表紙のマージン変更 @page :first { margin-top: 40mm }
ナビをPDFで非表示 @media print { nav { display: none } }
フォント確実に読み込む ローカルWOFF2の@font-face
CDN経由フォント APIオプションにwaitForNetworkIdle: true
ページ番号 footerTemplateで<span class='pageNumber'>
総ページ数 footerTemplateで<span class='totalPages'>
フルブリード表紙 @page :first { margin: 0 }
2カラムレイアウト column-count: 2; column-gap: 10mm
Flex改ページ修正 子要素をブロックdivでラップ
背景をグローバル有効化 APIオプションにprintBackground: true
フォント読み込み待ち APIオプションにwaitForNetworkIdle: true
ヘッダーテンプレート displayHeaderFooter: true + headerTemplate
フッターテンプレート displayHeaderFooter: true + footerTemplate

関連ガイド

Powered by FUNBREW PDF