Стилі друку для документації, що не бентежать

Було корисно?

Хтось десь саме зараз роздруковує вашу документацію. Можливо, це аудитор з комплаєнсу. Можливо, черговий інженер у кімнаті без вікон з обмеженим ноутбуком і принтером, що пахне 2009 роком.
У будь-якому випадку ваш гамбургер-меню і липкий банер про файли cookie не повинні бути увічнені на папері.

Це практичний посібник із CSS для друку, орієнтований на продукцію: читаємий макет, приховування навігації, перенесення коду без перетворення його на кашу і відлагодження друку так само, як ви відлагоджуєте ненадійний сервіс.

Як виглядає «добрий друк» (і чого він відмовляється робити)

Друк — найсуворіший ревізор, якого ви коли-небудь зустрінете. Він позбавляє інтерактивності, звужує полотно до фіксованих розмірів і карає все, що залежить від hover-станів, фіксованого позиціонування або «просто трохи прокрутити».
Мета не в тому, щоб папір виглядав як ваш сайт. Мета — щоб папір поводився як папір: передбачувано, легко сканувався, читабельно та без сміття інтерфейсу.

Хороша стилізація для друку добре виконує чотири завдання:

  • Зменшує шум: ховає навігацію, поля пошуку, «схожі пости» та все, що пахне метриками залучення.
  • Покращує читабельність: адекватні розміри шрифту, поля, міжрядковий інтервал і контраст.
  • Поважає структуру: заголовки не залишаються внизу сторінки; код не обрізається; таблиці не руйнуються.
  • Зберігає зміст: посилання показують призначення; ілюстрації мають підписи; код переноситься без обману.

Суб’єктивне зауваження: якщо ваша документація має оперативну значущість (runbook-и, інструкції при інцидентах, процедури комплаєнсу), друк — це не «приємний бонус». Це частина вашого плану доступності.

Жарт №1: CSS для друку схожий на бекапи — всі клянуться, що мають їх, доки не спробують відновити.

Ще одна річ: «друк-дружній» не означає «одноколонкова купа дуже дрібного тексту». Ви можете використовувати білий простір. Папір не оплачується за пікселі.

Цікаві факти та історичний контекст

Факт 1: CSS2 запровадив концепцію @media наприкінці 1990-х, включно з print як цільовою медією. Підтримка друку не нова; проблема в нашому доведенні справи до кінця.

Факт 2: Ранні рушії друку в браузерах часто перераховували сторінки, використовуючи інші алгоритми верстки, ніж для екрана, тому «в браузері виглядає нормально» ніколи не було гарантією.

Факт 3: Правило @page походить з вимог до сторінкового медіа (уявіть видавничу сферу). Підтримка браузерів неповна, але поля зазвичай шануються.

Факт 4: «Вдівці» і «сироти» — терміни набору тексту з друкарського світу. CSS підтримує їх не випадково: читачі на папері ненавидять самотні рядки.

Факт 5: Багато корпоративних робочих процесів «друку в PDF» насправді — це «рендеринг headless Chromium». Отже ваш CSS для друку — це також API експорту в PDF.

Факт 6: A4 і Letter настільки різняться, що хардкодні піксельні ширини дають кумедні помилки в різних регіонах. Ваш макет для друку потребує реальних полів, а не «все вміщується на моєму ноутбуці».

Факт 7: Деякі браузери навмисно не друкують фонові кольори/зображення, поки користувач явно не дозволить це, що може стерти інтерфейс, який залежить від контрасту. Не покладайтеся на фони для передачі значень.

Факт 8: Принтери та генератори PDF можуть растризувати складні сторінки, перетворюючи вибірковий текст на зображення. Важкі ефекти, великі тіні та певні SVG-фільтри можуть це спричинити.

Ставтеся до цього як до обмежень, а не цікавих фактів. Саме з обмежень починається хороша інженерія.

Основні принципи: друк — це окремий продукт

1) Починайте з контенту, а не з макета

Коли друкуєте, контент стає інтерфейсом. «Дизайн-система» має в основному зникати. Отже ваш CSS для друку має будуватися навколо семантичного HTML:
заголовки — це заголовки, списки — списки, код — код, таблиці — таблиці. Якщо ваш сайт — купа div-елементів, друк покарає вас за гріхи. Це не особисто. Це фізика.

2) Видаляйте все, що змінює стан

Липкі заголовки, акордеони, вкладки, підказки по hover, банери про файли cookie — це станні UI-патерни. Друк — безстанний. Якщо надрукований документ залежить від кліку «Розгорнути», ви випустили runbook, який потребує електрики та оптимізму.

3) Зробіть друкований артефакт самодостатнім

Друковані сторінки втрачають контекст домену. Читач може не знати назви сайту, заголовка сторінки або дати останнього оновлення. Включіть достатньо метаданих у зону контенту — заголовок, опціональну версію і, можливо, невеликий футер — але не відтворюйте весь хедер.

