Reference

Every option the offset-headings extension accepts.

The complete offset-headings configuration: the document options, the heading attributes, how offsets combine, and Quarto’s automatic shift.

Enabling the filter

filters:
  - offset-headings

Document options

extensions:
  offset-headings:
    by: 1
    recursive: true
    max-level: 6
    depth: 0
    quarto-shift-warning: true
Document and project options.
Option Type Default Description
by integer 0 Offset added to every heading in the document.
recursive boolean true Whether a per-heading offset cascades, for headings that do not say.
max-level integer 6 Deepest level a heading may be pushed to, for headings that do not say.
depth integer 0 How many descendant levels inherit a cascade, for headings that do not say. 0 is unlimited.
quarto-shift-warning boolean true Whether to warn when Quarto’s own shift can apply.

Heading attributes

## Section {offset-headings-by="1" offset-headings-depth="1"}
Attributes accepted on any heading.
Attribute Type Default Description
offset-headings-by integer 0 Offset added to this heading. Positive pushes it deeper, negative pulls it up.
offset-headings-recursive boolean recursive Whether the offset cascades to the headings nested below this one.
offset-headings-max-level integer max-level Caps the combined level of this heading and anything the cascade reaches.
offset-headings-depth integer depth How many descendant levels inherit the cascade. 0 is unlimited.

How a level is worked out

A heading’s final level is the sum of three things, then clamped:

  1. Its original level.
  2. The document offset, by.
  3. Its own offset-headings-by, or the offset cascading from an ancestor.

The result is capped at max-level, then clamped to the range one to six.

The offsets add rather than replace.
Written Document by Attribute Result
## Section 1 3
## Section {offset-headings-by="1"} 1 +1 4
## Section {offset-headings-by="-1"} 1 -1 2
## Section {offset-headings-by="3" offset-headings-max-level="4"} 1 +3 4, capped

A negative attribute cancels the document offset rather than overriding it.

The cascade

An offset flows to every heading nested below the one carrying it, and stops at the first heading back at or above the attributed heading’s original level.

offset-headings-recursive="false" applies the offset to that heading alone.

offset-headings-depth bounds the reach: with 1, only descendants within one level of the attributed heading’s original level inherit it, and anything deeper gets the document offset alone.

Worked example

extensions:
  offset-headings:
    by: 1
## Section {offset-headings-by="2" offset-headings-max-level="4" offset-headings-depth="1"}

### Subsection

#### Sub-subsection
The cap and the depth limit acting together.
Heading Sum Result
## Section 2 + 1 + 2 = 5 4, held by the cap
### Subsection 3 + 1 + 2 = 6, within the depth limit 4, held by the cap
#### Sub-subsection 4 + 1, beyond the depth limit 5

Quarto’s automatic shift

Quarto sets shift-heading-level-by: -1 by itself when a document has no level one heading:

  • for Typst, always;
  • for PDF and LaTeX, when number-sections is on and top-level-division is unset.

Pandoc applies that after every Lua filter, so it lands on top of the levels this extension produced.

Caution

Left alone it makes Typst and PDF headings one level shallower than the HTML, and it destroys any heading left at level one: the first becomes the document title and the rest become plain paragraphs.

A filter can neither read nor cancel it, so set it explicitly:

shift-heading-level-by: 0

Quarto skips its own shift whenever the option is set, whatever the value.

The filter warns when the shift could apply. An explicit shift-heading-level-by is invisible to Lua, so the warning cannot tell that you have already dealt with it; silence it once you have:

extensions:
  offset-headings:
    quarto-shift-warning: false

Validation

Validation rules.
Rule On failure
The resulting level is between one and six. Clamped.
max-level is between one and six. Warns, and the value is clamped.
by, max-level, and depth are integers. Warns, and the option falls back to its default.
offset-headings-by is an integer. Warns, the heading keeps the document offset alone, and no cascade starts.
offset-headings-max-level and offset-headings-depth are integers. Warns, and the document-level option applies instead.

A fractional number is rounded down rather than rejected, so offset-headings-by="1.7" offsets by one and "-1.7" by two in the other direction.

Booleans read true, yes, and 1 as true, in any case; every other value is false.

Limitations

  • Levels cannot leave the range one to six, so a large offset saturates.
  • Quarto’s automatic shift is applied after every filter and can only be prevented at the source, as above.
Back to top