03| Variables and Functions

Miles Robertson, 08.14.25

Introduction

Practically all documents have repeating elements. Things like entries in a list, section headers, and new chapter pages require a document author to use the same kind of formatting over and over. If you are trying to create entries of a resumé, as discussed in the first chapter, making the same formatting for each entry would be repetitive and error-prone. Since these repeating elements are so common, Typst allows users to create their own variables and functions for use throughout documents.

Variables

Sometimes, the same chunk of content has to be used again and again. For example, say we are trying to create the following paragraph:

The grand façade of the opera house gleamed under the evening lights, its ornate details drawing the eye of every passerby. Beneath the elegant façade, however, the building's structure was showing signs of age, with cracks hidden just out of sight. For the city's residents, the opera house's façade symbolized both cultural pride and the delicate effort to preserve history.

Writing the word façade is a little more tedious to type on English keyboards because of the accented letter. Additionally, every time it appears in the paragraph, it is bolded. If you want to avoid copying the word over and over, you can make this easier for yourself by creating your own variable for the content:

#let facade = strong[façade] // could also use [*façade*] here

The grand #facade of the opera house gleamed under the evening 
lights, its ornate details drawing the eye of every passerby. 
Beneath the elegant #facade, however, the building's structure
was showing signs of age, with cracks hidden just out of sight. 
For the city's residents, the opera house's #facade symbolized 
both cultural pride and the delicate effort to preserve history.
example of using a variable

Apart from increased convenience, using this variable wherever you need it will make it easier if you ever need to change the styling of the word. This way, if you later wanted to change every instance of "façade" to be blue, it would be as easy as changing what the variable is assigned to a single time, instead of needing to change every use of the word "façade".

#let facade = text(blue, strong[façade])

// ... paragraph here ...
another example of using a variable

The variable's name I chose was facade, but I actually could have called it all sorts of things. Variable names can use letters, numbers, hyphens, and underscores (though they cannot start with a number or a hyphen). Therefore, we just as well could have called it word-with-accent or my_least_favorite_word_to_type. Then, the variable can be used wherever you would type its contents. However, if you need to include letters, numbers, hyphens, or underscores immediately after the content in a variable, you can use a semicolon ; to let Typst know that you are done typing the variable name. For example:

#let greeting = [howdy]

// no need for semicolon since variable is followed by a space
Well #greeting there partner

// no need for semicolon since ! cannot be part of a variable name
#greeting!

// uses semicolon which lets Typst know that the variable name is over
// and does not include the following underscore, and does not add a
// space after the output
_Hi #greeting;_

// Semicolon needed here to clarify that "-ho" is not part of
// the variable name
#greeting;-ho
example of using a variable with a semicolon

I will note that variables can store any object used in Typst, which can be helpful in many cases. Below, saving the font size and color in variables gives you the flexibility to edit them later to affect every place the variables have been used. However, read the next section about functions to see how this example could be made even easier.

#let title_size = 20pt
#let title_color = blue

#text(title_size, title_color)[First title]

A bunch of words here

#text(title_size, title_color)[Second title]

A bunch of words here too
example of using a variable for a length and a color

Intro to User-Defined Functions

User-defined functions in Typst are similar to variables, except they take inputs that specify what is returned, instead of just having one value. We have already seen many built-in functions (e.g., lorem, strong, text, etc.), but now I will discuss how to craft your own.

Intro Function Example, Part 1

To start with a simple example, imagine you want to create the same formatting for each section title of your document. Although this can be achieved in an even simpler way than what I will show here, it is a good example to introduce you to the basic ideas about functions. Here is one way to implement this idea to achieve the same output from the last example in the previous section:

#let my_title(title) = text(20pt, blue, title)

#my_title[First Title]

A bunch of words here

#my_title[Second Title]

A bunch of words here too
example of using a user-defined function

Some important things to note here:

Apart from making things look cleaner, the function is great because I can change it later if I want to adjust how things look across the whole document:

#let my_title(title) = align(center, text(20pt, blue, title))

// ... rest is same as in previous ...
example of using a user-defined function, changed

This user-defined function approach makes it so all the styling you do is done in one place, instead of everywhere it is used.

