02| Text Formatting

Miles Robertson, 08.12.25

Introduction

In Typst, you can get a long way to making basic documents with markdown mode. However, fine-grained control of text appearance is often necessary. Typst offers an array of functions that let users change the appearance of text. In this section, I will cover the most commonly-used functions used for text formatting in Typst.

Bolding, Italicizing, and Intro to Code Mode

As covered in the previous chapter, you can quickly bold or italicize words using *asterisks* or _underscores_, respectively. However, these markdown-mode symbols have some limitations. For example, you cannot style just part of a word: *antidis*establishment_arianism_ does not produce antidisestablishmentarianism. For more flexibitily, you can instead use the functions strong and emph. These two names, though perhaps unfamiliar, are borrowed from HTML, the language used for websites.

This sentence has its last word #strong([bolded]).
This sentence has its last word #emph([italicized]).
This sentence has its last word with both #strong([sty])#emph([les]).
example using both emph and strong

Note that these functions have the same form as lorem did in the previous chapter: a pound symbol, followed by the function name, and then some stuff in parentheses. However, there is a difference here: the words we're trying to italicize or bold are in brackets ([]). To explain why, we will quickly re-examine why we used the pound sign in the first place.

While in markdown mode, we use the pound symbol to signal to Typst that we are leaving markdown mode and entering "code mode", which is how we access all functions. However, the input to the strong and emph functions needs to be markdown content, and markdown content needs to be in brackets while you're in code mode.

Because it is so common to give a function some markdown content, you can use a shorthand provided by Typst and move that markdown content to the outside of the parentheses, so #strong([bolded]) becomes #strong()[bolded]. Some functions do accept more arguments inside of the parentheses, but if there is nothing in the parentheses, they can be ommitted altogether, as follows:

This sentence has its last word #strong[bolded].
This sentence has its last word #emph[italicized].
This sentence has its last word with both #strong[sty]#emph[les].

This structure of switching between "markdown mode" and "code mode" is a great way to put all the content (markdown) and all the manipulation of that content (code) in one document, which makes Typst a very powerful tool.

Underline Function (and Absolute Length)

Unsurprisingly, the function used to underline content is called underline. Here is how you can use it:

This sentence fragment is not underlined,
#underline[but this one is].
example of underline

The documentation for this function, linked in the previous paragraph, shows that there are several arguments we can give this function to specify how we want this underline to appear. For each of the possible arguments, the documentation includes an example if you click the > View Example link. To start exposing you to how arguments of functions are used in Typst, I will go over some examples here, but I encourage you to look at the documentation.

First, I will talk about the offset argument. With it, you can control the distance between the underline and the text you are underlining. A few examples:

#underline[this line has a normal underline.]

#underline(offset:5pt)[this line has an underline that's further away.]

#underline(offset:-1pt)[this line's underline is bumped up]
example of offset argument

Although in regular text flow you might not think too much about this attribute, the offset argument can be helpful to control the style of your document. Certain fonts might look better if you change this argument a little bit.

The offset argument can receive a length value. This is a special type in Typst that gives an absolute measurement of distance (as opposed to a fractional or relative distance). In the example above, I used points (pt) as the unit of length, but you can also specify lengths in millimeters (mm), centimeters (cm), inches (in), and relative to the current font size (em), depending on whatever is most convenient for you. The length type is described more on the documentation page linked in the first sentence of this paragraph.

To reiterate what was discussed in the section above, all of the following are equivalent in Typst. You can give markdown content to any function in Typst either within the parentheses or by putting it in brackets at the end of the function call.

#underline(offset: 2pt, [this line is the same as the other one.])

#underline(offset: 2pt)[this line is the same as the other one.]
example of offset argument

I will briefly cover one more argument of this function, one that comes up in tons of places in Typst: stroke. It refers to the color, thickness, and style of the underline itself. You will see it in the documentation for the underline function, but it is so common that there is a full page on the topic in the documentation. This will come up more later, but for now, I will give you a few examples:

#underline(stroke: 2pt)[this sentence has a thicker underline.]

#underline(stroke: red)[this sentence has a red underline.]

#underline(stroke: red+2pt)[this sentence has a thick, red underline.]
example of stroke argument

I'll keep this part brief, but I will point out a few things now:

Setting Font & its Size/Color, and Intro to Set Rules

One of the more obvious functions of a typesetting language is to able to change its font and some attributes about it. Typst provides the text function for this purpose. It can take several arguments, some of which are specified by names (like offset: 2pt and stroke: red seen in the underline function), and some of which are not. Here are a few examples of how the text function can be used:

There are many ways to use the `text` function. 
You can specify #text(blue)[the color] and #text(20pt)[the size]
of text, and can even do #text(red, 16pt)[both at the same time]. 
You can #text(font: "Barlow")[specify the font], 
#text(stroke:olive+0.5pt)[set the stroke, or even 
use #text(yellow)[`text` within `text` to combine styles]]. 
This can also be used to control 
#text(spacing:30pt)[the spacing between words] and
#text(tracking:2pt)[the spacing between characters].
example of text function

Although I will mostly leave you to compare the code and the output to see how the arguments used change the text style, I will make a few notes:

There are tons of cases where you want to apply a styling to text for the whole document. For example, you might want to specify 20pt font for everything. You can "wrap" your whole document to be inside a text function call, but you can also use a set rule on text which ensures that all text from that point forward uses the specified style. For example:

This text is the default size for Typst.
    
#set text(20pt)

All text from this point forward is by default `20pt` font. 
That's because Typst uses the `text` function for all text 
in the background, even if you don't actually call the `text` 
function. #text(12pt)[That doesn't mean you can't change that 
style for a little while by using the `text` function again], though.

