This package allows you to have counters which can inherit from other counters.
Concretely, it implements rich-counter, which is a counter that can inherit one or more levels from another counter.
The interface is pretty much the same as the usual counter.
It provides a display(), get(), final(), at(), and a step() method.
An update() method will be implemented soon.
Simple typical Showcase
In the following example, mycounter inherits the first level from heading (but not deeper levels).
#import "@preview/rich-counters:0.2.2": *
#set heading(numbering: "1.1")
#let mycounter = rich-counter(identifier: "mycounter", inherited_levels: 1)
// DOCUMENT
Displaying `mycounter` here: #context (mycounter.display)()
= First level heading
Displaying `mycounter` here: #context (mycounter.display)()
Stepping `mycounter` here. #(mycounter.step)()
Displaying `mycounter` here: #context (mycounter.display)()
= Another first level heading
Displaying `mycounter` here: #context (mycounter.display)()
Stepping `mycounter` here. #(mycounter.step)()
Displaying `mycounter` here: #context (mycounter.display)()
== Second level heading
Displaying `mycounter` here: #context (mycounter.display)()
Stepping `mycounter` here. #(mycounter.step)()
Displaying `mycounter` here: #context (mycounter.display)()
= Aaand another first level heading
Displaying `mycounter` here: #context (mycounter.display)()
Stepping `mycounter` here. #(mycounter.step)()
Displaying `mycounter` here: #context (mycounter.display)()

Construction of a rich-counter
To create a rich-counter, you have to call the rich-counter(...) function.
It accepts three arguments:
-
identifier(required)Must be a unique
stringwhich identifies the counter. -
inherited_levelsSpecifies how many levels should be inherited from the parent counter.
-
inherited_from(Default:heading)Specifies the parent counter. Can be a
rich-counteror any key that is accepted by thecounter(...)constructor, such as alabel, aselector, alocation, or afunctionlikeheading. If not specified, defaults toheading(and hence it will inherit from the counter of the headings).If it’s a
rich-counterandinherited_levelsis not specified, theninherited_levelswill default to one level higher than the givenrich-counter.
For example, the following creates a rich-counter foo which inherits one level from the headings, and then another rich-counter bar which inherits two levels (implicitly) from foo.
#import "@preview/rich-counters:0.2.2": *
#let foo = rich-counter(identifier: "foo", inherited_levels: 1)
#let bar = rich-counter(identifier: "bar", inherited_from: foo)
Usage of a rich-counter
-
display(numbering)(needscontext)Displays the current value of the counter with the given numbering style. Giving the numbering style is optional, with default value
"1.1". -
get()(needscontext)Returns the current value of the counter (as an
array). -
final()(needscontext)Returns the value of the counter at the end of the document.
-
at(loc)(needscontext)Returns the value of the counter at
loc, whereloccan be alabel,selector,location, orfunction. -
step(depth: 1)Steps the counter at the specified
depth(default:1). That is, it steps therich-counterat levelinherited_levels + depth.
Due to a Typst limitation, you have to put parentheses before you put the arguments. (See below.)
For example, the following steps mycounter (at depth 1) and then displays it.
#import "@preview/rich-counters:0.2.2": *
#let mycounter = rich-counter(...)
#(mycounter.step)()
#context (mycounter.display)("1.1")
Limitations
Due to current Typst limitations, there is no way to detect manual updates or steps of Typst-native counters, like counter(heading).update(...) or counter(heading).step(...).
Only occurrences of actual headings can be detected.
So make sure that after you call e.g. counter(heading).update(...), you place a heading directly after it, before you call any rich-counters.
Roadmap
- implement
update() - use Typst custom types as soon as they become available
- adopt native Typst implementation of dependent counters as soon it becomes available