[More quick guides]

A quick guide to
running headers and footers
in Prince

Born to run

Running headers and footers are found in most books. They are there to remind readers of what they are reading, and to help navigate within the book. This guide will show you how to create running headers and footers in your HTML documents.

The formatter of choice is Prince, an HTML-and-CSS-to-PDF converter. The screenshots you see, and the PDF documents linked from this guide, have all been generated with Prince. You can easily create the same pdf files by downloading Prince and pointing it to the HTML links provided in this document.

A basic running header

In the most basic example, a string is placed at the top right of every page in the document:

html@page { @top-right { content: "Chapter 1: The Machine" }}

Inserting a string into the style sheet is easy, but the style sheet is then tied to a specific document. A more reusable style sheet will pick up the string from the document itself.

Using named strings

In the following example, the content of the h2 element is copied into a named string called title. The named string is then, in the second line, referred to inside the string() function:

htmlh2 { string-set: title contents } 
@page { @top-right { content: string(title) }}

When more chapters are added, the running page header is automatically updated:

htmlh2 { 
  string-set: title contents 
  break-before: page;
} 
@page { @top-right { content: string(title) }}

Page numbers

Page numbers are often placed at the bottom of the page:

html@page { @bottom-right { content: counter(page) }}

The predefinded counter called page is automatically increased at every page turn. Another counter, called pages holds the total number of pages in the document.

html@page { @bottom-center { content: counter(page) " of " counter(pages) }}

In the previous example, notice how the two counters are combined with a litera string between them.

Counting chapters

In the examples above, the chapter number has been written into the HTML code. It's often better to use CSS counters to generate these:

html@page { 
  @top-right { 
    content: "Chapter " counter(chapter) ": " string(title);
  }
}
h2 {  
  counter-increment: chapter;
  string-set: title contents;
  break-before: page;
}
h2::before { 
  content: "Chapter " counter(chapter) ": ";
}

Avoiding headers

On the first page of a chapter, where the chapter title is shown, you probably do not want to show a running header.

htmlh2 { 
  string-set: title contents;
  prince-page-group: start;   /* enables :first, see below  */
  break-before: page;
} 
@page { @top-right { content: string(title) }}
@page :first { @top-right { content: none }}

In the above example, the first @page-rule sets a running header on the top right of every page. However, the last @page-rule overrides the first on pages that are considered to be first. Normally, only the first page of a document is considered to be the first. However, by setting prince-page-group on the chapter heading, pages with chapter headings are also considered to be first.

Styling running headers

You can use the CSS properties you know & love on running headers:

html@page { 
  @top-right { 
    content: string(title);
    font-style: italic;
    vertical-align: bottom;
    padding-bottom: 0.3em;
    border-bottom: thin solid black;
    margin-bottom: 0.7em;
  }
}

Page margin boxes

Running headers can be placed in any of the CSS page-margin boxes:

html@page { 
  @top-left-corner { content: "@top-left-corner"; background: #eee }
  @top-left { content: "@top-left"; background: #ddd }
  @top-center { content: "@top-center"; background: #eee }
  @top-right { content: "@top-right"; background: #ddd }
  @top-right-corner { content: "@top-right-corner"; background: #eee }

  @left-top { content: "@left-top"; background: #ddd }
  @left-middle { content: "@left-middle"; background: #eee }
  @left-bottom { content: "@left-bottom"; background: #ddd }

  @right-top { content: "@right-top"; background: #ddd }
  @right-middle { content: "@right-middle"; background: #eee }
  @right-bottom { content: "@right-bottom"; background: #ddd }

  @bottom-left-corner { content: "@bottom-left-corner"; background: #eee }
  @bottom-left { content: "@bottom-left"; background: #ddd }
  @bottom-center { content: "@bottom-center"; background: #eee }
  @bottom-right { content: "@top-right"; background: #ddd }
  @bottom-right-corner { content: "@top-right-corner"; background: #eee }
}

Notice how how the default text-alignment changes from one margin box to the next.

Running elements

Named strings, as described above, is a simple mechanism for copying textual content from the document into margin boxes. Running elements is another mechanism, where elements are moved (not copied) from the document into a receiving margin box. Typically, running elements are used with more complex headers. Here is an example where the running header has a mathematical equation:

html@page{
  @top-right {
    content: element(header);
  }
}

div.math {
  position: running(header);
  text-align: right;
}

...

<div class=math>
The solution is:
  <math><msqrt><mi>x</mi></msqrt></math>
</div>

Notice that the text-align property is set on the h3 element; when using running elements, properties are typically set on the element itself, and not inside @page.

Page numbers in running elements

Running elements are evaluated on a per-page basis. Page numbers and other counter values can be added to a running element through a pseudo-element:

html@page { @top-left { content: element(header) }}

div { position: running(header); text-align: outside }
div:after { content: " " counter(page); color: red }

Note how the running div element is aligned to the outside. This is a quick way to make headers appear on the outside, but it only works when the other margin boxes are empty.

Recto and verso running elements

A more complex header may have several compoents that should be shown in the opposite order on left and right pages. You can float sub-elements to the outside to see this.

html@page :left { @top-left { content: element(header) }}
@page :right { @top-right { content: element(header) }}

div { position: running(header) }
span.page:after { content: " " counter(page); color: red }
span.section:after { content: " XXI " }
span { float: outside }

Note how the alignment of the running element places it in the outside position. This kind of placement works as long as the other margin boxes are empty.

Changing running elements

Running elements can be replaced at any point in the document. In this example, there are different running elements on left and right pages, and the elements are replaced several times. When replaced, the change takes place from (and including) the page where the replacement occurs.

html@page :left { @top-left { content: element(lheader) }}
@page :right { @top-right { content: element(rheader) }}

h2 { counter-increment: numchapter }
h2:before { content: "Chapter " counter(numchapter) ": " }
span:before { content: counter(numchapter) " " }
span { color: red }
div.lheader { position: running(lheader) }
div.rheader { position: running(rheader); text-align: right }

Combining running elements with named strings

It is not possible to combine running elements and named strings in the same margin box, but you can do so in separate margin boxes. In this example, page numbers (in green) named strings, and the other headers are running elements.

html@page :left { @top-left { content: element(lheader) }}
@page :right { @top-right { content: element(rheader) }}
@page :left { @top-right { content: counter(page); color: green }}
@page :right { @top-left { content: counter(page); color: green }}

A logo on every page

Adding a logo or watermark on every page is easy with running elements:

html@page{
    @bottom-right-corner {
        content: element(logo);
    }
}

img#logo {
  width: 10vw;
  position: running(logo);
}

Running elements in CSS

Pseudo-elements can also be made to run. This way you avoid using HTML elements:

html@page{
  @bottom-left-corner {
    content: element(logo);
  }
}

body:before  {
  width: 10vw;
  content: url(princelogo.png);
  position: running(logo);
}

2022-07-21 HÃ¥kon Wium Lie