4) Оптимізуйте для двох аудиторій: людей і аудитів

Людям потрібен читабельний текст, переносний код і без зайвих сторінок. Аудити хочуть стабільні артефакти. Якщо ваш друк змінюється щоразу, коли змінюється посилання в навігації, ви створюєте безпідставну мінливість.

Корисна ідея від John Allspaw у парафразі: стабільність приходить, коли системи проектуються з урахуванням ймовірності відмов, а потім зменшується зона ураження, коли відмова відбувається.

Читаємий друк: типографіка, поля та структура

Поля: обирайте нудні числа

Використовуйте @page поля в дюймах або міліметрах і тримайте їх консервативними. Принтери мають непечатні зони; PDF — ні, але користувачі друкують PDF на принтерах з непечатними зонами.
Якщо вміст торкається краю, він буде обрізаний. Не «може». Буде.

Робіть так: @page { margin: 0.6in; }. Не робіть: margin: 0 і сподівайтеся, що принтер сучасний і доброзичливий.

Розміри шрифтів: друк — не retina

Для основного тексту 10.5–12pt зазвичай підходить. Для щільної технічної документації 11pt — хороший стандарт. Для коду 8.5–10pt залежно від довжини рядка. Варто тільки зменшити, щоб «вмістити більше», як читачі починають проскакувати замість читати.

Довжина рядка: тиранія 100 символів

Папери і PDF не скроляться. Тримайте абзаци читабельними: уникайте надшироких рядків, використовуючи поля, а не піксельні max-width. У друку «повна ширина» сторінки вже обмежена. Дайте їй дихати.

Контраст: покінчіть із сірим на сірому

Стилі для друку повинні забезпечувати високий контраст. Багато компаній люблять світло-сірий текст на трохи іншому сірому фоні.
Потім хтось друкує це на бюджетному принтері, і ваша «нотатка» стає невидимим привидом. У друку, якщо це важливо — це чорне.

Заголовки: не дозволяйте їм «зависати»

Використовуйте break-after: avoid (і старий запасний page-break-after: avoid) для заголовків. Також захищайте перший абзац після заголовка за допомогою обгортки, або хоча б уникайте ситуації, коли заголовок опиняється внизу сторінки без контенту.

Принцип: якщо H2 друкується як останній рядок на сторінці, ваша стилізація для друку не виконує свою єдину задачу.

Приховування навігації, оболонки та непотрібного UI

Ховайте за класом, а не надією

Потрібні явні селектори для елементів, які не повинні друкуватися. Створіть утилітний клас на кшталт .no-print і застосуйте його до:
панелей навігації, футерів з юридичним текстом, бічних панелей, кнопок «редагувати сторінку», віджетів шарингу, коментарів та банерів cookie.

Віддавайте перевагу «display: none» у друку

Для друку display: none — ваш друг. Не використовуйте трюки з прозорістю. Не використовуйте позаекранне позиціонування. Деякі рушії друку все ще обчислюють макет для елементів, які приховані, але не мають display:none, і ви отримаєте таємничі порожні області.

Фіксовані та липкі елементи «з’їдають» сторінки

Фіксований хедер висотою 72px стає повторюваним податком на кожній надрукованій сторінці, якщо браузер вирішить рендерити його для кожної сторінки. Так ви перетворюєте 10-сторінковий runbook на 17-сторінковий і змушуєте офісний принтер плакати.

Жарт №2: Нічого так не видає «корпорацію», як друк 40-сторінкового гайду, де 12 сторінок — це лише липкий хедер.

Блоки коду: перенесення, переповнення, достовірність копіювання

Гірка правда: код не любить папір

Вивід терміналу та блоки коду створені для горизонтальної прокрутки. Папір відмовляється це робити. Ваш вибір:

  • Переносити рядки і прийняти, що точність копіювання не є головною метою на папері.
  • Не переносити, але тоді потрібно забезпечити, щоб вміст не обрізався (часто неможливо).
  • Надати варіант, безпечний для друку (рекомендовано): за замовчуванням переносити, але дозволити критичним командам мати ручні символи продовження або явні розриви рядків у джерелі.

Переносьте блоки pre у друці

Для друку використовуйте white-space: pre-wrap для pre і code. Додайте word-break: break-word. Це запобігає обрізанню і відсутнім символам.

Не використовуйте white-space: normal для коду. Воно згортає пробіли і може зіпсувати приклади коду. Вам потрібен перенос, а не повторне токенізування.

Зберігайте підказки та маркери

Якщо у вашій документації є команди, зберігайте підказки узгодженими. Для друку підказки допомагають людям розрізняти, що введено, а що — вивід.
Але не робіть підказки величезними або стилізованими як фонова відмітка, яка не буде друкуватися.

Колір і підсвітка синтаксису

Багато пайплайнів для друку втрачають фон. Підсвітка синтаксису також може стати низькоконтрастною. Для друку віддавайте перевагу простому:
чорний текст, легка рамка, можливо світло-сірий фон, якщо він не критичний.
Код має залишатися читабельним без кольору.

Не допускайте розриву коду в середині рядка, якщо можна уникнути

Використовуйте break-inside: avoid для pre. Це не ідеально в усіх рушіях, але зменшує найгірші випадки.
Для довгих виводів розриви неминучі — тому переконайтеся, що ваші команди містять контекст і точки відновлення.

Таблиці, діаграми та зображення без сліз

Таблиці: або вміщайте, або переробляйте

Широкі таблиці — головне джерело проблем при друку. Якщо таблиця ширша за сторінку, у вас є три варіанти:

  • Зробити таблицю вужчою, переробивши вміст (найкраще в довгостроковій перспективі).
  • Дозволити агресивне обтікання клітинок (іноді підходить для прозових даних, погано для ідентифікаторів).
  • Друкувати таблицю в альбомній орієнтації (важко у браузерному друці; здійсненно у спеціалізованих PDF-інструментах).

Не намагайтеся «масштабувати сторінку», щоб вмістити таблицю. Це призводить до дрібного тексту і розлючених людей.

Зображення: видаляйте декоративні, залишайте інформативні

Декоративні зображення не мають друкуватися. Інформативні зображення повинні друкуватися з достатнім дозволом, щоб залишатися читаємими. Встановіть:
img { max-width: 100%; height: auto; }

Якщо ви покладаєтесь на діаграми з кольоровим кодуванням, додайте підписи. Принтери не зобов’язані відтворювати вашу бренд-палітру. Вони часто відтворюють «якийсь сірий сьогодні».

SVG і фільтри

Складні SVG-фільтри можуть спричинити растризацію. Коли це відбувається, текст стає нечітким і не виділяється у PDF.
Якщо у ваших документах є SVG-діаграми з фільтрами розмиття/тіней, розгляньте запасний SVG для режиму друку без фільтрів.

Розриви сторінок, сироти, вдівці та уникнення незручних розривів

Використовуйте сучасні властивості розривів, залиште старі запасні варіанти

Віддавайте перевагу break-before, break-after та break-inside. Додайте старі page-break-* для сумісності.
Браузери різняться, але ці властивості — ваш найкращий шанс контролювати пагінацію.

Захищайте заголовки, короткі списки та визначення

Заголовок внизу сторінки — класична невдача. Так само і двоелементний список, розірваний між сторінками. Застосуйте:

  • h2, h3 { break-after: avoid; }
  • pre, table, blockquote { break-inside: avoid; }
  • li { break-inside: avoid; } (використовуйте обережно; великі списки змушуватимуть нерівномірні пробіли)

Orphans і widows: варто використовувати

orphans і widows не завжди ідеально впроваджуються, але вони допомагають. Встановіть їх для абзаців і елементів списків.
Ви не намагаєтеся верстати роман. Ви намагаєтеся припинити явище «один рядок на наступній сторінці».

Друк у PDF в конвеєрах: відтворюваність і дрейф

У багатьох організаціях «друк» насправді означає «генерувати PDF для розповсюдження». Це прийнятно — поки це не стає частиною робочого процесу комплаєнсу, артефакта релізу або пакета підтримки.
Тоді ви виявляєте, що ваш PDF змінюється, бо headless-браузер оновився, веб-шрифт не завантажився або CSS правило було обрізане під час збірки.

Зафіксуйте рушій рендерингу

Якщо ви генеруєте PDF у CI, зафіксуйте версію Chromium (або тег контейнера) і ставтеся до неї як до будь-якої іншої залежності.
Неприв’язаний «latest» — джерело сюрпризів напередодні аудиту.

Вимкніть нестабільні залежності

Веб-шрифти можуть не завантажитися. Віддалені зображення можуть упасти. Зовнішні скрипти можуть підвести. Для друку/PDF використовуйте локальні ресурси або інлайн критичний CSS.
Друкований артефакт не повинен залежати від п’яти CDN і доброго дня резольвера DNS.

Проектуйте для детермінованої пагінації

Невеликі відмінності в макеті можуть змінити розриви сторінок, що змінює все (наприклад, номери сторінок у SOP).
Якщо вам потрібна детермінована пагінація, розгляньте генерування PDF із джерела за допомогою інструментів для сторінкового медіа або зафіксуйте CSS і шрифти.

Практичні завдання: команди, виводи, рішення

Стилі друку дають збій двома шляхами: ваш CSS неправильний або ваш конвеєр брешe про те, що саме друкується.
Ось реальні завдання, які я використовую для відлагодження друку як SRE: перевірити вхідні дані, спостерігати поведінку та вирішити, що змінити.

Завдання 1: Підтвердити, який CSS фактично відвантажується

cr0x@server:~$ curl -sS -D- https://docs.example.internal/article/runbook.html -o /tmp/runbook.html
HTTP/2 200
content-type: text/html; charset=utf-8
etag: "9b3b2e9a"
cache-control: max-age=300

