How to set adaptive margins based on page size?

I want to set page margins that scale with the paper size, something like a percentage of the page dimensions, but clamped to a minimum and maximum in terms of line-height multiples.

The ideal would be something like:

let line-height = font-size * leading-ratio
set page(
  margin: (
    x: calc.clamp(page.width * 0.12, line-height * 2, line-height * 6),
    y: calc.clamp(page.height * 0.10, line-height * 2, line-height * 5),
  )
)

The problems I’ve run into:

  • page.width / page.height require a context, but set page(margin:) doesn’t accept a context block
  • calc.clamp won’t mix ratio and length types, so 12% and line-height * 2 can’t be compared
  • Wrapping the body in show: it => context { set page(...); it } works in isolation but creates a new page in my template

Is there any way to compute a length-valued margin that depends on the page dimensions? Or a way to use context inside set page?

I’m a Typst newbie. This isn’t the prettiest code, but it seems to work. There might be subtle side-effects that I am missing.

An auxiliary function, make-page-dict, creates a dictionary array using the page setup values. It passes through extra page arguments. Since the calculations occur inside the auxiliary function, it has access to the values being provided.

Note the .pt() code that grabs the numeric value of the lengths (in terms of points) so calc.clamp will work on numbers. Then multiplying by * 1pt converts the calc.clamp output back to lengths.

Note the . . (double periods) prefixing things to handle extra arguments and converting the dictionary array to arguments.

#let make-page-dict(width: 6in, height: 9in, line-height: 0.25in, ..extraargs) = {
  (
    width: width,
    height: height,
    margin: (
      x: calc.clamp(width.pt() * 0.12, line-height.pt() * 2, line-height.pt() * 6 ) * 1pt,
      y: calc.clamp(height.pt() * 0.10, line-height.pt() * 2, line-height.pt() * 5 ) * 1pt,
         ),
      ..extraargs.named()
  )
}

#set page(..make-page-dict(width: 4in, height: 7in, line-height: 0.3in, numbering: "1"))

// outputting some input arguments to make sure they are as expected.
#context page.width 
#context page.margin
#context page.numbering 
1 Like

Hello @Forgon,

This is discussed in a few posts on this forum as well as some GitHub issues relating to margins.

I would have a look at How to define the page paper format and define margins using set rules?