back

CSS for Print & Paged Media

Unlike websites, which are not constrained by any physical document size, paged media are limited by the size of the target page like an A4 page. Fortunately, CSS supports paged media. Let's go through some of the basic concepts and CSS properties that will help you control your document flow on a paged document.

The page size

In CSS you can define a page and a reference to it by name or special selectors (e.g. :left, :right). Use the @page rule to set the relevant page properties.

@page { size: A4; }

This is the minimum requirement for defining a page. Other size options could look like:

@page { size: A4 landscape; size: 400mm 200mm; size: 10in 20in; }

The page margins

Margins can also be defined within the @page - rule.

@page { size: A4; margin: 20mm; }

Printing current and total page number

Assume you want to print the current and total page number on each page at the bottom right corner. Within the @page rule, you also have access to 16 pre-defined margin areas, all of which will automatically position and align. You can read more about those margin areas here.

@page { size: A4; margin: 20mm; @bottom-right { content: 'Page' counter(page) ' of ' counter(pages); } }

The two counters page and pages are special predefined counters that will always return the current page or total document page number respectively.

Adding header and footer

In addition to the 16 pre-defined margin areas mentioned above, you can also define as many area as you want anywhere on the page yourself. Use the @area rule inside the @page rule for that.

@page { size: A4; margin: 50mm 20mm 20mm; @area my-awesome-header { content: 'Here is a page header'; background: red; top: 5mm; left: 20mm; right: 20mm; } }

This works smoothly, but let’s say you want to use an element (e.g. <div id="header">Hello Page</div>) within your document, and position it inside your “awesome-header” area.

@page { size: A4; margin: 50mm 20mm 20mm; @area my-awsome-header { content: element(my-header); background: red; top: 5mm; left: 20mm; right: 20mm; } } #header { position: running(my-header); }

You basically define an element at the beginning, that you would like to see as your header, as a running-element, and place it inside your page area. The element then appears printed on every page.

You can read more about paged generated content at www.w3.org, where also more options that facilitate content fine-tuning.

Working with page groups

Consider following issue, you have multiple chapters in your document and you want to change the layout of the first page of each chapter. This can be done by using page groups. A page group is created each time a named page is introduced with page: page-name. If two adjoining elements introducing the same page, they will be joined and the internal page group count is not getting reseted, therefor you must force a page break before and after to ensure that each chapter will introduce a new page group.

@page chapter { background: grey; } section.chapter { page: chapter; break-after: page; break-before: page; }

In addition, a page can belong to multiple page groups. If a nested element sets a new named page with page: page-name then it will create a new name page.

Bellow is a simple example of page group for each chapter. You can also read more about it at w3.org.