Що означає вивід: Ви отримали відрендерений HTML і заголовки відповіді. ETag і кешування підкажуть, чи не бачите ви застарілий контент.

Рішення: Якщо кешування агресивне, очистіть або обійдіть його, перш ніж звинувачувати CSS.

Завдання 2: Витягнути підключені стилі (швидка перевірка)

cr0x@server:~$ grep -Eo 'href="[^"]+\.css[^"]*"' /tmp/runbook.html | head
href="/assets/site.css"
href="/assets/print.css"

Що означає вивід: Сторінка містить окремий CSS для друку, а не лише стилі для екрана.

Рішення: Якщо print CSS відсутній, створіть його. Якщо є — завантажте його і перевірте, що це та версія, що розгорнута.

Завдання 3: Отримати print CSS і перевірити наявність правил для друку

cr0x@server:~$ curl -sS https://docs.example.internal/assets/print.css | sed -n '1,80p'
@media print {
  .site-nav, .sidebar, .cookie-banner { display: none !important; }
  pre { white-space: pre-wrap; word-break: break-word; }
}

Що означає вивід: Є блок @media print, і він виконує базові дії.

Рішення: Якщо правила друку відсутні або порожні — ви покладаєтесь на стилі екрану. Зупиніться і виправте це перш за все.

Завдання 4: Виявити випадкове «display: none» на основному вмісті

cr0x@server:~$ rg -n "display:\s*none" /tmp/runbook.html /tmp/print.css
/tmp/print.css:2:  .site-nav, .sidebar, .cookie-banner { display: none !important; }

Що означає вивід: Приховано лише UI. Добре.

Рішення: Якщо ви знайдете main, article або ваш контейнер контенту прихованими — виправте селектори (часто занадто широкий селектор, як div або *).

Завдання 5: Згенерувати PDF через headless Chromium для відтворення проблем

cr0x@server:~$ chromium --headless --disable-gpu --print-to-pdf=/tmp/runbook.pdf https://docs.example.internal/article/runbook.html
[1229/102312.184556:INFO:headless_shell.cc(659)] Written to file /tmp/runbook.pdf.

Що означає вивід: У вас тепер відтворюваний PDF-артефакт від відомого рушія.

Рішення: Якщо PDF відрізняється від «Друку…» у десктопному Chrome, ймовірно, у вас є дрейф середовища (шрифти, DPI або різні версії Chromium).

Завдання 6: Перевірити, чи PDF містить текст чи растрові зображення

cr0x@server:~$ pdftotext /tmp/runbook.pdf - | head
Runbook: Storage Failover
Last updated: 2025-12-01

1. Preconditions
...

Що означає вивід: Текстова екстракція працює; PDF здебільшого містить реальний текст.

Рішення: Якщо екстракція не дає нічого або дає сміття, ваш конвеєр може растризувати. Зменшіть візуальні ефекти і перевірте вбудовані шрифти/SVG-фільтри.

Завдання 7: Перевірити вбудовані шрифти (відсутні шрифти викликають дрейф)

cr0x@server:~$ pdffonts /tmp/runbook.pdf | head
name                                 type              encoding         emb sub uni object ID
AAAAAA+Inter-Regular                  TrueType          WinAnsi          yes yes yes      12  0
AAAAAA+Inter-SemiBold                 TrueType          WinAnsi          yes yes yes      13  0

Що означає вивід: Шрифти вбудовані і підмножені. Це добре для портативності і стабільності макету.

Рішення: Якщо шрифти не вбудовані, очікуйте дрейф макету на різних машинах. Вбудовуйте або використовуйте системні шрифти в режимі друку.

Завдання 8: Перевірити кількість сторінок і виявити регресії з порожніми сторінками

cr0x@server:~$ pdfinfo /tmp/runbook.pdf | egrep 'Pages|Page size'
Pages:           14
Page size:       612 x 792 pts (letter)

Що означає вивід: У вас 14 сторінок у форматі Letter. Це стабільна базова метрика для відстеження в CI.

Рішення: Якщо кількість сторінок раптово зростає (наприклад, з 14 до 19), підозрюйте фіксовані/липкі елементи, примусові розриви або зміну шрифта.

Завдання 9: Підтвердити, що довгі рядки коду переносяться (не обрізаються)

cr0x@server:~$ pdftotext /tmp/runbook.pdf - | rg -n "cr0x@server:~\$" | head
42:cr0x@server:~$ zpool status -v pool0
77:cr0x@server:~$ journalctl -u nginx --since "1 hour ago"

Що означає вивід: Команди присутні в екстрактованому тексті, отже вони не були обрізані на краю сторінки.

Рішення: Якщо екстрактований текст відсутній для довгих команд, перегляньте перенесення pre і поля.

Завдання 10: Підтвердити, що застосовано медіа print (відлагодження CSS)

