How to set headers and page numbers with alternating text on even and odd pages?

I want to set my book template to show even pages aligned left with “# | chapter number” and odd pages aligned right with “book title | #”, but I can’t quite figure out how within the header/page numbering documentation.

most likely will need separate sections for each chapter, but that’s fine.

also want the page numbers to start on the first chapter (page 7 in the file) and preferably be hidden on blank pages

Hello @indoor_plant, welcome! Have you looked at hydra? I think this will suit your needs.

Also, don’t hesitate to take a look at How to post in the Questions category! :smiley:

I use the following snippet in my template:

set page(header: context {
    let page = counter(page).get().first()
    let body = if calc.odd(page) [Section #chapter_num #section_num #h(0.5cm) #section #h(0.5cm) *#str(page)*]
      else [*#str(page)* #h(0.5cm) Chapter #chapter_num #h(0.5cm) #chapter]
    let alignment = if calc.odd(page) { right } else { left }
    align(alignment, body)
  }

image

image

1 Like

hi, thanks! been fiddling with hydra and reading the documentation on it but I’m still not seeing where I can actually define header info?

typst seems to not accept “hydra” as a variable despite having the @import string included

thanks - would you mind sharing the code where you defined those chapter_num and section_num variables?

Can you provide what you’ve been working on? You can wrap code here using three ```typ on a new line, and closing by three backticks too.

sure, this is what I grabbed from hydra

#import "@preview/hydra:0.6.0": hydra

#set page(
  paper: "a5", 
  margin: (inside: 1.25in, outside: .75in, y: 1in),
  numbering: "1", 
  number-align: top,
  header: context {
  if calc.odd(here().page()) {
    align(right, emph(hydra(1)))
  } else {
    align(left, emph(hydra(2)))
  }
})

You can define the header struct in the header parameter of page. From your example right now, you have

context {
  if calc.odd(here().page()) {
    align(right, emph(hydra(1)))
  } else {
    align(left, emph(hydra(2)))
  }
}

which gives you alternating chapter/section header.
If you want a struct more like title | #, then you can use

context {
  let h = (
    counter(page).display(),
    h(1fr),
    hydra(1)
  )
  if calc.odd(here().page()) { h.join() } else { h.rev().join() }
}

which will give you alternating page number/ title.

That’s as simple as using

#set page(numbering: none)

when you don’t want any numbering! Once your chapters start, you can re-enable numbering.

I don’t think it’s possible to detect blank page? Except if you introduce one with a hard pagebreak(). In which case, you can set page numbering to none before breaking the page and re-enable it afterwards.

that gives me alternating page numbers but again, there is no variable for defining the title or section info - is that just something I set up outside the header? that’s the piece I’m lost on.

My use is very simple, so I simply type #let chapter_num = "3"
in the beginning of the document like a medieval man :sweat_smile:.

I set a template.typ like

#let conf(
  chapter: none,
  chapter_num: none,
  section: none,
  section_num: none,
  doc,
) = {
  set heading(numbering: "1A")
  set page(
    header: context {
      let page = counter(page).get().first()
      let body = if calc.odd(page) [Section #chapter_num#section_num #h(0.5cm) #section #h(0.5cm) *#str(page)*] else [*#str(page)* #h(0.5cm) Chapter #chapter_num #h(0.5cm) #chapter]
      let alignment = if calc.odd(page) { right } else { left }
      align(alignment, body)
    },
  )

  doc
}

and write one file for section like Chapter_1A.typ:

#let chapter = "Vector Spaces"
#let chapter_num = "1"
#let section = $RR^n "and" CC^n$
#let section_num = "A"

#import "../template.typ": *
#show: conf.with(
  chapter: chapter,
  chapter_num: chapter_num,
  section: section,
  section_num: section_num,
)

#heading(level: 1)[#chapter]
#heading(level: 2)[#section]
#heading(level: 3, numbering: none)[Definitions and Theorems]

#lorem(100)

Finally, I #include every file in a main.typ.

I don’t know if this is good practice (very new to Typst).

very new myself, so I appreciate you sharing! ty! :slight_smile:

Ah! I see your problem. What hydra does internally is query the current chapter hydra(1), or section hydra(2), i.e. heading(level: 1) or heading(level: 2).

There is no variable for that, everything is done using query.

Hence, all you need to do is write your headings as usual, and the page header will be evaluated every time it needs to be (on every page). The contextual expression in the page header parameter will output on each page the return value of hydra(1) or hydra(2), i.e. the current chapter or section.

1 Like

Hey @indoor_plant, welcome to the forum! I’ve changed your question post’s title to better fit our guidelines: How to post in the Questions category

For future posts, make sure your title is a question you’d ask to a friend about Typst. :wink:

In addition, please let us know if you’d like further help, or mark an answer as a solution. Thanks :slight_smile: