How to have different color schemes for template?

Hello everyone (this is my first question on the forum, please let me know if I’m doing something wrong).

I’m trying to write a template that would have two possible set of colors, say light or dark, to typeset certain elements (background, text, titles, etc). I also want to expose these colors to the template user. Before writing it as a template, I was simply doing

#let theme = "dark"
#let color_lib = (
  dark: (
    background: oklch(25%, 0.03, 290deg),
    text: oklch(90%, 0.045, 270deg),
    highlight: oklch(88%, 0.08, 220deg)
  ),
  light: (
    background: oklch(90%, 0.02, 290deg),
    text: oklch(25%, 0.03, 290deg),
    highlight: oklch(50%, 0.25, 290deg),
  )
)
// insert defaults in the color dict
#for (name, color) in color_lib.at(theme) { 
    color_lib.insert(name, color)
}

#set page(fill: color_lib.background)
#set text(fill: color_lib.text)

Hello #text(fill: color_lib.highlight)[there].

Then, I can change the full theme just by changing the variable theme, and while writing, I can decide either to follow the theme by using colors in color_lib.<name> or specify I want a color from a specific theme with color_lib.<theme>.<name>.

I now would like to hide this logic away into a package/template, which I believe should look like

#import "lib.typ" : template_func

#show: template_func.with(
    theme: "dark", 
    ...
)

but things get complicated inside lib.typ :

  • If I try to just copy paste things within the definition of template_func, I am not allowed to mutate the color_lib dictionnary anymore to inject defaults based on the selected theme.
  • Elsewhere in lib.typ, I do not know which theme the user has selected.
  • To what I understood from the documentation, I could use a state variable for color_lib, which would allow to mutate it inside a function, but it sounds completely overkill to me : conceptually, I don’t want to be able to change the theme mid document : theme should be an unmutable constant. In practice, it also forces me to put context statements everytime I want a color, which makes the code pretty ugly.

Since I’m very new to typst, I’m not sure if I misunderstood some concept, if I’m really meant to use state/context everywhere, or if I’m missing something much simpler. Any help is very welcome :)

Hello. “Elsewhere in lib.typ” sounds strange if the template function is the only thing that user will use. The whole point of the template function is to include all the styling that is applied once.

If there are some styling wrapper functions, then you either have to provide an explicit argument for colors dictionary or use state.

Thanks for the answer and pointing me towards relevants discussions. Sorry I did not find them before asking.

I found pretty much what I wanted at What is the best way to retrieve template argument outside the template? - #3 by PgBiel . Basically, the trick is to have a setup function in which I initialize the dictionnary color_lib and update it based on the theme input param. The function also spits out a template function, and both are given the user.

I don’t know how standard this trick is, but the question seems to have surfaced many times in pretty much the same way. Maybe it could be added to the (overwise very helpful) tutorial Making a Template – Typst Documentation .

1 Like