From 6daae2e292ad148a458c04d9a244021373063649 Mon Sep 17 00:00:00 2001 From: Andrew Voynov Date: Thu, 12 Jun 2025 09:03:09 +0300 Subject: [PATCH] Refactor "Making a Template" tutorial --- docs/tutorial/4-template.md | 243 +++++++++++++++--------------------- 1 file changed, 102 insertions(+), 141 deletions(-) diff --git a/docs/tutorial/4-template.md b/docs/tutorial/4-template.md index 7542cd6e4..6432ba6f7 100644 --- a/docs/tutorial/4-template.md +++ b/docs/tutorial/4-template.md @@ -25,7 +25,10 @@ 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. +the term we are amazed by cannot be separated from its sparkles by a line +break. Alternatively, you can use a +[shorthand](https://typst.app/docs/reference/symbols/#shorthands) +for a no-break space and write `{[✨~#term~✨]}`. 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 @@ -34,7 +37,7 @@ parameter isn't given. ```example #let amazed(term, color: blue) = { - text(color, box[✨ #term ✨]) + text(color)[✨~#term~✨] } You are #amazed[beautiful]! @@ -43,7 +46,7 @@ I am #amazed(color: purple)[amazed]! Templates now work by wrapping our whole document in a custom function like `amazed`. But wrapping a whole document in a giant function call would be -cumbersome! Instead, we can use an "everything" show rule to achieve the same +cumbersome! Instead, we can use an "global" show rule to achieve the same with cleaner code. To write such a show rule, put a colon directly after the show keyword and then provide a function. This function is given the rest of the document as a parameter. The function can then do anything with this content. @@ -52,7 +55,7 @@ just pass it by name to the show rule. Let's try it: ```example >>> #let amazed(term, color: blue) = { ->>> text(color, box[✨ #term ✨]) +>>> text(color)[✨~#term~✨] >>> } #show: amazed I choose to focus on the good @@ -68,69 +71,56 @@ powerful. ## Embedding set and show rules { #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. +within a code block in our function and then insert the document into +that code block. ```example -#let template(doc) = [ - #set text(font: "Inria Serif") - #show "something cool": [Typst] - #doc -] +#let template(doc) = { + set text(font: "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. +Just like we already discovered in the previous chapter, set rules will apply +to everything within their scope. Since the global 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. + +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 `#`. This also +removes the implicit spaces that are naturally introduced in the markup mode. +In exchange, we cannot write markup directly in the code block anymore. + +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 page( paper: "us-letter", ->>> margin: auto, - header: align( - right + horizon, - title - ), + header: align(right, title), columns: 2, <<< ... ) set par(justify: true) set text( + 11pt, font: "Libertinus Serif", - size: 11pt, ) // Heading show rules. <<< ... ->>> show heading.where( ->>> level: 1 ->>> ): it => block( ->>> align(center, ->>> text( ->>> 13pt, ->>> weight: "regular", ->>> smallcaps(it.body), ->>> ) ->>> ), ->>> ) ->>> show heading.where( ->>> level: 2 ->>> ): it => box( ->>> text( ->>> 11pt, ->>> weight: "regular", ->>> style: "italic", ->>> it.body + [.], ->>> ) ->>> ) +>>> show heading.where(level: 1): set align(center) +>>> show heading.where(level: 1): set text(13pt, weight: "regular") +>>> show heading.where(level: 1): it => block(smallcaps(it.body)) +>>> +>>> show heading.where(level: 2): set text(11pt, weight: "regular", style: "italic") +>>> show heading.where(level: 2): it => [#it.body.] doc } @@ -154,24 +144,17 @@ previous chapter. >>> #lorem(200) ``` -We copy-pasted most of that code from the previous chapter. The two differences -are this: +We copied most of that code from the previous chapter. However, now we wrapped +everything in the function `conf` using a global show rule. The function applies +a few set and show rules and echoes the content it has been passed at the end. -1. We wrapped everything in the function `conf` using an everything show rule. - The function applies a few set and show rules and echoes the content it has - been passed at the end. - -2. 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 in the code block anymore. - -Also note where the title comes from: We previously had it inside of a variable. +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. To do so, we passed a closure (that's a function without a name that is used right -away) to the everything show rule. We did that because the `conf` function -expects two positional arguments, the title and the body, but the show rule will -only pass the body. Therefore, we add a new function definition that allows us -to set a paper title and use the single parameter from the show rule. +away) to the global show rule. We did that because the `conf` function expects +two positional arguments: the title and the body, but the show rule will only +pass the body. Therefore, we add a new function definition that allows us to set +a paper title and use the single parameter from the show rule. ## Templates with named arguments { #named-arguments } Our paper in the previous chapter had a title and an author list. Let's add @@ -230,6 +213,9 @@ multiple arguments for the grid. We can do that by using the [`spread` operator]($arguments). It takes an array and applies each of its items as a separate argument to the function. +Let's also include some PDF metadata. We can achieve this by using +the [`document`] function and specifying fields such as `title` and `author`. + The resulting template function looks like this: ```typ @@ -239,31 +225,33 @@ The resulting template function looks like this: abstract: [], doc, ) = { + set document(title: title, author: authors.map(author => author.name)) // Set and show rules from before. ->>> #set page(columns: 2) <<< ... - set align(center) - text(17pt, title) + { + set align(center) + set par(justify: false) - 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) - ]), - ) + block(text(17pt, strong(title))) - par(justify: false)[ - *Abstract* \ - #abstract - ] + 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) + ]), + ) + + strong[Abstract] + linebreak() + abstract + } - set align(left) doc } ``` @@ -291,72 +279,45 @@ call. >>> abstract: [], >>> doc, >>> ) = { ->>> set text(font: "Libertinus Serif", 11pt) ->>> set par(justify: true) ->>> set page( ->>> "us-letter", ->>> margin: auto, ->>> header: align( ->>> right + horizon, ->>> title ->>> ), ->>> numbering: "1", ->>> columns: 2, ->>> ) +>>> set document(title: title, author: authors.map(author => author.name)) +>>> set page( +>>> "us-letter", +>>> header: align(right, title), +>>> numbering: "1", +>>> columns: 2, +>>> ) +>>> set par(justify: true) +>>> set text(11pt, font: "Libertinus Serif") >>> ->>> show heading.where( ->>> level: 1 ->>> ): it => block( ->>> align(center, ->>> text( ->>> 13pt, ->>> weight: "regular", ->>> smallcaps(it.body), ->>> ) ->>> ), ->>> ) ->>> show heading.where( ->>> level: 2 ->>> ): it => box( ->>> text( ->>> 11pt, ->>> weight: "regular", ->>> style: "italic", ->>> it.body + [.], ->>> ) ->>> ) +>>> show heading.where(level: 1): set align(center) +>>> show heading.where(level: 1): set text(13pt, weight: "regular") +>>> show heading.where(level: 1): it => block(smallcaps(it.body)) >>> ->>> place( ->>> top, ->>> float: true, ->>> scope: "parent", ->>> clearance: 2em, ->>> { ->>> 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 ->>> ] ->>> }, ->>> ) ->>> doc ->>>} +>>> show heading.where(level: 2): set text(11pt, weight: "regular", style: "italic") +>>> show heading.where(level: 2): it => [#it.body.] +>>> +>>> place(top + center, float: true, scope: "parent", clearance: 2em, { +>>> set par(justify: false) +>>> block(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) +>>> ]), +>>> ) +>>> strong[Abstract] +>>> linebreak() +>>> abstract +>>> }) +>>> doc +>>> } <<< #import "conf.typ": conf #show: conf.with( - title: [ - Towards Improved Modelling - ], + title: [Towards Improved Modelling], authors: ( ( name: "Theresa Tungsten", @@ -397,7 +358,7 @@ 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 you found a bug, please let us know +questions, suggestions, or you found a bug, please let us know in the [Forum](https://forum.typst.app/), on our [Discord server](https://discord.gg/2uDybryKPe), on [GitHub](https://github.com/typst/typst/),