Using CSS string-set for Print Headers

The CSS string-set property allows developers to dynamically extract text content from elements within a document and inject that content into margin boxes, such as running headers and footers, during print rendering. Defined in the CSS Paged Media Module, string-set pairs with the string() function to create context-aware headers that update automatically as readers move through different sections, chapters, or pages of a printed document.

How string-set Works

In standard web layouts, headers typically remain fixed or scroll with the viewport. In paged media (such as PDFs or physical prints), headers reside in page margin boxes defined within an @page rule. Because margin boxes exist outside the normal document flow, they cannot directly inherit DOM content.

The string-set property bridges this gap by acting like a variable assignment. It copies the text value from an element in the document tree and assigns it to a named string identifier.

Capturing Document Text

To capture text, you apply string-set directly to the selector whose content you want to reuse, such as an <h1> or <h2> tag:

h1 {
  string-set: chapter-title content(text);
}

In this example:

  • chapter-title is the custom name chosen for the string.
  • content(text) is a value function that extracts the plain text content of the <h1> element, ignoring any descendant HTML tags.

Other keyword variations for content() include:

  • content(before): Captures the generated content of the element's ::before pseudo-element.
  • content(after): Captures the generated content of the element's ::after pseudo-element.
  • content(): Captures the full content, including text and generated pseudo-element content.
  • attr(attribute-name): Captures the value of a specific HTML attribute instead of inner text.

Displaying Captured Strings in Margin Boxes

Once a string is defined, it can be referenced inside any margin box rule (such as @top-left, @top-center, or @top-right) using the string() function:

@page {
  @top-center {
    content: string(chapter-title);
    font-size: 9pt;
    color: #555;
  }
}

Whenever the print layout engine encounters a new <h1>, the value of chapter-title updates. Pages printed after that point will reflect the updated heading.

Handling Multiple Headings on a Single Page

If a page contains multiple elements that update the same named string (for example, two <h2> headings on the same page), you can control which occurrence the margin box displays using page-scope arguments:

  • first: Uses the value of the first assignment on the page. (This is the default behavior if an assignment occurs on the page.)
  • start: Uses the value in effect at the start of the page, even if an element on that page redefines the string later.
  • last: Uses the value of the last assignment on the page.
  • first-except: Uses the value of the string on pages where the element is not present, suppressing the header on the page where the heading actually appears (useful for title or chapter-start pages).
@page {
  @top-right {
    /* Displays the last section title encountered on the page */
    content: string(section-title, last);
  }
}

Implementation Context

Native web browsers have limited support for CSS Paged Media margin boxes and string-set. Consequently, this property is primarily used alongside dedicated HTML-to-PDF print engines such as PrinceXML, WeasyPrint, Antenna House Formatter, or polyfills like Paged.js that implement CSS paged media standards.