How to properly customize heading numbers with prefix, number and suffix?

Hi, I’m new to Typst and cannot find a way to properly apply a specific customization to headings. I’m trying to format them into the following:

TITLE [number in Roman]

Title text

As an output example it should give something like this:

TITLE I

The first title

Content, content, content, ....

TITLE II

A second title

More content, content, content, ....

TITLE III

Another title text

Even more content, content, content, ....


Both “TITLE [number]” and “Title text” must be centered (the preview is not showing them center aligned even with explicit html code) and in different lines.

I could find on the forum several examples to accomplish almost everything I want (e.g., the following topics: 1, 2, 3, and so on).

However they all suppose a heading left-aligned. When I tried to center align, both “TITLE [number]” and “Title text” get shifted to the right. With the following code it can be replicated:

#set heading(numbering: it => {
    set align(center)
    set text(14pt, weight: "bold")
    numbering(upper("Title") + " I", it)
  })

= Test 1
Content

= Test 2
Content

The closest I could get to what I want was with the following:

#show heading.where(level: 1): it => {
    text(size: 14pt,
    weight: "bold",
    [
      #set align(center)
      #upper("Title") #counter(heading).display("I")\
      #it.body
      #v(1.5em)
    ])
}

= Test 1
Content

= Test 2
Content

It then shows TITLE N in every heading. I lack enough knowledge to figure out how to replace the “N” with the actual number in roman format.


Finally I feel I must give some constructive criticism. One of the key points of Typst, related to LaTeX, was the aim to be way easier as a result of its modern syntax. However, a task so simple as customizing a heading with some given inputs became a challenge. In conclusion, IMHO Typst failed hard here. It could be as simple as (prefix, number format, suffix, level, [text]) with some options. Maybe it is and I was just unable/unlucky to find it.

1 Like

Edit: There is actually a cleaner/simpler solution available based on your first approach. See below for the code.

Your second code snippet is already really close to the solution. The counter does not display the heading number since you have not set the heading numbering yet. By default, the heading number is none, see here. The counter will then just return “N” as the dummy value. If you would use #counter(heading).display("1") it would return 0, which is a lot easier to understand than “N” in my opinion.

Since you are creating the heading from scratch, some of the default behavior is lost in your code. If you are interested in the details, please see How can I show the heading number after the heading itself? - #3 by Andrew (this is from the second topic you referenced). You don’t have to replicate the heading completely here, but you should definitely use a Block Function – Typst Documentation. Headings use “sticky” blocks by default to prevent a heading at the end of a page with the content only starting on the next page. See here for an example. Furthermore, with a block you can use the parameter below to adjust the spacing after the heading.

#set heading(numbering: "I")
#show heading.where(level: 1): it => block(
  below: 1.5em,
  {
    set text(size: 14pt, weight: "bold")
    set align(center)
    upper("TITLE ") + counter(heading).display()
    linebreak()
    it.body
  }
)

= Test 1
#lorem(570)

= Test 2
#lorem(100)

I agree with you that this modification of the heading feels somewhat complicated. After playing around with your first idea a bit, I actually found a simpler solution. The shift of the title you mentioned is caused by the hanging-indent. This is required for a correct formatting of multiline headings. If you set this to zero, the shift will be gone and you can use show-set rules for the other modifications. The required code is then only

#set heading(numbering: (..n) => "TITLE " + numbering("I", ..n) + linebreak(), hanging-indent: 0pt)
#show heading: set align(center)
#show heading: set text(size: 14pt, weight: "bold")

which is a reasonable effort given the level of modification compared to the default heading structure. I hope that you consider this solution easy enough to stick with Typst. :slightly_smiling_face:

If you just need to customize the numbering, the package numbly – Typst Universe is really useful. Especially if you need to modify the numbering for different heading levels.

4 Likes

Thank you very much for your response, it was very didactic! The simpler version works perfectly!

I surely underestimated the “programming” aspect of Typst, e.g., when I did not consider the counter as a variable which needed to be initiated. Furthermore, I did not read the docs carefully as I should.

I will keep learning Typst for sure! Thanks!

1 Like