How to draw at the margin of a page?

I’m trying to replicate a LaTeX template where the heading of each chapter has a black rectangle that stretches to the right margin, as shown in the picture below.

How can such a behavior be replicated in Typst? Here’s what I have at the moment:

show heading.where(level: 1): x => [
    #pagebreak(weak: true)
    #set text(
        size: 1.6em,
        weight: "regular"
    )
    #set align(right)
    #block(
        above: 20pt,
        below: 50pt,
    )[
        #smallcaps([Chapter #x.numbering]) \
        #text(
            size: 1.3em
        )[
            *#x.body*
        ]
        #place(
            dx: 75pt,
            horizon + right,
            rect(
                fill: black,
                height: 9%
            )
        )
    ]
]

With this code, I obtain the following output:

Please ignore the fact that the style of this heading does not perfectly match the one of the heading before. What concerns me in this post is just how to draw the rectangle at the side of the page. Here are the specifics of the solution I would like to find:

  • No hardcoded values, like in the #place call above where dx: 75pt or height: 9% are used. The rectangle should adapt dynamically to the content of the heading.
  • The height of the rectangle should perfectly match the height of the parent element. In the snippet above, the rectangle should have exactly the same height as the surrounding #block.
1 Like

Hi Oscar, I recommend reading about measure to figure out how it works. Here’s my code:

Margin calculation

/// Reference:
/// https://typst.app/docs/reference/layout/page/#parameters-margin
#let calc-margin(margin, shape) = if margin == auto {
  2.5 / 21 * calc.min(..shape)
} else {
  margin
}

Show rule

#show heading.where(level: 1): it => {
  pagebreak(weak: true)
  set text(
    size: 1.6em,
    weight: "regular",
  )
  set align(right)
  block(
    above: 20pt,
    below: 50pt,
    context {
      let title-content = {
        smallcaps([Chapter #counter(heading).display(heading.numbering)])
        linebreak()
        text(size: 1.3em)[*#it.body*]
      }
      title-content
      place(
        dx: calc-margin(page.margin, (page.width, page.height)),
        horizon + right,
        rect(
          fill: black,
          height: measure(title-content).height,
        ),
      )
    },
  )
}

I also modified the chapter numbering to make it work properly. Hope this helps :slight_smile:

2 Likes

Hi @sjfhsjfh, thanks for your code! Yes, your snippet was actually useful. I made some modifications to suit it to a variant of the specification I gave earlier, and the result is as follows:

My only question at this point is, is there any mistake in the way the numbering style of the heading is fetched? When I pasted your snippet as-is into my code, the compiler was issuing an error saying it was receiving a none value instead of the numbering style (“1.” in my case).

At the end, I replaced the line

smallcaps([Chapter #counter(heading).display(heading.numbering)])

with

smallcaps([Chapter #counter(heading).display("1")])

also because I wanted the “1” numbering style to be shown in the heading, and the “1.” one elsewhere in the document.

1 Like

I assumed that you have already set the numbering for heading in the former document like:

#set heading(numbering: "1.")

If you didn’t do this you will have context heading.numbering to be none. My purpose of doing this to keep consistency. However it seems that you would like to use a different pattern from the one in the document, then simply using .display("1") would be fine.

Yes, I already had that setting in my code before. It was strange for me to see that too.

I just bumped into this a few minutes ago. I was using an outline in the document, and outlines set heading.numbering to none. You can test this easily to figure out if that is where it breaks: replace the line with

 smallcaps([Chapter #repr(heading.numbering)])

and then hunt that none in your document :)