cr0x@server:~$ node -e 'const fs=require("fs"); const css=fs.readFileSync("/tmp/print.css","utf8"); console.log(/@media\s+print/.test(css)?"has print media":"missing print media");'
has print media

Що означає вивід: Ваш CSS містить правила для друку.

Рішення: Якщо відсутні, ваш збірник міг їх обрізати. Виправте конфігурацію збірки.

Завдання 11: Виявити непечатні елементи, що залежать від фону

cr0x@server:~$ rg -n "background-color|color:\s*#(7|8|9|a|b|c|d|e|f){3,6}" /tmp/print.css | head
14:  .callout { border-left: 4px solid #333; background-color: #f6f6f6; }

Що означає вивід: Ви використовуєте фоновий колір для викликів уваги. Це прийнятно, але не покладайтеся на фон як на єдиний спосіб передачі значення.

Рішення: Додайте рамки, ярлики або типографіку, щоб виклик уваги був очевидним, коли фони не друкуються.

Завдання 12: Перевірити примусові розриви сторінок, які створюють порожні сторінки

cr0x@server:~$ rg -n "page-break-before|page-break-after|break-before|break-after" /tmp/print.css
22:  h1 { break-before: page; }

Що означає вивід: Є примусовий розрив перед h1. Це може створювати порожню першу сторінку в деяких рушіях.

Рішення: Приберіть його або звужте область дії (наприклад, тільки в «режимі друку пакета»). Перерахуйте кількість сторінок знову.

Завдання 13: Переконатися, що nav/sidebar дійсно зникають у надрукованому PDF

cr0x@server:~$ pdftotext /tmp/runbook.pdf - | rg -n "Search|Sign in|Subscribe|Cookie" | head

Що означає вивід: У надрукованому артефакті немає очевидних фраз інтерфейсу.

Рішення: Якщо UI-текст з’являється, ваші селектори не відповідають виробничому DOM. Додайте стабільні класи (.site-nav, .cookie-banner) і уникайте крихкого CSS на кшталт nav > ul > li.

Завдання 14: Переконатися, що той самий макет рендериться на A4 і Letter (перевірка дрейфу розміру сторінки)

cr0x@server:~$ chromium --headless --disable-gpu --print-to-pdf=/tmp/runbook-a4.pdf --virtual-time-budget=10000 --run-all-compositor-stages-before-draw https://docs.example.internal/article/runbook.html
[1229/102409.918211:INFO:headless_shell.cc(659)] Written to file /tmp/runbook-a4.pdf.

Що означає вивід: У вас додатковий PDF-артефакт для порівняння. (Контроль розміру сторінки в Chromium обмежений; багато команд використовують інші інструменти для A4/Letter.)

Рішення: Якщо A4 vs Letter призводить до обрізання вмісту, збільшіть поля і припиніть використовувати фіксовані ширини.

Завдання 15: Ловіть регресії CSS у CI, зафіксувавши бюджет по кількості сторінок

cr0x@server:~$ pdfinfo /tmp/runbook.pdf | awk -F': *' '/Pages/ {print $2}'
14

Що означає вивід: Одне число, яке ви можете порівнювати між збірками.

Рішення: Якщо кількість сторінок несподівано змінюється після зміни CSS, сприймайте це як регресію і перевіряйте diff змін, а не лише HTML.

План швидкої діагностики

Коли друк неправильний, не починайте з «підкручування CSS, поки не виглядатиме краще».
Саме так ви отримуєте тисячерядковий print stylesheet, якому ніхто не довіряє.
Діагностуйте як оператор: перевірте кілька речей, які зазвичай ламаються першими.

Перше: чи застосовується стилізація для друку взагалі?

  • Відкрийте попередній перегляд друку і перевірте, чи зникає навігація/бічні панелі.
  • Якщо ні — ваш @media print не завантажується, його обрізано або селектори не відповідають DOM.

Друге: чи ви боретеся з фіксованим/липким позиціонуванням?

  • Симптоми: повторювані хедери, марна пустота, зміщений вниз контент, вибух кількості сторінок.
  • Виправлення: у друку встановіть для фіксованих/липких контейнерів position: static і сховайте зайву оболонку.

Третє: чи блоки коду обрізаються або погано переносяться?

  • Симптоми: відсутні символи праворуч, обрізані команди або блок коду на п’ять сторінок із розірваними словами.
  • Виправлення: pre { white-space: pre-wrap; word-break: break-word; } і переконайтеся, що поля ненульові.

Четверте: чи посилання мають зміст на папері?

  • Симптоми: всюди «натисни тут», посилання, які неможливо відстежити.
  • Виправлення: додайте зовнішні URL через attr(href); уникайте додачі для внутрішніх якорів.

П’яте: чи розриви сторінок саботують читабельність?

  • Симптоми: заголовки поодинці внизу сторінки; розірвані таблиці; елементи списків відокремлені від маркерів.
  • Виправлення: break-inside: avoid для блоків; break-after: avoid для заголовків; налаштуйте widows/orphans.

Оперативна підказка: зробіть знімок «відомо-доброго» PDF і порівнюйте його щоразу, коли змінюєте CSS. Люди погано помічають тонкі переробки в попередньому перегляді друку.

Типові помилки (симптом → корінь → виправлення)

1) Симптом: у надрукованих сторінках є навігація, пошук і банер cookie

Корінь: немає стилю для друку, або селектори спрямовані на стару структуру DOM, яка змінилася під час редизайну.

Виправлення: введіть стабільні класи (.site-nav, .cookie-banner, .sidebar) і ховайте їх у @media print з display: none !important.

2) Симптом: великі порожні ділянки, особливо вгорі кожної сторінки

Корінь: фіксований/липкий хедер залишився в друку; браузер повторює його на кожній сторінці або резервує під нього місце.

Виправлення: у друку встановіть контейнер хедера на position: static !important або сховайте його повністю. Перенесіть потрібні метадані в тіло статті.

3) Симптом: код або таблиці обрізані справа

Корінь: white-space: pre без перенесення, плюс недостатні поля або фіксовані ширини.

Виправлення: використовуйте pre-wrap для друку і приберіть фіксовані ширини. Злегка збільшіть поля в @page.

4) Симптом: код переноситься, але стає нечитаємим «салатом слів»

Корінь: використання white-space: normal або агресивного переносу, що ламає токени.

Виправлення: тримайте white-space: pre-wrap і віддавайте перевагу word-break: break-word. Уникайте переносів для блоків коду.

5) Симптом: PDF має текст, який не можна виділити, або виглядає нечітко

Корінь: растризація через важкі CSS-ефекти, складні SVG-фільтри або особливості рушія друку.

Виправлення: приберіть тіні/фільтри у друці, спростіть SVG і перевірте pdftotext, чи працює екстракція тексту.

6) Симптом: заголовки «осиротіли», списки розділені незручно

Корінь: відсутні правила пагінації; покладалися на значення за замовчуванням браузера; блоки контенту не захищені від розривів.

Виправлення: застосуйте break-after: avoid до заголовків і break-inside: avoid до критичних блоків. Використовуйте widows/orphans як страховку.

7) Симптом: «Друк у PDF» відрізняється від «Друку» на робочих столах

Корінь: різні версії Chromium, різні встановлені шрифти, різні розміри сторінки за замовчуванням або відсутні ресурси в headless-режимі.

Виправлення: зафіксуйте версії рушія рендерингу і вбудовуйте шрифти (або використовуйте системні шрифти). Генеруйте PDF у контрольованому контейнері.

8) Симптом: надруковані сторінки не містять діаграм або іконок

Корінь: активи блокуються CSP, крос-доменні обмеження у headless-режимі, або в налаштуваннях користувача стоїть «не друкувати фони».

Виправлення: не передавайте зміст через фон; інлайньте критичні SVG; переконайтеся, що діаграми — реальні <img> або інлайн <svg>.

Контрольні списки / покроковий план

Покроково: побудуйте стилі друку, що переживуть продакшн

  1. Інвентаризуйте, що повинно друкуватися.

    Заголовок, дата останнього оновлення, заголовки, основний текст, блоки коду, важливі таблиці і мінімальний футер (опціонально). Все інше спочатку «ні».

  2. Додайте @media print з жорсткими скиданнями.

    Примусово чорний текст, приберіть фони і тіні, нормалізуйте поля і встановіть розмір шрифту для друку.

  3. Сховайте UI зі стабільними класами.

    Додайте .no-print і застосуйте до навігації/пошуку/бічної панелі/банера cookie/віджетів шарингу. Потім ховайте ці селектори у друці.

  4. Виправте позиціювання.

    У друку фіксовані/липкі елементи мають стати статичними або зникнути. Перевірте кількість сторінок до і після.

  5. Зробіть посилання корисними.

    Додавайте призначення зовнішніх посилань. Уникайте додавання для внутрішніх якорів, щоб зменшити шум.

  6. Забезпечте безпечний перенос коду.

    pre-wrap + break-word + рамки. Перевірте, що довгі команди не обрізаються.

  7. Контролюйте розриви сторінок.

    Захищайте заголовки, таблиці і блоки коду від розривів. Додайте widows/orphans за замовчуванням.

  8. Тестуйте на A4 і Letter.

    Принаймні раз. Ваша глобальна компанія тихо вам подякує (найвищий прояв вдячності).

  9. Автоматизуйте генерацію PDF у CI.

    Зафіксуйте версію браузера. Зберігайте базовий артефакт. Сигналізуйте при великих відмінностях (кількість сторінок + візуальні перевірки).

  10. Напишіть «тест прийнятності друку».

    Крихітний чекліст: навігація схована, код переноситься, немає обрізаного вмісту, посилання показують призначення, немає порожньої першої сторінки.