#set text(16pt, red)

You can also use `set` rules multiple times in a document, setting
a new value to be used from that point forward.
example of text set rule

These set rules are really powerful for changing the default value for arguments of these functions. In many cases, Typst documents start with a few set rules, which are then applied to the whole document. That means that you can type without needing to worry about using the text function as often, and the result will be still be formatted just as you like.

Alignment

Aligning content, whether it be text, images, tables, or anything else, can be done with the align function. A few examples:

This text is not used in an `align` function call.

#align(left)[This text is used in one, but it is aligned to the left, so nothing looks different.]

#align(center)[This text is centered.]

#align(right)[This text is right-aligned.]

#align(bottom)[This text is aligned to the bottom of the page.]
example of alignment

Alignments can also be combined using +, as seen in the following examples:

#align(horizon)[horizontally-aligned text]
example of alignment
#align(horizon+center)[horizontally-aligned and centered text]
example of alignment

If you wanted to make a title that was centered and sized to 15pt font, you need to use both the align function and the text function. The order does not matter, but let's assume you have decided to size the text first and then center it. There are several equivalent ways you could acheive this:

#align(center)[#text(15pt)[My Title]] // option 1

#align(center)[#text(15pt, [My Title])] // option 2

#align(center, [#text(15pt, [My Title])]) // option 3

#align(center, text(15pt, [My Title])) // option 4

#align(center, text(15pt)[My Title]) // option 5
example of alignment

As you can see, the output of all of these options give the same result. When starting with Typst, these options can appear confusing. In case you can't pick apart what is happening here, I will try to point out some ideas specifically. To start, I will again mention that when you want to pass content to a function as an argument, you can either (1) give it to the function inside of the parentheses, or (2) put it in brackets immediately after the parentheses. Those two approaches are showed several times across these five options:

There is one more thing happening here: Up till now, every time that we have used a function, it has needed to be immediately preceded by a pound sign (#) to alert Typst that you are moving into code mode. I followed that pattern in options 1, 2, and 3. However, this is only required if you are currently in markdown mode (either typing right into the document outside of any function, or while typing within brackets, []). In options 4 and 5, I am showing that if you are already in code mode (which is the case in these options because we are within a function call and not within brackets), you do not need to use the pound sign.

All in all, option 5 is my personal favorite in most cases because it is the shortest.

Horizontal/Vertical Spacing (and Relative & Fractional Lengths)

Apart from aligning, you can also use the vertical and horizontal space functions to position content, which are simply called v and h, respectively. In the previous chapter, I mentioned that hitting the space bar multiple times in content get condensed into a single space. If you do need to add more space, these functions are the way to do it. They can take an absolute length as input, which is seen here:

Before horizontal space #h(1cm) after horizontal space.

Before vertical space #v(1cm) after vertical space.
example of v and h functions

These spacing functions can take in two other types of lengths, relative/ratio and fraction. The first one, relative, uses a percentage to indicate what portion of the relevant direction to use. Here are a few examples:

#v(30%) This sentence contains a #h(20%) horizontal gap 
that is 20% of the available width of the page (not including 
margin). Meanwhile, this whole paragraph starts 30% down the 
available height of the page because it is preceded by a vertical gap.
example of v and h functions with relative lengths

You can even add or subtract a percentage and an absolute length when desired to get a relative length, such as 100% - 50pt, which will calculate its final value based on the context its used in.

The last type of length is fractional. The idea here is that there is some amount of space left (either horizontally or vertically) after taking the content present into account, and this space can be divided into fractions. Some examples:

The space in this line #h(1fr) pushes everything else as 
far right as possible.

The number used before `fr` only matters #h(100fr) if
multiple fractional lengths are used.

For example, #h(1fr) this gap is half as big as #h(2fr) this gap.

#v(1fr) It can also be used to push content as far down as possible.
example of v and h functions with relative lengths

Conclusion

In this chapter, I introduced basic text formatting (bold, italicize, underline, color, size, etc.), spacing (alignment and vertical/horizontal space), lengths (absolute, relative, and fractional), and set rules. These powerful tools can go a long ways by themselves and are fundamental to more advanced topics later.


Practice

Replicate Document

Replicate the following in your own Typst document:

example document

Make sure you satisfy the following requirements. If there are multiple ways to satisfy them, pick whichever you like.

As in the last chapter, I will provide all the raw text for you to format. Just click the text to select all of it at once.

MY JOURNEY
Table of Contents
Chapter 1 - The biggest reason I stopped eating dirt   pg. 1
Chapter 2 - Aw dang I relapsed and am again eating dirt   pg. 10
Chapter 3 - Don't worry guys I figured it out   pg. 15

Complete the Following Tasks

Use what you have learned so far to complete the following in a Typst document. Insert a paragraph break into your document between each task.

  1. Insert the phrase Test 1.1.
  2. Insert the phrase Test 1.2, centered.
  3. Insert the phrase Test 1.3, centered and red.
  4. Insert the phrase Test 1.4, centered, red, and 8pt font.
  5. Insert the phrase Test 2.1, followed by a line break, followed by the phrase secondary title.
  6. Insert the phrase Test 2.2, then line break, then secondary title, where the secondary title is italicized.
  7. Repeat previous task, except make it Test 2.3 and make everything centered and red.
  8. Repeat task for Test 2.2, except make it Test 2.4, make everything centered and red, and make just the secondary title 8pt font.

Following the above instructions should create an output that looks like this:

result of following steps

If you feel stuck on how to do this, look at the following example to try to achieve what you need:

#align(right, 
  text(purple)[
    line one \ #text(16pt)[line two, larger] \ _line three, italicized_
  ]
) // could be done all in one line, but this way is easier to read
result of following steps