How do I count elements on a page so the result isn't always zero in the header?

The closest thing you can get to a custom function that returns a locateable element would be a figure with a custom kind. This will also automatically create a corresponding counter, you can use show rules to target the elements and you could even create an outline only for the verses.

I decided to use an empty figure body here. All the actual content is put inside the caption. You don’t have to do it like this, but this makes it easier to get the correct formatting for the custom counter and the custom supplement in the show rule for figure.caption.

While the function verse() itself looks quite verbose, there is no “hacky” solution in there. If you know your way around show rules and set rules, this should be easy to understand and modify to your needs.

#let verse(body, label: none) = [
  #show figure: set align(left)
  #set figure(numbering: "1.")
  #show figure.caption: it => context { 
    strong(it.counter.display()) + [ ]
    it.supplement + [: ]
    linebreak()
    it.body
  }
  #figure(kind: "verse", supplement: "Strophe", [], caption: body)
  #label
]

#let count-verses() = {
  let locations = query(figure.where(kind: "verse")).map(h => h.location())
  locations.filter(loc => loc.page() == here().page()).len()
}

#set page(
  header: context count-verses(),
  footer: context count-verses(),
)

#verse(lorem(2))
#verse(lorem(2))
#verse(lorem(2))
#verse(lorem(2))

#pagebreak()
#counter(figure.where(kind: "verse")).update(0)
#verse(lorem(2))
#verse(lorem(2))
#verse(lorem(2))
2 Likes