<!DOCTYPE html> <html lang="la"> <head> <meta charset="UTF-8"> <title>Page groups</title> <style> @page { size: 120mm 120mm; margin: 10mm; @bottom-center { content: counter(page) ' of ' counter(pages); font-size: 10pt; color: grey; } } /* First page of each chapter */ @page :nth(0n + 1 of chapter) { margin-top: 40mm; background: lightyellow; border-top: #000000 2pt dashed; @bottom-center { content: none; } } section.chapter { page: chapter; break-before: page; break-after: page; column-count: 2; } h1 { font-size: 25pt; break-after: avoid; column-span: all; text-align: center; } h2 { font-size: 20pt; font-weight: bold; break-after: avoid; column-span: all; text-align: center; } p { margin: 0 0 0.5em; font-size: 12pt; text-align: justify; hyphens: auto; hyphenate-limit-lines: 2; } .image { position: relative; padding: 2mm; border: 0.2mm solid black; } .image img { width: 100%; } .image .author { display: block; position: absolute; top: 2%; right: 0; background: rgba(0, 0, 0, .7); color: white; font-size: 6pt; padding: 2mm; } </style> </head> <body> <section class="chapter"> <h1>Chapter 1</h1> <h2>Vel ut detraxit pericula tractatos</h2> <p>Lorem ipsum dolor sit amet, vis ludus audire ei. Quis justo quidam eam id, prima decore philosophia has cu, meis rebum feugait ex est. Eos ea dicunt repudiandae. Ponderum nominati perpetua no vim. Eam cu graeci alterum, veri doctus ancillae sit ad.</p> <p>Tale verear mea ad, te appetere repudiandae vix, ex putant impedit maluisset mei. Est ut porro dicta habemus. No quem moderatius cum, molestie detraxit sea cu, cu sit legimus abhorreant honestatis. Erat regione ne pri, liber repudiare his at. Duo timeam perpetua ad, cum delectus delicatissimi in. Ei nec enim dicta.</p> <h2>Nam ei solum tollit scriptorem</h2> <p>Alienum neglegentur quo ut, vis id exerci inermis, cu aperiri noluisse sed. Et usu decore nostro, ea tritani eleifend accommodare qui. Delenit facilis explicari nec at. Ei sea corpora theophrastus, vix melius blandit an, ne pro partem perfecto inciderint. Munere aperiri tacimates sed et, oratio mucius prompta mei cu.</p> <p>Te nibh ferri bonorum qui, cu illum ullum omnium usu. Magna fastidii mei an, duo volumus accusam voluptaria te, nam ut tollit nostrud suavitate. Ea habemus posidonium repudiandae mel, lorem vivendum pri ea. Quem odio sea at, ex vidisse alterum inimicus sit. Nostrud labores mel ex, in altera semper offendit duo, porro mentitum omittantur eos cu.</p> <div class="image"> <img src="https://typeset.sh/images/demo/yellow-and-white-sail-boat-on-sea-3703074.jpg" /> <span class="author">Photo by Dimitry Anikin from Pexels</span> </div> <p>Eum vero vidisse eu. Qui ut duis alienum, his viris delicata no. Habeo ornatus qualisque te nam, duo nominati persequeris id, sed regione instructior ex. Nec ex quodsi appellantur. Ius persius omittantur ea. Vis tibique iudicabit cu, id mei labore accommodare. Ut eam commodo oblique adolescens.</p> <h2>Nec ex quodsi appellantur</h2> <p>Lorem ipsum dolor sit amet, vis ludus audire ei. Quis justo quidam eam id, prima decore philosophia has cu, meis rebum feugait ex est. Eos ea dicunt repudiandae. Ponderum nominati perpetua no vim. Eam cu graeci alterum, veri doctus ancillae sit ad.</p> <p>Tale verear mea ad, te appetere repudiandae vix, ex putant impedit maluisset mei. Est ut porro dicta habemus. No quem moderatius cum, molestie detraxit sea cu, cu sit legimus abhorreant honestatis. Erat regione ne pri, liber repudiare his at. Duo timeam perpetua ad, cum delectus delicatissimi in. Ei nec enim dicta.</p> </section> <section class="chapter"> <h1>Chapter 2</h1> <h2>Vel ut detraxit pericula tractatos</h2> <p>Lorem ipsum dolor sit amet, vis ludus audire ei. Quis justo quidam eam id, prima decore philosophia has cu, meis rebum feugait ex est. Eos ea dicunt repudiandae. Ponderum nominati perpetua no vim. Eam cu graeci alterum, veri doctus ancillae sit ad.</p> <p>Tale verear mea ad, te appetere repudiandae vix, ex putant impedit maluisset mei. Est ut porro dicta habemus. No quem moderatius cum, molestie detraxit sea cu, cu sit legimus abhorreant honestatis. Erat regione ne pri, liber repudiare his at. Duo timeam perpetua ad, cum delectus delicatissimi in. Ei nec enim dicta.</p> <h2>Nam ei solum tollit scriptorem</h2> <p>Alienum neglegentur quo ut, vis id exerci inermis, cu aperiri noluisse sed. Et usu decore nostro, ea tritani eleifend accommodare qui. Delenit facilis explicari nec at. Ei sea corpora theophrastus, vix melius blandit an, ne pro partem perfecto inciderint. Munere aperiri tacimates sed et, oratio mucius prompta mei cu.</p> <p>Te nibh ferri bonorum qui, cu illum ullum omnium usu. Magna fastidii mei an, duo volumus accusam voluptaria te, nam ut tollit nostrud suavitate. Ea habemus posidonium repudiandae mel, lorem vivendum pri ea. Quem odio sea at, ex vidisse alterum inimicus sit. Nostrud labores mel ex, in altera semper offendit duo, porro mentitum omittantur eos cu.</p> <div class="image"> <img src="https://typeset.sh/images/demo/yellow-and-white-sail-boat-on-sea-3703074.jpg" /> <span class="author">Photo by Dimitry Anikin from Pexels</span> </div> <p>Eum vero vidisse eu. Qui ut duis alienum, his viris delicata no. Habeo ornatus qualisque te nam, duo nominati persequeris id, sed regione instructior ex. Nec ex quodsi appellantur. Ius persius omittantur ea. Vis tibique iudicabit cu, id mei labore accommodare. Ut eam commodo oblique adolescens.</p> <h2>Nec ex quodsi appellantur</h2> <p>Lorem ipsum dolor sit amet, vis ludus audire ei. Quis justo quidam eam id, prima decore philosophia has cu, meis rebum feugait ex est. Eos ea dicunt repudiandae. Ponderum nominati perpetua no vim. Eam cu graeci alterum, veri doctus ancillae sit ad.</p> <p>Tale verear mea ad, te appetere repudiandae vix, ex putant impedit maluisset mei. Est ut porro dicta habemus. No quem moderatius cum, molestie detraxit sea cu, cu sit legimus abhorreant honestatis. Erat regione ne pri, liber repudiare his at. Duo timeam perpetua ad, cum delectus delicatissimi in. Ei nec enim dicta.</p> </section> </body> </html>

Colors

Examples for working with CMYK and sport colors.

html, body { /* Setting default black text color with overprint */ color: cmyk(0%, 0%, 0%, 100%, overprint); } .corporate { /* Spot color with overprint */ color: -ts-separation("Corporate Red", cmyk(0%, 100%, 100%, 0%) overprint); } .corporate_light { /* Spot color with 25% tint and no overprint */ color: -ts-separation("Corporate Red", 25% cmyk(0%, 100%, 100%, 0%)); }

You can also register CMYK values for named sport colors.

<?php \Typesetsh\Css\Color::$SPOT_COLORS['Corporate Red'] = \Typesetsh\Css\Color\Cmyk::create(0, 1, 1, 0); .corporate { color: -ts-separation("Corporate Red"); }

Color Profiles

Typeset.sh also allows @color-profile rules to define a custom color set. This allows to create custom color profiles with more than the standard CMYK components.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Color profiles</title> <style> @page { size: 320pt 320pt; margin: 10pt; } @color-profile --cmykb { src: device-cmyk; components: "Cyan" 1 0 0 0, "Magenta" 0 1 0 0, "Yellow" 0 0 1 0, "Black" 0 0 0 1, "PANTONE Reflex Blue C" 1 0.723 0 0.02; } @color-profile --fogra52 { src: url('https://www.color.org/registry/profiles/PSOuncoated_v3_FOGRA52.icc'); } @color-profile --duotone { src: device-cmyk; components: "PANTONE Reflex Blue C" 1 0.723 0 0.02, "PANTONE Warm Red C" 0 0.75 0.9 0; } .f1 { color: color(--cmykb 0 0 0 0 1); } .f2 { color: color(--fogra52 0 1 0 0); } .f3 { color: lab(62.753% 52.460 -34.103); } #left { color: -ts-overprint(color(--duotone 1 0)); float: left; font-weight: bold; } #right { color: -ts-overprint(color(--duotone 0 1)); float: right; font-weight: bold; } #gradient { background: linear-gradient(to right, color(--duotone 1 0), color(--duotone 0 1)); height: 100px; clear: both; } </style> </head> <body> <p>Testing <span class="f1">cmykb</span> support.</p> <p>Testing <span class="f2">fogra52</span> support.</p> <p>Testing <span class="f3">lab</span> support.</p> <div> <span id="left">PANTONE Reflex Blue C</span> <span id="right">PANTONE Warm Red C</span> </div> <div id="gradient"></div> </body> </html>