summaryrefslogtreecommitdiff
path: root/docs/src/tutorial/4-template.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/src/tutorial/4-template.md')
-rw-r--r--docs/src/tutorial/4-template.md378
1 files changed, 378 insertions, 0 deletions
diff --git a/docs/src/tutorial/4-template.md b/docs/src/tutorial/4-template.md
new file mode 100644
index 00000000..db012db7
--- /dev/null
+++ b/docs/src/tutorial/4-template.md
@@ -0,0 +1,378 @@
+---
+description: Typst's tutorial.
+---
+
+# Making a Template
+In the previous three chapters of this tutorial, you have learned how to write a
+document in Typst, apply basic styles, and customize its appearance in-depth to
+comply with a publisher's style guide. Because the paper you wrote in the
+previous chapter was a tremendous success, you have been asked to write a
+follow-up article for the same conference. This time, you want to take the style
+you created in the previous chapter and turn it into a reusable template. In
+this chapter you will learn how to create a template that you and your team can
+use with just one show rule. Let's get started!
+
+## A toy template
+In Typst, templates are functions in which you can wrap your whole document. To
+learn how to do that, let's first review how to write your very own functions.
+They can do anything you want them to, so why not go a bit crazy?
+
+```example
+#let amazed(term) = box[✨ #term ✨]
+
+You are #amazed[beautiful]!
+```
+
+This function takes a single argument, `term`, and returns a content block with
+the `term` surrounded by sparkles. We also put the whole thing in a box so that
+the term we are amazed by cannot be separated from its sparkles by a line break.
+
+Many functions that come with Typst have optional named parameters. Our
+functions can also have them. Let's add a parameter to our function that lets us
+choose the color of the text. We need to provide a default color in case the
+parameter isn't given.
+
+```example
+#let amazed(term, color: blue) = {
+ text(color, box[✨ #term ✨])
+}
+
+You are #amazed[beautiful]!
+I am #amazed(color: purple)[amazed]!
+```
+
+Templates now work by using an "everything" show rule that applies the custom
+function to our whole document. Let's do that with our `amazed` function.
+
+```example
+>>> #let amazed(term, color: blue) = {
+>>> text(color, box[✨ #term ✨])
+>>> }
+#show amazed
+I choose to focus on the good
+in my life and let go of any
+negative thoughts or beliefs.
+In fact, I am amazing!
+```
+
+Our whole document will now be passed to the `amazed` function, as if we
+wrapped it around it. This is not especially useful with this particular
+function, but when combined with set rules and named arguments, it can be very
+powerful.
+
+## Embedding set and show rules
+To apply some set and show rules to our template, we can use `set` and `show`
+within a content block in our function and then insert the document into
+that content block.
+
+```example
+#let template(doc) = [
+ #set text("Inria Serif")
+ #show "something cool": [Typst]
+ #doc
+]
+
+#show template
+I am learning something cool today.
+It's going great so far!
+```
+
+Just like we already discovered in the previous chapter, set rules will apply to
+everything within their content block. Since the everything show rule passes our
+whole document to the `template` function, the text set rule and string show
+rule in our template will apply to the whole document. Let's use this knowledge
+to create a template that reproduces the body style of the paper we wrote in the
+previous chapter.
+
+```example
+#let conf(title, doc) = {
+ set text(11pt, "Linux Libertine")
+ set par(justify: true)
+ set page(
+ paper: "us-letter",
+>>> margin: auto,
+ header: align(
+ right + horizon,
+ title
+ ),
+<<< ...
+ )
+
+ // Heading show rules.
+<<< ...
+>>> show heading.where(
+>>> level: 1
+>>> ): it => block(
+>>> align(center,
+>>> text(
+>>> 13pt,
+>>> weight: "regular",
+>>> smallcaps(it.title),
+>>> )
+>>> ),
+>>> )
+>>> show heading.where(
+>>> level: 2
+>>> ): it => box(
+>>> text(
+>>> 11pt,
+>>> weight: "regular",
+>>> style: "italic",
+>>> it.title + [.],
+>>> )
+>>> )
+
+ columns(2, doc)
+}
+
+#show doc => conf([Paper title], doc)
+
+= Introduction
+#lorem(90)
+
+<<< ...
+>>> == Motivation
+>>> #lorem(140)
+>>>
+>>> == Problem Statement
+>>> #lorem(50)
+>>>
+>>> = Related Work
+>>> #lorem(200)
+```
+
+We copy-pasted most of that code from the previous chapter. The only two
+differences are that we wrapped everything in the function `conf` and are
+calling the columns function directly on the `doc` argument as it already
+contains the content of the document. Moreover, we used a curly-braced code
+block instead of a content block. This way, we don't need to prefix all set
+rules and function calls with a `#`. In exchange, we cannot write markup
+directly into it anymore.
+
+Also note where the title comes from: We previously had it inside of a variable.
+Now, we are receiving it as the first parameter of the template function.
+Thus, we must specify it in the show rule where we call the template.
+
+## Templates with named arguments
+Our paper in the previous chapter had a title and an author list. Lets add these
+things to our template. In addition to the title, we want our template to accept
+a list of authors with their affiliations and the paper's abstract. To keep
+things readable, we'll add those as named arguments. In the end, we want it to
+work like this:
+
+```typ
+#show doc => conf(
+ title: [Towards Improved Modelling],
+ authors: (
+ (
+ name: "Theresa Tungsten",
+ affiliation: "Artos Institute",
+ email: "tung@artos.edu",
+ ),
+ (
+ name: "Eugene Deklan",
+ affiliation: "Honduras State",
+ email: "e.deklan@hstate.hn",
+ ),
+ ),
+ abstract: lorem(80),
+ doc,
+)
+
+...
+```
+
+Let's build this new template function. First, we add a default value to the
+`title` argument. This way, we can call the template without specifying a title.
+We also add the named `authors` and `abstract` parameters with empty defaults.
+Next, we copy the code that generates title, abstract and authors from the
+previous chapter into the template, replacing the fixed details with the
+parameters.
+
+The new `authors` parameter expects an [array]($type/array) of
+[dictionaries]($type/dictionary) with the keys `name`, `affiliation` and
+`email`. Because we can have an arbitrary number of authors, we dynamically
+determine if we need one, two or three columns for the author list. First, we
+determine the number of authors using the [`.len()`]($type/array.len) method on
+the `authors` array. Then, we set the number of columns as the minimum of this
+count and three, so that we never create more than three columns. If there are
+more than three authors, a new row will be inserted instead. For this purpose,
+we have also added a `row-gutter` parameter to the `grid` function. Otherwise,
+the rows would be too close together. To extract the details about the authors
+from the dictionary, we use the [field access syntax]($scripting/#fields).
+
+We still have to provide an argument to the grid for each author: Here is where
+the array's [`map` method]($type/array.map) comes in handy. It takes a function
+as an argument that gets called with each element of the array. We pass it a
+function that formats the details for each author and returns a new array
+containing content values. We've now got one array of values that we'd like to
+use as multiple arguments for the grid. We can do that by using the
+[`spread` operator]($type/arguments). It takes an array and applies each of its elements as a separate argument to the function.
+
+The resulting template function looks like this:
+
+```typ
+#let conf(
+ title: none,
+ authors: (),
+ abstract: [],
+ doc,
+) = {
+ // Set and show rules from before.
+<<< ...
+
+ set align(center)
+ text(17pt, title)
+
+ let count = authors.len()
+ let ncols = calc.min(count, 3)
+ grid(
+ columns: (1fr,) * ncols,
+ row-gutter: 24pt,
+ ..authors.map(author => [
+ {author.name} \
+ {author.affiliation} \
+ #link("mailto:" + author.email)
+ ]),
+ )
+
+ par(justify: false)[
+ *Abstract* \
+ #abstract
+ ]
+
+ set align(left)
+ columns(2, doc)
+}
+```
+
+## A separate file
+Most of the time, a template is specified in a different file and then imported
+into the document. This way, the main file you write in is kept clutter free and
+your template is easily reused. Create a new text file in the file panel by
+clicking the plus button and name it `conf.typ`. Move the `conf` function
+definition inside of that new file. Now you can access it from your main file by
+adding an import before the show rule. Name the function that you want to import
+from another file between the `{import}` and `{from}` keywords and specify the
+path of the file after the `{from}` keyword.
+
+```example
+>>> #let conf(
+>>> title: none,
+>>> authors: (),
+>>> abstract: [],
+>>> doc,
+>>> ) = {
+>>> set text(11pt, "Linux Libertine")
+>>> set par(justify: true)
+>>> set page(
+>>> "us-letter",
+>>> margin: auto,
+>>> header: align(
+>>> right + horizon,
+>>> title
+>>> ),
+>>> footer: page => align(
+>>> center + horizon,
+>>> [#page]
+>>> ),
+>>> )
+>>>
+>>> show heading.where(
+>>> level: 1
+>>> ): it => block(
+>>> align(center,
+>>> text(
+>>> 13pt,
+>>> weight: "regular",
+>>> smallcaps(it.title),
+>>> )
+>>> ),
+>>> )
+>>> show heading.where(
+>>> level: 2
+>>> ): it => box(
+>>> text(
+>>> 11pt,
+>>> weight: "regular",
+>>> style: "italic",
+>>> it.title + [.],
+>>> )
+>>> )
+>>>
+>>> set align(center)
+>>> text(17pt, title)
+>>>
+>>> let count = calc.min(authors.len(), 3)
+>>> grid(
+>>> columns: (1fr,) * count,
+>>> row-gutter: 24pt,
+>>> ..authors.map(author => [
+>>> {author.name} \
+>>> {author.affiliation} \
+>>> #link("mailto:" + author.email)
+>>> ]),
+>>> )
+>>>
+>>> par(justify: false)[
+>>> *Abstract* \
+>>> #abstract
+>>> ]
+>>>
+>>> set align(left)
+>>> columns(2, doc)
+>>>}
+<<< #import conf from "conf.typ"
+
+#show doc => conf(
+ title: [
+ Towards Improved Modelling
+ ],
+ authors: (
+ (
+ name: "Theresa Tungsten",
+ affiliation: "Artos Institute",
+ email: "tung@artos.edu",
+ ),
+ (
+ name: "Eugene Deklan",
+ affiliation: "Honduras State",
+ email: "e.deklan@hstate.hn",
+ ),
+ ),
+ abstract: lorem(80),
+ doc,
+)
+
+= Introduction
+#lorem(90)
+
+== Motivation
+#lorem(140)
+
+== Problem Statement
+#lorem(50)
+
+= Related Work
+#lorem(200)
+```
+
+We have now converted the conference paper into a reusable template for that
+conference! Why not share it on
+[Typst's Discord server](https://discord.gg/2uDybryKPe) so that others can use
+it too?
+
+## Review
+Congratulations, you have completed Typst's Tutorial! In this section, you have
+learned how to define your own functions and how to create and apply templates
+that define reusable document styles. You've made it far and learned a lot. You
+can now use Typst to write your own documents and share them with others.
+
+We are still a super young project and are looking for feedback. If you have any
+questions, suggestions or found a bug, please let us know on
+[Typst's Discord server](https://discord.gg/2uDybryKPe), on our
+[contact form](https://typst.app/contact), or on
+[social media.](https://twitter.com/typstapp)
+
+So what are you waiting for? Go forth and write something! Or, if you haven't
+got access yet, [sign up for the wait list](https://typst.app).