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.
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.
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 ...
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
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
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.
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
Some important things to note here:
[First Title])
which makes it size 20pt font and blue and puts it in my textflow. This is
different than using a variable, since I need to define some instructions of
what to do with content instead of just making content one time and reusing it.
let my_title(title), I am telling Typst
to make a function called my_title that takes a
single argument, title, which is then used within the function.
In this case, this title argument is meant to be some markdown content.
title size 20pt font and blue,
I can use the text function like this: text(20pt, blue, title).
Since this one function call does everything we want to achieve with my_title,
we can just put it directly after the equals sign. I will discuss later
what to do if you needed to do more than just one function call for your function.
title to do something with.
I start with a pound and then give the function my input: #my_title[First Title] (note that
this is the same as #my_title([First Title])). That means we are setting title
to take on the value [First Title] within the definition of my_title.
It is set to be blue and font size 20pt, then put to the screen.
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 ...
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 ...
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.
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
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.
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 description | MM, 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:
title and time,
is bolded, hence the asterisks (**). Inside that line, we include title,
then include a horizontal space with a fractional length (as explained in the
previous chapter)
which pushes the time as far right as possible. Finally, a line break is included.
company name is italicized, which can be done easily using underscores. Since
I am using underscores around a variable name, I need to specify when the variable name has ended
using a semicolon (;). Alternatively, I could have added a space after the variable name and before
the second underscore, or used the emph function. Then another line break is included.
10pt font.
1cm is inserted to space between entries.
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.]
)
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]
I have introduced a few new ideas here:
#) before
the let keyword. We can also define variables within functions
if using brackets ([]), as shown below.
{}), you can
type the names of variables and functions without using pound (#)
at all. Here, the last five lines all produce some kind of content
(the first line just defines a variable, so it does not generate content on its own),
and those are all appended together to make a single chunk of content
which is put to the screen.
#),
so I preceded it with a backslash to let Typst know I was not trying to switch to
code mode.
{}), I could either include it in markdown (i.e., use [ / ]),
or equivalently use the linebreak() function.
{first second third} would fail, as you need to have each item on
its own line. If you wanted to compress everything into one line, you can do so
using a semicolon (;) like {first; second; third}. The
semicolon serves this purpose within code mode.
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}
]
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.
For each of the following, write a function that will return the shown result.
#section_title[My Title][a meaningful subtitle]
section_title should take two arguments, the first being sized to
20pt font and the second being italicized, both right-aligned.
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).
#colored_text[first][second][third]
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.
[]) when sizing the font,
do not include spaces between the uses of the arguments.
#words_with_fun_pattern[top][middle][bottom]
x or y)
or in both directions.
words_with_fun_pattern should take three arguments:
700% in the x direction
and 200% in the y direction,
and then should be centered.
x and y,
and then center it to get the correct effect.