I could continue adjusting this, adding an underline with arguments offset and extent adjusted to achieve the style I want. Note that I included some new lines (i.e., hit the enter key) mid-way through the definition. That splits up what you have to look at into multiple lines instead of one.

#let my_title(title) = align(center, 
    underline(offset:3pt, extent: -5pt, text(20pt, blue, title))
)

// ... rest is same as in previous ...
example of using a user-defined function, changed again

One note here: in this way of styling the code, we first set the text size and color, then include an underline, and then center the text (reading inside-out, as coding languages do). But it could have actually been in any order, and the output would stay the same.

Intro Function Example, Part 2

Now, imagine that I later changed my mind that I want each chapter to say both a title and the date it was written on the line below it, both in blue. That is different from before because now I will need to give two things (a title and a date) instead of one. I can change my function definition to say #let my_title(title, date) so it expects a second input. That means I can use the function as #my_title([First Title], [01/01/1970]) to give the two inputs. Even easier, I can use the brackets-outside trick twice: #my_title[First Title][01/01/1970]. But, we still have to use that input date somewhere in the function for it to show up in the output.

How can we include both the title and the date in the function output? The first step is to think about how you would type it normally. If you had the title First Title and the data 01/01/1970, how could you style them according to the specifications given above? Taking inspiration from this problem in the previous chapter, here is how I might do it:

#align(center, 
  text(blue)[
    #text(20pt, underline(offset:3pt, extent: -5pt)[First Title]) \
    01/01/1970
  ]
) // this could be done on one line, but this way is more readable.
// The extra spaces & new lines on line 2, 3, and 4 are ignored 
// from the output, as discussed in Ch 1.

For kicks and giggles, I first applied the underline and then the new text size to First Title, the reverse of the order I did above.

Now, imagine if we used the variables title and date to represent [First title] and [01/01/1970], respectively. How could we change this code to use those variables instead of their values? Here is one way:

#let title = [First title]
#let date = [01/01/1970]

#align(center, 
  text(blue)[
    #text(20pt, underline(offset:3pt, extent: -5pt, title)) \
    #date
  ]
)

Since this was made thinking in terms of using two variables, this result can be used directly in the function:

#let my_title(title, date) = align(center, 
  text(blue)[
    #text(20pt, underline(offset:3pt, extent: -5pt, title)) \
    #date
  ]
)

#my_title[First Title][01/01/1970]

A bunch of words here

#my_title[Second Title][12/31/1999]

A bunch of words here too
example of using a user-defined function, now with two inputs

All in all, the definition we gave for the my_title function gives instructions for how to treat its two inputs title and date in the same way every time.

Second Function Example

I will reiterate the example explained in the first chapter of this tutorial: making resumé entries. A function will format each entry the same way, keeping a consistant style throughout. Here is one format you might hope to achieve:

Job descriptionMM, YYYY - MM, YYYY
Jobname, Inc.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed elit purus, fringilla ut magna ut, aliquet ultricies felis. Nullam nec nunc ultricies, gravida lectus volutpat, feugiat diam. Donec at sodales massa, nec sagittis lectus. Donec enim arcu, interdum et neque eu, auctor consequat ligula. Etiam ultricies ullamcorper sagittis.

This could be achieved as follows with the following function:

#let resume-entry(title, time, company, description) = [
  *#title #h(1fr) #time* \
  _#company;_ \
  #text(10pt, description)
  #v(1cm)
]

Before showing this function in use, let's break down what it is doing. First of all, it is not being directly given a function like align as was seen in the previous example. Instead, it is given brackets ([]) with all the stying happening within there. Here is a breakdown of the formatting used:

Here is the example of the function in use from the first chapter:

#let resume-entry(title, time, company, description) = [
  *#title #h(1fr) #time* \
  _#company;_ \
  #text(10pt, description)
  #v(1cm)
]

#resume-entry(
  [Customer Experience Associate],
  [2019 - 2023],
  [BrightMart Superstore, Atlanta, GA],
  [Helped train new hires on POS systems and service standards.]
)