Короткий чекліст прийнятності друку (людський)

  • Перша сторінка починається з заголовка, а не з порожнього простору або панелі навігації.
  • Немає банера cookie, поля пошуку, «підпишіться», чат- віджету.
  • Блоки коду переносяться та залишаються читабельними; нічого не обрізано справа.
  • Заголовки не відокремлені від першого абзацу.
  • Посилання показують куди ведуть (принаймні зовнішні посилання).
  • Таблиці або вміщуються, або мають прийнятне обгортання.
  • Документ друкується і на A4, і на Letter без критичних втрат.

Короткий чекліст прийнятності друку (CI)

  • Згенерувати PDF з зафіксованим headless-браузером.
  • Екстрагувати текст за допомогою pdftotext; падати, якщо порожньо.
  • Зафіксувати кількість сторінок за допомогою pdfinfo; сигналізувати при великих змінах.
  • За потреби сканувати екстрагований текст на наявність UI-рядків («Search», «Cookie», «Subscribe»).

Три корпоративні міні-історії з полігонів друку

Міні-історія 1: інцидент, спричинений хибним припущенням

Середня SaaS-компанія мала вимогу «друкувати runbook» для робіт у дата-центрі: папка з операційними процедурами, що оновлювалася щотижня.
Доккоманда вважала, що друк — це просто «екран без навігації». Інженери думали, що документація — це «просто HTML». Ніхто не володів PDF-артефактом.

Потім під час вікна технічного обслуговування сталося ускладнення. Технік на місці роздрукував runbook, бо в зоні робіт був обмежений доступ до мережі.
Сторінка з процедурою відключення сховища містила довгі команди і довгі шляхи пристроїв. На екрані все було добре: горизонтальна прокрутка в блоках коду.
Надруковано — права частина кожного рядка була обрізана. Не перенесена — обрізана. Ідентифікатори пристроїв були відсутні.

Технік в екстреній ситуації зробив те, що роблять люди: здогадався. Вони зіставили видимий префікс шляху пристрою і продовжили. Неправильна ціль була відключена.
Пошкодження було локалізоване, але це був поганий вечір: простоюваня, розбір інциденту і багато «як ми не протестували друк?»

Корінне припущення було просте: «якщо це відображається, це друкується». Не так. Друк — інший рушій верстки з іншими обмеженнями.
Виправлення не було героїчним. Вони додали стилі друку, що переносять pre, збільшили поля і ввели CI-завдання «перегляд друку», яке генерувало PDF і запускало перевірки pdftotext.

Результат теж був простим: наступне вікно обслуговування пройшло добре, і більше ніхто не говорив про друк — саме так і має бути.

Міні-історія 2: оптимізація, що дала задню

Інша організація вирішила «оптимізувати доставку CSS». Вони виносили критичний CSS для першого рендеру, а решту відкладали.
CSS для друку був загорнутий у «решту», завантажувався пізно, бо не впливав на перший відмальовування. На папері це звучало розумно.

Для headless-генерації PDF це було нерозумно. Завдання з PDF завантажувало сторінку, чекало фіксований таймаут, а потім друкувало.
Іноді CSS для друку встигав завантажитись. Іноді — ні. Згенеровані PDF були недетермінованими: одні мали навігацію і бічні панелі, інші — ні.
Гірше: ті, що підвантажувалися частково, мали обидва, але з поламаним макетом. Схоже було на записку-вимогу, зібрану з фрагментів UI.

Команда спочатку звинуватила headless-інструмент. Вони збільшили таймаути. Повторювали роботи. Додали більше CPU.
Класичний рух: трактувати детерміновану проблему як проблему пропускної спроможності.

Насправді проблема була в порядку залежностей. CSS для друку не «некритичний», якщо ви генеруєте артефакти друку.
Вони виправили це, об’єднавши CSS для друку з основним стилем (або хоча б забезпечивши його детерміноване завантаження перед друком) і чекали на надійний сигнал «мережа проста», а не таймаут.

Бонус-урок: оптимізуючи під один шлях (екран), легко нашкодити іншому (друк/PDF), якщо не розглядати його як перший клас доставки.

Міні-історія 3: нудна, але правильна практика, що врятувала день

Фінансова компанія мала внутрішній портал документації. Вони також дотримувалися звички, яку інженери люблять висміювати, поки вона не рятує:
вони зберігали «артефакти релізу» для операційних документів. Кожного разу, коли виходила велика версія системи, знімок відповідних runbook-ів експортувався в PDF і зберігався разом із пакетом релізу.

Це було нудно. Це вимагало дисципліни. Іноді були скарги, чому ми витрачаємо хвилини CI на друк PDF.
Але це означало, що під час інциденту черговий міг витягти точну версію runbook-а, що відповідала розгорнутій системі.
Ніяких здогадок щодо того, чи застосовуються останні зміни. Ніяких «доки оновилися вчора, і CLI тепер інший».

