What Are Best Practices for Allowing Users to Modify Constants in Typst Packages?

Hi everyone,

I’m currently working on a Typst project destined to become a package and would love to know how to design it so users can decide whether or not to modify certain constants. The goal is to give them the flexibility to customize the package while keeping it simple and intuitive.

In this project, I have a large number of constants (e.g., font choices, spacings, font sizes) that control the layout and appearance of elements. Here’s an example of how the constants look:

// in constant.typ
#let _Title-font = "Source Sans Pro"       // Title font
#let _Header-tune-font = "Noto Sans"       // "Air:" font
#let _Title-spacing = 0.2cm                // Spacing between title and header
#let _Title-fontsize = 14pt                // Title font size
// ... and many more

I want users to be able to use the package defaults without any extra configuration or easily modify specific constants if they need to customize the layout.

For the moment i’m thinking of using state but it comes with certain disavantages such as updating the whole project and forcing the user to use longer and “less intuitive” syntax :

// in constant.typ
#let _Title-font = state("it","Source Sans Pro") 
// in main.typ
#_Title-font.update(it => "Arial")

The ideal solution would be something like #set that allows users to override things with very clear and understandable syntax. For example:

#set _Title-font = "Arial"

In summary i have this questions :

  • How do you typically design Typst packages to make some constants easily customizable?
  • Is there a better alternative to using state for this purpose? Or a hack that i can develop to make it easier for the end user ?
  • If previous quesiton is no, do you think requiring users to use #_Title-font.update(...) is acceptable, or would you consider it too cumbersome?

Thank you in advance for your time and help. Since I started typst, I’ve found the community very responsive and helpful.

1 Like

The idiomatic way, so far at least until we get custom elements and types, is to make all these variables (not constants) parameters of your template function which the user would apply with #show: template.with(...).

You could still keep your template-defined values in constant.typ by utilizing named parameters, like so:

#import constant.typ: *
#let my-template(
  title-font: _Title-font,
  title-spacing: _Title-spacing,
  ..., // and so on
  body
) = {
  // Your show and set rules.
  body
}

That way the user can either choose to override these values with #show: my-template.with(title-font: "Comic Sans") or leave them as-is by not providing the named parameter, see also Making a Template – Typst Documentation.

1 Like