#resume-entry(
  [Sales Floor Team Member],
  [2016 - 2019],
  [StyleHub Clothing Co., Charlotte, NC],
  [Recognized twice as Employee of the Month for outstanding service.]
)

#resume-entry(
  [Cashier & Customer Support],
  [2014 - 2016],
  [FreshHarvest Market, Tampa, FL],
  [Maintained smooth front-end operations during peak hours.]
)
example using the function

Third Function Example

In the previous examples, when there was more than one piece of content to format, we used markdown mode via the brackets ([]) to join it together, using variables and functions with the pound sign (#). However, you do not have to leave code mode at all by instead grouping lines of code together with braces ({}). This is sometimes a cleaner solution depending on the situation at hand. For example, say you want to make a function that takes three inputs and places them all on one line, with equal spacing around a hyphen between each input. This can be achieved as follows:

#let space_out(first, second, third) = {
  let gap_and_hyphen = [#h(1fr)-#h(1fr)]
  first
  gap_and_hyphen
  second
  gap_and_hyphen
  third
}

#space_out[item 1][item 2][item 3]

#space_out[second item 1][second item 2][second item 3]

#space_out[very long first item][\#2][\#3]
example using braces

I have introduced a few new ideas here:

Here are a few other ways I could have written the function, all of which give the same exact output. Look through them carefully to introduce yourself to what kind of flexibility you have when deciding how to write your function.

#let space_out(first, second, third) = {
  let gap_and_hyphen = {h(1fr);[-];h(1fr)} // used braces here instead
  first
  gap_and_hyphen
  second
  gap_and_hyphen
  third
}
#let space_out(first, second, third) = {
  let gap_and_hyphen = [#h(1fr)-#h(1fr)]
  // condensed into one line, spaces between optional
  first; gap_and_hyphen; second; gap_and_hyphen; third
}
#let space_out(first, second, third) = [
  // used markdown brackets instead
  #let gap_and_hyphen = {h(1fr);[-];h(1fr)} // used braces here instead
  #first
  #gap_and_hyphen
  #second
  #gap_and_hyphen
  #third
]
#let space_out(first, second, third) = [
  #let gap_and_hyphen = [#h(1fr)-#h(1fr)]
  // condensed into one line, spaces between should be omitted
  #first#gap_and_hyphen#second#gap_and_hyphen#third 
]
#let space_out(first, second, third) = [
  #let gap_and_hyphen = [#h(1fr)-#h(1fr)]
  // condensed into one line, use code mode braces
  #{first; gap_and_hyphen; second; gap_and_hyphen; third}
]

Conclusion

In this chapter, we have covered a lot of examples about user-defined variables and functions, and all the different ways they can be represented. In the next chapter, you will see how set and show rules can be used in a way that lets you style basic markdown throughout your document, often allowing you to avoid making functions at all.


Practice

Write Functions to Match Behavior

For each of the following, write a function that will return the shown result.

  1. #section_title[My Title][a meaningful subtitle]
    result 1
    • The function section_title should take two arguments, the first being sized to 20pt font and the second being italicized, both right-aligned.
    • Hint: You should use the align function only once. You can give the function its content either using brackets ([]) or braces ({}), separating the title and subtitle with a line break (not a paragraph break).
  2. #colored_text[first][second][third]
    result 2
    • The function colored_text should take three arguments, the first colored black, the second blue, and the third red, with no spaces in between. Additionally, the text should be size 30pt and centered.
    • Hint: If using markdown brackets ([]) when sizing the font, do not include spaces between the uses of the arguments.
  3. #words_with_fun_pattern[top][middle][bottom]
    result 3 Note: the scale function can be used to shrink or expand content, either in just one direction (x or y) or in both directions.
    • The function words_with_fun_pattern should take three arguments:
      • the first should be repeated ten times in a row with spaces in between.
      • the second should first be scaled 700% in the x direction and 200% in the y direction, and then should be centered.
      • the third should be repeated like the first one was, but should be right-aligned.
    • Hint: Although there are easier ways to repeat content, for now, just use the arguments ten times in your function definition. Also, as suggested above, when using the second argument in your function, you should first scale it using the scale function's x and y, and then center it to get the correct effect.