summaryrefslogtreecommitdiff
path: root/docs/reference/language
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-02-05 14:24:10 +0100
committerGitHub <noreply@github.com>2025-02-05 13:24:10 +0000
commit029ae4a5ea7ad1e52112ce26b6d38ce1750dae3f (patch)
tree0c56e8c9898efff5e6735750e4291605e25a0d3f /docs/reference/language
parent25f6a7ab161b2106c22a9997a68afee60ddb7412 (diff)
Export target docs (#5812)
Co-authored-by: Martin Haug <3874949+reknih@users.noreply.github.com>
Diffstat (limited to 'docs/reference/language')
-rw-r--r--docs/reference/language/context.md233
-rw-r--r--docs/reference/language/scripting.md373
-rw-r--r--docs/reference/language/styling.md145
-rw-r--r--docs/reference/language/syntax.md215
4 files changed, 966 insertions, 0 deletions
diff --git a/docs/reference/language/context.md b/docs/reference/language/context.md
new file mode 100644
index 00000000..bdd520f5
--- /dev/null
+++ b/docs/reference/language/context.md
@@ -0,0 +1,233 @@
+---
+description: |
+ How to deal with content that reacts to its location in the document.
+---
+
+# Context
+Sometimes, we want to create content that reacts to its location in the
+document. This could be a localized phrase that depends on the configured text
+language or something as simple as a heading number which prints the right
+value based on how many headings came before it. However, Typst code isn't
+directly aware of its location in the document. Some code at the beginning of
+the source text could yield content that ends up at the back of the document.
+
+To produce content that is reactive to its surroundings, we must thus
+specifically instruct Typst: We do this with the `{context}` keyword, which
+precedes an expression and ensures that it is computed with knowledge of its
+environment. In return, the context expression itself ends up opaque. We cannot
+directly access whatever results from it in our code, precisely because it is
+contextual: There is no one correct result, there may be multiple results in
+different places of the document. For this reason, everything that depends on
+the contextual data must happen inside of the context expression.
+
+Aside from explicit context expressions, context is also established implicitly
+in some places that are also aware of their location in the document:
+[Show rules]($styling/#show-rules) provide context[^1] and numberings in the
+outline, for instance, also provide the proper context to resolve counters.
+
+## Style context
+With set rules, we can adjust style properties for parts or the whole of our
+document. We cannot access these without a known context, as they may change
+throughout the course of the document. When context is available, we can
+retrieve them simply by accessing them as fields on the respective element
+function.
+
+```example
+#set text(lang: "de")
+#context text.lang
+```
+
+As explained above, a context expression is reactive to the different
+environments it is placed into. In the example below, we create a single context
+expression, store it in the `value` variable and use it multiple times. Each use
+properly reacts to the current surroundings.
+
+```example
+#let value = context text.lang
+#value
+
+#set text(lang: "de")
+#value
+
+#set text(lang: "fr")
+#value
+```
+
+Crucially, upon creation, `value` becomes opaque [content] that we cannot peek
+into. It can only be resolved when placed somewhere because only then the
+context is known. The body of a context expression may be evaluated zero, one,
+or multiple times, depending on how many different places it is put into.
+
+## Location context
+We've already seen that context gives us access to set rule values. But it can
+do more: It also lets us know _where_ in the document we currently are, relative
+to other elements, and absolutely on the pages. We can use this information to
+create very flexible interactions between different document parts. This
+underpins features like heading numbering, the table of contents, or page
+headers dependent on section headings.
+
+Some functions like [`counter.get`]($counter.get) implicitly access the current
+location. In the example below, we want to retrieve the value of the heading
+counter. Since it changes throughout the document, we need to first enter a
+context expression. Then, we use `get` to retrieve the counter's current value.
+This function accesses the current location from the context to resolve the
+counter value. Counters have multiple levels and `get` returns an array with the
+resolved numbers. Thus, we get the following result:
+
+```example
+#set heading(numbering: "1.")
+
+= Introduction
+#lorem(5)
+
+#context counter(heading).get()
+
+= Background
+#lorem(5)
+
+#context counter(heading).get()
+```
+
+For more flexibility, we can also use the [`here`] function to directly extract
+the current [location] from the context. The example below
+demonstrates this:
+
+- We first have `{counter(heading).get()}`, which resolves to `{(2,)}` as
+ before.
+- We then use the more powerful [`counter.at`] with [`here`], which in
+ combination is equivalent to `get`, and thus get `{(2,)}`.
+- Finally, we use `at` with a [label] to retrieve the value of the counter at a
+ _different_ location in the document, in our case that of the introduction
+ heading. This yields `{(1,)}`. Typst's context system gives us time travel
+ abilities and lets us retrieve the values of any counters and states at _any_
+ location in the document.
+
+```example
+#set heading(numbering: "1.")
+
+= Introduction <intro>
+#lorem(5)
+
+= Background <back>
+#lorem(5)
+
+#context [
+ #counter(heading).get() \
+ #counter(heading).at(here()) \
+ #counter(heading).at(<intro>)
+]
+```
+
+As mentioned before, we can also use context to get the physical position of
+elements on the pages. We do this with the [`locate`] function, which works
+similarly to `counter.at`: It takes a location or other [selector] that resolves
+to a unique element (could also be a label) and returns the position on the
+pages for that element.
+
+```example
+Background is at: \
+#context locate(<back>).position()
+
+= Introduction <intro>
+#lorem(5)
+#pagebreak()
+
+= Background <back>
+#lorem(5)
+```
+
+There are other functions that make use of the location context, most
+prominently [`query`]. Take a look at the
+[introspection]($category/introspection) category for more details on those.
+
+## Nested contexts
+Context is also accessible from within function calls nested in context blocks.
+In the example below, `foo` itself becomes a contextual function, just like
+[`to-absolute`]($length.to-absolute) is.
+
+```example
+#let foo() = 1em.to-absolute()
+#context {
+ foo() == text.size
+}
+```
+
+Context blocks can be nested. Contextual code will then always access the
+innermost context. The example below demonstrates this: The first `text.lang`
+will access the outer context block's styles and as such, it will **not**
+see the effect of `{set text(lang: "fr")}`. The nested context block around the
+second `text.lang`, however, starts after the set rule and will thus show
+its effect.
+
+```example
+#set text(lang: "de")
+#context [
+ #set text(lang: "fr")
+ #text.lang \
+ #context text.lang
+]
+```
+
+You might wonder why Typst ignores the French set rule when computing the first
+`text.lang` in the example above. The reason is that, in the general case, Typst
+cannot know all the styles that will apply as set rules can be applied to
+content after it has been constructed. Below, `text.lang` is already computed
+when the template function is applied. As such, it cannot possibly be aware of
+the language change to French in the template.
+
+```example
+#let template(body) = {
+ set text(lang: "fr")
+ upper(body)
+}
+
+#set text(lang: "de")
+#context [
+ #show: template
+ #text.lang \
+ #context text.lang
+]
+```
+
+The second `text.lang`, however, _does_ react to the language change because
+evaluation of its surrounding context block is deferred until the styles for it
+are known. This illustrates the importance of picking the right insertion point for a context to get access to precisely the right styles.
+
+The same also holds true for the location context. Below, the first
+`{c.display()}` call will access the outer context block and will thus not see
+the effect of `{c.update(2)}` while the second `{c.display()}` accesses the inner context and will thus see it.
+
+```example
+#let c = counter("mycounter")
+#c.update(1)
+#context [
+ #c.update(2)
+ #c.display() \
+ #context c.display()
+]
+```
+
+## Compiler iterations
+To resolve contextual interactions, the Typst compiler processes your document
+multiple times. For instance, to resolve a `locate` call, Typst first provides a
+placeholder position, layouts your document and then recompiles with the known
+position from the finished layout. The same approach is taken to resolve
+counters, states, and queries. In certain cases, Typst may even need more than
+two iterations to resolve everything. While that's sometimes a necessity, it may
+also be a sign of misuse of contextual functions (e.g. of
+[state]($state/#caution)). If Typst cannot resolve everything within five
+attempts, it will stop and output the warning "layout did not converge within 5
+attempts."
+
+A very careful reader might have noticed that not all of the functions presented
+above actually make use of the current location. While
+`{counter(heading).get()}` definitely depends on it,
+`{counter(heading).at(<intro>)}`, for instance, does not. However, it still
+requires context. While its value is always the same _within_ one compilation
+iteration, it may change over the course of multiple compiler iterations. If one
+could call it directly at the top level of a module, the whole module and its
+exports could change over the course of multiple compiler iterations, which
+would not be desirable.
+
+[^1]: Currently, all show rules provide styling context, but only show rules on
+ [locatable]($location/#locatable) elements provide a location context.
diff --git a/docs/reference/language/scripting.md b/docs/reference/language/scripting.md
new file mode 100644
index 00000000..5e0f1555
--- /dev/null
+++ b/docs/reference/language/scripting.md
@@ -0,0 +1,373 @@
+---
+description: Automate your document with Typst's scripting capabilities.
+---
+
+# Scripting
+Typst embeds a powerful scripting language. You can automate your documents and
+create more sophisticated styles with code. Below is an overview over the
+scripting concepts.
+
+## Expressions
+In Typst, markup and code are fused into one. All but the most common elements
+are created with _functions._ To make this as convenient as possible, Typst
+provides compact syntax to embed a code expression into markup: An expression is
+introduced with a hash (`#`) and normal markup parsing resumes after the
+expression is finished. If a character would continue the expression but should
+be interpreted as text, the expression can forcibly be ended with a semicolon
+(`;`).
+
+```example
+#emph[Hello] \
+#emoji.face \
+#"hello".len()
+```
+
+The example above shows a few of the available expressions, including
+[function calls]($function), [field accesses]($scripting/#fields), and
+[method calls]($scripting/#methods). More kinds of expressions are
+discussed in the remainder of this chapter. A few kinds of expressions are not
+compatible with the hash syntax (e.g. binary operator expressions). To embed
+these into markup, you can use parentheses, as in `[#(1 + 2)]`.
+
+## Blocks
+To structure your code and embed markup into it, Typst provides two kinds of
+_blocks:_
+
+- **Code block:** `{{ let x = 1; x + 2 }}` \
+ When writing code, you'll probably want to split up your computation into
+ multiple statements, create some intermediate variables and so on. Code blocks
+ let you write multiple expressions where one is expected. The individual
+ expressions in a code block should be separated by line breaks or semicolons.
+ The output values of the individual expressions in a code block are joined to
+ determine the block's value. Expressions without useful output, like `{let}`
+ bindings yield `{none}`, which can be joined with any value without effect.
+
+- **Content block:** `{[*Hey* there!]}` \
+ With content blocks, you can handle markup/content as a programmatic value,
+ store it in variables and pass it to [functions]($function). Content
+ blocks are delimited by square brackets and can contain arbitrary markup. A
+ content block results in a value of type [content]. An arbitrary number of
+ content blocks can be passed as trailing arguments to functions. That is,
+ `{list([A], [B])}` is equivalent to `{list[A][B]}`.
+
+Content and code blocks can be nested arbitrarily. In the example below,
+`{[hello ]}` is joined with the output of `{a + [ the ] + b}` yielding
+`{[hello from the *world*]}`.
+
+```example
+#{
+ let a = [from]
+ let b = [*world*]
+ [hello ]
+ a + [ the ] + b
+}
+```
+
+## Bindings and Destructuring { #bindings }
+As already demonstrated above, variables can be defined with `{let}` bindings.
+The variable is assigned the value of the expression that follows the `=` sign.
+The assignment of a value is optional, if no value is assigned, the variable
+will be initialized as `{none}`. The `{let}` keyword can also be used to create
+a [custom named function]($function/#defining-functions). Variables can be
+accessed for the rest of the containing block (or the rest of the file if there
+is no containing block).
+
+```example
+#let name = "Typst"
+This is #name's documentation.
+It explains #name.
+
+#let add(x, y) = x + y
+Sum is #add(2, 3).
+```
+
+Let bindings can also be used to destructure [arrays]($array) and
+[dictionaries]($dictionary). In this case, the left-hand side of the
+assignment should mirror an array or dictionary. The `..` operator can be used
+once in the pattern to collect the remainder of the array's or dictionary's
+items.
+
+```example
+#let (x, y) = (1, 2)
+The coordinates are #x, #y.
+
+#let (a, .., b) = (1, 2, 3, 4)
+The first element is #a.
+The last element is #b.
+
+#let books = (
+ Shakespeare: "Hamlet",
+ Homer: "The Odyssey",
+ Austen: "Persuasion",
+)
+
+#let (Austen,) = books
+Austen wrote #Austen.
+
+#let (Homer: h) = books
+Homer wrote #h.
+
+#let (Homer, ..other) = books
+#for (author, title) in other [
+ #author wrote #title.
+]
+```
+
+You can use the underscore to discard elements in a destructuring pattern:
+
+```example
+#let (_, y, _) = (1, 2, 3)
+The y coordinate is #y.
+```
+
+Destructuring also works in argument lists of functions ...
+
+```example
+#let left = (2, 4, 5)
+#let right = (3, 2, 6)
+#left.zip(right).map(
+ ((a,b)) => a + b
+)
+```
+
+... and on the left-hand side of normal assignments. This can be useful to
+swap variables among other things.
+
+```example
+#{
+ let a = 1
+ let b = 2
+ (a, b) = (b, a)
+ [a = #a, b = #b]
+}
+```
+
+## Conditionals
+With a conditional, you can display or compute different things depending on
+whether some condition is fulfilled. Typst supports `{if}`, `{else if}` and
+`{else}` expressions. When the condition evaluates to `{true}`, the conditional
+yields the value resulting from the if's body, otherwise yields the value
+resulting from the else's body.
+
+```example
+#if 1 < 2 [
+ This is shown
+] else [
+ This is not.
+]
+```
+
+Each branch can have a code or content block as its body.
+
+- `{if condition {..}}`
+- `{if condition [..]}`
+- `{if condition [..] else {..}}`
+- `{if condition [..] else if condition {..} else [..]}`
+
+## Loops
+With loops, you can repeat content or compute something iteratively. Typst
+supports two types of loops: `{for}` and `{while}` loops. The former iterate
+over a specified collection whereas the latter iterate as long as a condition
+stays fulfilled. Just like blocks, loops _join_ the results from each iteration
+into one value.
+
+In the example below, the three sentences created by the for loop join together
+into a single content value and the length-1 arrays in the while loop join
+together into one larger array.
+
+```example
+#for c in "ABC" [
+ #c is a letter.
+]
+
+#let n = 2
+#while n < 10 {
+ n = (n * 2) - 1
+ (n,)
+}
+```
+
+For loops can iterate over a variety of collections:
+
+- `{for value in array {..}}` \
+ Iterates over the items in the [array]. The destructuring syntax described in
+ [Let binding]($scripting/#bindings) can also be used here.
+
+- `{for pair in dict {..}}` \
+ Iterates over the key-value pairs of the [dictionary]. The pairs can also be
+ destructured by using `{for (key, value) in dict {..}}`. It is more efficient
+ than `{for pair in dict.pairs() {..}}` because it doesn't create a temporary
+ array of all key-value pairs.
+
+- `{for letter in "abc" {..}}` \
+ Iterates over the characters of the [string]($str). Technically, it iterates
+ over the grapheme clusters of the string. Most of the time, a grapheme cluster
+ is just a single codepoint. However, a grapheme cluster could contain multiple
+ codepoints, like a flag emoji.
+
+- `{for byte in bytes("😀") {..}}` \
+ Iterates over the [bytes], which can be converted from a [string]($str) or
+ [read] from a file without encoding. Each byte value is an [integer]($int)
+ between `{0}` and `{255}`.
+
+To control the execution of the loop, Typst provides the `{break}` and
+`{continue}` statements. The former performs an early exit from the loop while
+the latter skips ahead to the next iteration of the loop.
+
+```example
+#for letter in "abc nope" {
+ if letter == " " {
+ break
+ }
+
+ letter
+}
+```
+
+The body of a loop can be a code or content block:
+
+- `{for .. in collection {..}}`
+- `{for .. in collection [..]}`
+- `{while condition {..}}`
+- `{while condition [..]}`
+
+## Fields
+You can use _dot notation_ to access fields on a value. For values of type
+[`content`], you can also use the [`fields`]($content.fields) function to list
+the fields.
+
+The value in question can be either:
+- a [dictionary] that has the specified key,
+- a [symbol] that has the specified modifier,
+- a [module] containing the specified definition,
+- [content] consisting of an element that has the specified field. The
+ available fields match the arguments of the
+ [element function]($function/#element-functions) that were given when the
+ element was constructed.
+
+```example
+#let it = [= Heading]
+#it.body \
+#it.depth \
+#it.fields()
+
+#let dict = (greet: "Hello")
+#dict.greet \
+#emoji.face
+
+```
+
+## Methods
+A _method call_ is a convenient way to call a function that is scoped to a
+value's [type]. For example, we can call the [`str.len`]($str.len) function in
+the following two equivalent ways:
+
+```example
+#str.len("abc") is the same as
+#"abc".len()
+```
+
+The structure of a method call is `{value.method(..args)}` and its equivalent
+full function call is `{type(value).method(value, ..args)}`. The documentation
+of each type lists its scoped functions. You cannot currently define your own
+methods.
+
+```example
+#let values = (1, 2, 3, 4)
+#values.pop() \
+#values.len() \
+
+#("a, b, c"
+ .split(", ")
+ .join[ --- ])
+
+#"abc".len() is the same as
+#str.len("abc")
+```
+
+There are a few special functions that modify the value they are called on (e.g.
+[`array.push`]($array.push)). These functions _must_ be called in method form.
+In some cases, when the method is only called for its side effect, its return
+value should be ignored (and not participate in joining). The canonical way to
+discard a value is with a let binding: `{let _ = array.remove(1)}`.
+
+## Modules
+You can split up your Typst projects into multiple files called _modules._ A
+module can refer to the content and definitions of another module in multiple
+ways:
+
+- **Including:** `{include "bar.typ"}` \
+ Evaluates the file at the path `bar.typ` and returns the resulting [content].
+
+- **Import:** `{import "bar.typ"}` \
+ Evaluates the file at the path `bar.typ` and inserts the resulting [module]
+ into the current scope as `bar` (filename without extension). You can use the
+ `as` keyword to rename the imported module: `{import "bar.typ" as baz}`. You
+ can import nested items using dot notation: `{import "bar.typ": baz.a}`.
+
+- **Import items:** `{import "bar.typ": a, b}` \
+ Evaluates the file at the path `bar.typ`, extracts the values of the variables
+ `a` and `b` (that need to be defined in `bar.typ`, e.g. through `{let}`
+ bindings) and defines them in the current file. Replacing `a, b` with `*`
+ loads all variables defined in a module. You can use the `as` keyword to
+ rename the individual items: `{import "bar.typ": a as one, b as two}`
+
+Instead of a path, you can also use a [module value]($module), as shown in the
+following example:
+
+```example
+#import emoji: face
+#face.grin
+```
+
+## Packages
+To reuse building blocks across projects, you can also create and import Typst
+_packages._ A package import is specified as a triple of a namespace, a name,
+and a version.
+
+```example
+>>> #let add(x, y) = x + y
+<<< #import "@preview/example:0.1.0": add
+#add(2, 7)
+```
+
+The `preview` namespace contains packages shared by the community. You can find
+all available community packages on [Typst Universe]($universe).
+
+If you are using Typst locally, you can also create your own system-local
+packages. For more details on this, see the
+[package repository](https://github.com/typst/packages).
+
+## Operators
+The following table lists all available unary and binary operators with effect,
+arity (unary, binary) and precedence level (higher binds stronger). Some
+operations, such as [modulus]($calc.rem-euclid), do not have a special syntax
+and can be achieved using functions from the
+[`calc`]($category/foundations/calc) module.
+
+| Operator | Effect | Arity | Precedence |
+|:----------:|---------------------------------|:------:|:----------:|
+| `{-}` | Negation | Unary | 7 |
+| `{+}` | No effect (exists for symmetry) | Unary | 7 |
+| `{*}` | Multiplication | Binary | 6 |
+| `{/}` | Division | Binary | 6 |
+| `{+}` | Addition | Binary | 5 |
+| `{-}` | Subtraction | Binary | 5 |
+| `{==}` | Check equality | Binary | 4 |
+| `{!=}` | Check inequality | Binary | 4 |
+| `{<}` | Check less-than | Binary | 4 |
+| `{<=}` | Check less-than or equal | Binary | 4 |
+| `{>}` | Check greater-than | Binary | 4 |
+| `{>=}` | Check greater-than or equal | Binary | 4 |
+| `{in}` | Check if in collection | Binary | 4 |
+| `{not in}` | Check if not in collection | Binary | 4 |
+| `{not}` | Logical "not" | Unary | 3 |
+| `{and}` | Short-circuiting logical "and" | Binary | 3 |
+| `{or}` | Short-circuiting logical "or" | Binary | 2 |
+| `{=}` | Assignment | Binary | 1 |
+| `{+=}` | Add-Assignment | Binary | 1 |
+| `{-=}` | Subtraction-Assignment | Binary | 1 |
+| `{*=}` | Multiplication-Assignment | Binary | 1 |
+| `{/=}` | Division-Assignment | Binary | 1 |
+
+[semver]: https://semver.org/
diff --git a/docs/reference/language/styling.md b/docs/reference/language/styling.md
new file mode 100644
index 00000000..b0b7ab71
--- /dev/null
+++ b/docs/reference/language/styling.md
@@ -0,0 +1,145 @@
+---
+description: All concepts needed to style your document with Typst.
+---
+
+# Styling
+Typst includes a flexible styling system that automatically applies styling of
+your choice to your document. With _set rules,_ you can configure basic
+properties of elements. This way, you create most common styles. However, there
+might not be a built-in property for everything you wish to do. For this reason,
+Typst further supports _show rules_ that can completely redefine the appearance
+of elements.
+
+## Set rules
+With set rules, you can customize the appearance of elements. They are written
+as a [function call]($function) to an [element
+function]($function/#element-functions) preceded by the `{set}` keyword (or
+`[#set]` in markup). Only optional parameters of that function can be provided
+to the set rule. Refer to each function's documentation to see which parameters
+are optional. In the example below, we use two set rules to change the
+[font family]($text.font) and [heading numbering]($heading.numbering).
+
+```example
+#set heading(numbering: "I.")
+#set text(
+ font: "New Computer Modern"
+)
+
+= Introduction
+With set rules, you can style
+your document.
+```
+
+A top level set rule stays in effect until the end of the file. When nested
+inside of a block, it is only in effect until the end of that block. With a
+block, you can thus restrict the effect of a rule to a particular segment of
+your document. Below, we use a content block to scope the list styling to one
+particular list.
+
+```example
+This list is affected: #[
+ #set list(marker: [--])
+ - Dash
+]
+
+This one is not:
+- Bullet
+```
+
+Sometimes, you'll want to apply a set rule conditionally. For this, you can use
+a _set-if_ rule.
+
+```example
+#let task(body, critical: false) = {
+ set text(red) if critical
+ [- #body]
+}
+
+#task(critical: true)[Food today?]
+#task(critical: false)[Work deadline]
+```
+
+## Show rules
+With show rules, you can deeply customize the look of a type of element. The
+most basic form of show rule is a _show-set rule._ Such a rule is written as the
+`{show}` keyword followed by a [selector], a colon and then a set rule. The most
+basic form of selector is an [element function]($function/#element-functions).
+This lets the set rule only apply to the selected element. In the example below,
+headings become dark blue while all other text stays black.
+
+```example
+#show heading: set text(navy)
+
+= This is navy-blue
+But this stays black.
+```
+
+With show-set rules you can mix and match properties from different functions to
+achieve many different effects. But they still limit you to what is predefined
+in Typst. For maximum flexibility, you can instead write a show rule that
+defines how to format an element from scratch. To write such a show rule,
+replace the set rule after the colon with an arbitrary [function]. This function
+receives the element in question and can return arbitrary content. The available
+[fields]($scripting/#fields) on the element passed to the function again match
+the parameters of the respective element function. Below, we define a show rule
+that formats headings for a fantasy encyclopedia.
+
+```example
+#set heading(numbering: "(I)")
+#show heading: it => [
+ #set align(center)
+ #set text(font: "Inria Serif")
+ \~ #emph(it.body)
+ #counter(heading).display(
+ it.numbering
+ ) \~
+]
+
+= Dragon
+With a base health of 15, the
+dragon is the most powerful
+creature.
+
+= Manticore
+While less powerful than the
+dragon, the manticore gets
+extra style points.
+```
+
+Like set rules, show rules are in effect until the end of the current block or
+file.
+
+Instead of a function, the right-hand side of a show rule can also take a
+literal string or content block that should be directly substituted for the
+element. And apart from a function, the left-hand side of a show rule can also
+take a number of other _selectors_ that define what to apply the transformation
+to:
+
+- **Everything:** `{show: rest => ..}` \
+ Transform everything after the show rule. This is useful to apply a more
+ complex layout to your whole document without wrapping everything in a giant
+ function call.
+
+- **Text:** `{show "Text": ..}` \
+ Style, transform or replace text.
+
+- **Regex:** `{show regex("\w+"): ..}` \
+ Select and transform text with a regular expression for even more flexibility.
+ See the documentation of the [`regex` type]($regex) for details.
+
+- **Function with fields:** `{show heading.where(level: 1): ..}` \
+ Transform only elements that have the specified fields. For example, you might
+ want to only change the style of level-1 headings.
+
+- **Label:** `{show <intro>: ..}` \
+ Select and transform elements that have the specified label. See the
+ documentation of the [`label` type]($label) for more details.
+
+```example
+#show "Project": smallcaps
+#show "badly": "great"
+
+We started Project in 2019
+and are still working on it.
+Project is progressing badly.
+```
diff --git a/docs/reference/language/syntax.md b/docs/reference/language/syntax.md
new file mode 100644
index 00000000..fdc4d154
--- /dev/null
+++ b/docs/reference/language/syntax.md
@@ -0,0 +1,215 @@
+---
+description: |
+ A compact reference for Typst's syntax. Learn more about the language within
+ markup, math, and code mode.
+---
+
+# Syntax
+Typst is a markup language. This means that you can use simple syntax to
+accomplish common layout tasks. The lightweight markup syntax is complemented by
+set and show rules, which let you style your document easily and automatically.
+All this is backed by a tightly integrated scripting language with built-in and
+user-defined functions.
+
+## Modes
+Typst has three syntactical modes: Markup, math, and code. Markup mode is the
+default in a Typst document, math mode lets you write mathematical formulas, and
+code mode lets you use Typst's scripting features.
+
+You can switch to a specific mode at any point by referring to the following
+table:
+
+| New mode | Syntax | Example |
+|----------|---------------------------------|---------------------------------|
+| Code | Prefix the code with `#` | `[Number: #(1 + 2)]` |
+| Math | Surround equation with `[$..$]` | `[$-x$ is the opposite of $x$]` |
+| Markup | Surround markup with `[[..]]` | `{let name = [*Typst!*]}` |
+
+Once you have entered code mode with `#`, you don't need to use further hashes
+unless you switched back to markup or math mode in between.
+
+## Markup
+Typst provides built-in markup for the most common document elements. Most of
+the syntax elements are just shortcuts for a corresponding function. The table
+below lists all markup that is available and links to the best place to learn
+more about their syntax and usage.
+
+| Name | Example | See |
+| ------------------ | ---------------------------- | ------------------------ |
+| Paragraph break | Blank line | [`parbreak`] |
+| Strong emphasis | `[*strong*]` | [`strong`] |
+| Emphasis | `[_emphasis_]` | [`emph`] |
+| Raw text | ``[`print(1)`]`` | [`raw`] |
+| Link | `[https://typst.app/]` | [`link`] |
+| Label | `[<intro>]` | [`label`] |
+| Reference | `[@intro]` | [`ref`] |
+| Heading | `[= Heading]` | [`heading`] |
+| Bullet list | `[- item]` | [`list`] |
+| Numbered list | `[+ item]` | [`enum`] |
+| Term list | `[/ Term: description]` | [`terms`] |
+| Math | `[$x^2$]` | [Math]($category/math) |
+| Line break | `[\]` | [`linebreak`] |
+| Smart quote | `['single' or "double"]` | [`smartquote`] |
+| Symbol shorthand | `[~]`, `[---]` | [Symbols]($category/symbols/sym) |
+| Code expression | `[#rect(width: 1cm)]` | [Scripting]($scripting/#expressions) |
+| Character escape | `[Tweet at us \#ad]` | [Below](#escapes) |
+| Comment | `[/* block */]`, `[// line]` | [Below](#comments) |
+
+## Math mode { #math }
+Math mode is a special markup mode that is used to typeset mathematical
+formulas. It is entered by wrapping an equation in `[$]` characters. This works
+both in markup and code. The equation will be typeset into its own block if it
+starts and ends with at least one space (e.g. `[$ x^2 $]`). Inline math can be
+produced by omitting the whitespace (e.g. `[$x^2$]`). An overview over the
+syntax specific to math mode follows:
+
+| Name | Example | See |
+| ---------------------- | ------------------------ | ------------------------ |
+| Inline math | `[$x^2$]` | [Math]($category/math) |
+| Block-level math | `[$ x^2 $]` | [Math]($category/math) |
+| Bottom attachment | `[$x_1$]` | [`attach`]($category/math/attach) |
+| Top attachment | `[$x^2$]` | [`attach`]($category/math/attach) |
+| Fraction | `[$1 + (a+b)/5$]` | [`frac`]($math.frac) |
+| Line break | `[$x \ y$]` | [`linebreak`] |
+| Alignment point | `[$x &= 2 \ &= 3$]` | [Math]($category/math) |
+| Variable access | `[$#x$, $pi$]` | [Math]($category/math) |
+| Field access | `[$arrow.r.long$]` | [Scripting]($scripting/#fields) |
+| Implied multiplication | `[$x y$]` | [Math]($category/math) |
+| Symbol shorthand | `[$->$]`, `[$!=$]` | [Symbols]($category/symbols/sym) |
+| Text/string in math | `[$a "is natural"$]` | [Math]($category/math) |
+| Math function call | `[$floor(x)$]` | [Math]($category/math) |
+| Code expression | `[$#rect(width: 1cm)$]` | [Scripting]($scripting/#expressions) |
+| Character escape | `[$x\^2$]` | [Below](#escapes) |
+| Comment | `[$/* comment */$]` | [Below](#comments) |
+
+## Code mode { #code }
+Within code blocks and expressions, new expressions can start without a leading
+`#` character. Many syntactic elements are specific to expressions. Below is
+a table listing all syntax that is available in code mode:
+
+| Name | Example | See |
+| ------------------------ | ----------------------------- | ---------------------------------- |
+| None | `{none}` | [`none`] |
+| Auto | `{auto}` | [`auto`] |
+| Boolean | `{false}`, `{true}` | [`bool`] |
+| Integer | `{10}`, `{0xff}` | [`int`] |
+| Floating-point number | `{3.14}`, `{1e5}` | [`float`] |
+| Length | `{2pt}`, `{3mm}`, `{1em}`, .. | [`length`] |
+| Angle | `{90deg}`, `{1rad}` | [`angle`] |
+| Fraction | `{2fr}` | [`fraction`] |
+| Ratio | `{50%}` | [`ratio`] |
+| String | `{"hello"}` | [`str`] |
+| Label | `{<intro>}` | [`label`] |
+| Math | `[$x^2$]` | [Math]($category/math) |
+| Raw text | ``[`print(1)`]`` | [`raw`] |
+| Variable access | `{x}` | [Scripting]($scripting/#blocks) |
+| Code block | `{{ let x = 1; x + 2 }}` | [Scripting]($scripting/#blocks) |
+| Content block | `{[*Hello*]}` | [Scripting]($scripting/#blocks) |
+| Parenthesized expression | `{(1 + 2)}` | [Scripting]($scripting/#blocks) |
+| Array | `{(1, 2, 3)}` | [Array]($array) |
+| Dictionary | `{(a: "hi", b: 2)}` | [Dictionary]($dictionary) |
+| Unary operator | `{-x}` | [Scripting]($scripting/#operators) |
+| Binary operator | `{x + y}` | [Scripting]($scripting/#operators) |
+| Assignment | `{x = 1}` | [Scripting]($scripting/#operators) |
+| Field access | `{x.y}` | [Scripting]($scripting/#fields) |
+| Method call | `{x.flatten()}` | [Scripting]($scripting/#methods) |
+| Function call | `{min(x, y)}` | [Function]($function) |
+| Argument spreading | `{min(..nums)}` | [Arguments]($arguments) |
+| Unnamed function | `{(x, y) => x + y}` | [Function]($function) |
+| Let binding | `{let x = 1}` | [Scripting]($scripting/#bindings) |
+| Named function | `{let f(x) = 2 * x}` | [Function]($function) |
+| Set rule | `{set text(14pt)}` | [Styling]($styling/#set-rules) |
+| Set-if rule | `{set text(..) if .. }` | [Styling]($styling/#set-rules) |
+| Show-set rule | `{show heading: set block(..)}` | [Styling]($styling/#show-rules) |
+| Show rule with function | `{show raw: it => {..}}` | [Styling]($styling/#show-rules) |
+| Show-everything rule | `{show: template}` | [Styling]($styling/#show-rules) |
+| Context expression | `{context text.lang}` | [Context]($context) |
+| Conditional | `{if x == 1 {..} else {..}}` | [Scripting]($scripting/#conditionals) |
+| For loop | `{for x in (1, 2, 3) {..}}` | [Scripting]($scripting/#loops) |
+| While loop | `{while x < 10 {..}}` | [Scripting]($scripting/#loops) |
+| Loop control flow | `{break, continue}` | [Scripting]($scripting/#loops) |
+| Return from function | `{return x}` | [Function]($function) |
+| Include module | `{include "bar.typ"}` | [Scripting]($scripting/#modules) |
+| Import module | `{import "bar.typ"}` | [Scripting]($scripting/#modules) |
+| Import items from module | `{import "bar.typ": a, b, c}` | [Scripting]($scripting/#modules) |
+| Comment | `{/* block */}`, `{// line}` | [Below](#comments) |
+
+## Comments
+Comments are ignored by Typst and will not be included in the output. This is
+useful to exclude old versions or to add annotations. To comment out a single
+line, start it with `//`:
+```example
+// our data barely supports
+// this claim
+
+We show with $p < 0.05$
+that the difference is
+significant.
+```
+
+Comments can also be wrapped between `/*` and `*/`. In this case, the comment
+can span over multiple lines:
+```example
+Our study design is as follows:
+/* Somebody write this up:
+ - 1000 participants.
+ - 2x2 data design. */
+```
+
+## Escape sequences { #escapes }
+Escape sequences are used to insert special characters that are hard to type or
+otherwise have special meaning in Typst. To escape a character, precede it with
+a backslash. To insert any Unicode codepoint, you can write a hexadecimal escape
+sequence: `[\u{1f600}]`. The same kind of escape sequences also work in
+[strings]($str).
+
+```example
+I got an ice cream for
+\$1.50! \u{1f600}
+```
+
+## Paths
+Typst has various features that require a file path to reference external
+resources such as images, Typst files, or data files. Paths are represented as
+[strings]($str). There are two kinds of paths: Relative and absolute.
+
+- A **relative path** searches from the location of the Typst file where the
+ feature is invoked. It is the default:
+ ```typ
+ #image("images/logo.png")
+ ```
+
+- An **absolute path** searches from the _root_ of the project. It starts with a
+ leading `/`:
+ ```typ
+ #image("/assets/logo.png")
+ ```
+
+### Project root
+By default, the project root is the parent directory of the main Typst file.
+For security reasons, you cannot read any files outside of the root directory.
+
+If you want to set a specific folder as the root of your project, you can use
+the CLI's `--root` flag. Make sure that the main file is contained in the
+folder's subtree!
+```bash
+typst compile --root .. file.typ
+```
+
+In the web app, the project itself is the root directory. You can always read
+all files within it, no matter which one is previewed (via the eye toggle next
+to each Typst file in the file panel).
+
+### Paths and packages
+A package can only load files from its own directory. Within it, absolute paths
+point to the package root, rather than the project root. For this reason, it
+cannot directly load files from the project directory. If a package needs
+resources from the project (such as a logo image), you must pass the already
+loaded image, e.g. as a named parameter `{logo: image("mylogo.svg")}`. Note that
+you can then still customize the image's appearance with a set rule within the
+package.
+
+In the future, paths might become a
+[distinct type from strings](https://github.com/typst/typst/issues/971), so that
+they can retain knowledge of where they were constructed. This way, resources
+could be loaded from a different root.