diff options
Diffstat (limited to 'docs/src/tutorial/4-template.md')
| -rw-r--r-- | docs/src/tutorial/4-template.md | 378 |
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). |