Одного дня їхній портал документації впав під час внутрішньої мережної події. Проблеми з аутентифікацією, каскадні відмови — типова корпоративна пригода.
Тим часом потрібна була процедура відновлення для іншої послуги. Runbook був у порталі. Портал був недоступний.

Черговий дістав PDF-артефакт релізу, що зберігався в системі збірки, і виконав кроки відновлення без доступу до порталу.
Все спрацювало, бо PDF згенеровано за зафіксованими інструментами, стилі друку стабільні, і блоки коду не обрізаються.

Ніхто не святкував стилі друку. Святкували відновлення. Стилі друку тихо зробили свою роботу — це найкраща похвала в операціях.

Питання та відповіді

1) Чи створювати окремий файл print.css або тримати правила друку в основному стилі?

Працює будь-який підхід. Якщо ви генеруєте PDF у CI, я віддаю перевагу включенню правил друку в основний CSS, щоб уникнути проблем із порядком завантаження. Якщо тримаєте окремий файл — забезпечте, щоб він завантажувався детерміновано й достатньо рано для headless-друку.

2) Чому перегляд друку відрізняється між Chrome і Firefox?

Різні рушії друку, різні значення за замовчуванням і різний рівень підтримки властивостей для сторінкового медіа. Використовуйте перегляд друку в обох браузерах, якщо ваша аудиторія широка, і валідуйте через headless-пайплайн, якщо ви генеруєте PDF.

3) Як припинити повторюваний липкий хедер на кожній надрукованій сторінці?

У @media print встановіть для липкого контейнера position: static !important або сховайте його. Також приберіть верхні паддінги/маржини, які компенсували хедер на екрані.

4) Чи варто друкувати футер сайту з юридичними посиланнями та соціальними іконками?

Ні. Якщо потрібен юридичний текст — включіть мінімальний друкований футер, який справді релевантний. Соціальні іконки на папері — це просто ознака, що ніхто не продумав це.

5) Як найкраще показувати URL у друці без псу макету?

Додавайте URL лише для зовнішніх посилань і робіть їх трохи меншим шрифтом. Якщо URL занадто довгі — дозволяйте їм переноситися. Не додавайте для внутрішніх якорів, якщо ви не готуєте формальний звіт з референсами.

6) Мої блоки коду переносяться, але відступи дивні. Що робити?

Перенесення зберігає пробіли з pre-wrap, але довгі рядки візуально «висять» без відступу. Подумайте про CSS для «hanging indent» у друці або вручну розбивайте довгі команди у джерелі з символами продовження.

7) Чи можна примусово встановити альбомну орієнтацію для широких таблиць?

Підтримка орієнтації сторінки в браузерному друці непослідовна. Якщо потрібно надійно — використовуйте контрольований пайплайн генерації PDF, що підтримує шаблони сторінок і правила орієнтації, або переробіть таблицю для друку.

8) Чому фонові кольори зникають у друці?

Багато браузерів за замовчуванням відключають друк фону, щоб заощадити чорнило. Не покладайтеся на фони для передачі значень. Використовуйте рамки, ярлики та висококонтрастний текст.

9) Як не допустити розриву таблиці між сторінками?

Застосуйте break-inside: avoid (та page-break-inside: avoid) до таблиці або рядків таблиці, але майте на увазі, що дуже високі таблиці все одно розділяться. Для критичних таблиць розгляньте перетворення їх на секції або кілька менших таблиць.

10) Що тестувати автоматично, якщо можна дозволити тільки одну перевірку друку в CI?

Згенеруйте PDF за допомогою зафіксованого headless-браузера, запустіть pdftotext, щоб переконатися, що він не растровий, і зафіксуйте кількість сторінок для виявлення регресій.

Висновок: наступні кроки на цей тиждень

Стилі друку — негарна справа. Саме тому вони кусають команди: ніхто не торкається їх, поки клієнт, аудитор або черговий інженер не поставлять питання.
Виправлення не складне, але потребує ставлення до друкованого артефакта як до реального продукту з реальними тестами.

  1. Створіть або очистіть присвячений блок @media print, який ховає UI та нормалізує типографіку.
  2. Забезпечте перенос блоків коду у друці і перевірте, що довгі команди не обрізаються.
  3. Додавайте зовнішні URL після тексту посилання, щоб читачі на папері могли відновити референси.
  4. Додайте базові правила пагінації, щоб захистити заголовки, код і таблиці від некрасивих розривів.
  5. Якщо генеруєте PDF, зафіксуйте версію браузера і додайте мінімальний CI-чек: генерація PDF + екстракція тексту + відстеження кількості сторінок.

Зробіть ці п’ять речей — і ваша надрукована документація перестане бути тягарем. Вона не стане прекрасною. Вона стане придатною. Продакшн-системи тримаються на придатності.

← Попередня
Thread Director: чому процесор тепер радить операційній системі
Наступна →
Шум котушок (coil whine): чому ваш GPU пищить — і що ви можете (і не можете) зробити

Залишити коментар