typst

How to use if condition in template function in Typst


I would like to use an if-condition in a template function to trigger dark mode. The template function has a boolean value dark that defaults to false. The following code works:

set page(fill: rgb("333333")) if dark
set text(fill: rgb("fdfdfd")) if dark
set text(font: "Source Sans Pro") if dark

However, when I try to wrap all three lines into an if-condition, I get a blank page (set in dark mode) and then the rest of the document in light mode instead. What am I doing wrong here?

  if dark {
    set page(fill: rgb("333333"))
    set text(fill: rgb("fdfdfd"))
    set text(font: "Source Sans Pro")
  }

Solution

  • set rules only work in their scope and child scopes. The if statement creates a new scope, meaning set rules are only active at that level:

    if dark {
        set page(fill: rgb("333333"))
        // "set" rule is active from here
        ....
    } // to here, where the scope ends
    

    You can get around this using set-if rules:

    set page(fill: rgb("333333")) if dark
    set text(fill: rgb("fdfdfd"), font: "Source Sans Pro") if dark
    

    But it is inconvenient to do this for multiple rules in a row or when you rely on nested conditions. For more complicated scenarios, I use the following approach:

    // Define non-dark mode variables in an outer scope
    let (page-fill, text-fill, font) = (none, black, "New Computer Modern")
    if dark {
        // Reuse variables from an outer scope, so changes persist
        (page-fill, text-fill, font) = (rgb("333333"), rgb("fdfdfd"), "Source Sans Pro")
    }
    // Apply set rules in parent scope
    set page(fill: page-fill)
    set text(fill: text-fill, font: font)