Virtual Package Examples
Tola injects sys.inputs and exposes them through Typst’s package mechanism. You use them with standard #import syntax, so site metadata, page indexes, and current-page context are available as regular Typst modules.
This design is extensible and customizable:
- Add arbitrary site-level fields in
tola.toml(info.extra) and read them from@tola/site. - Add page-level metadata in each post
argsand query them through@tola/pages. - Compose your own filters, rankings, and navigation logic directly in Typst code.
- …More
#import "@tola/site:0.0.0": info
#import "@tola/pages:0.0.0": pages
#let featured = pages().filter(p => p.at("featured", default: false))
Brand color: #info.extra.at("brand-color", default: "sky")
Featured count: #featured.len()API Overview
| Package | Exports |
@tola/site:0.0.0 | info |
@tola/pages:0.0.0 | pages(), by-tag(tag), by-tags(..tags), all-tags() |
@tola/current:0.0.0 | permalink, parent-permalink, path, filename, headings, links-to, linked-by, breadcrumbs, children, siblings, at-offset, prev, next, take-prev, take-next |
Site Metadata API (@tola/site)
Read site metadata from `tola.toml`
Same pattern as README: import `info` and read `info.title`, `info.author`, and `info.extra`.
#import "@tola/site:0.0.0": info
Site: #info.title
Author: #info.author
Language: #info.language
Custom0: #info.extra.at("custom0", default: "<none>")
Custom1: #info.extra.at("custom1", default: "<none>")- Site: Starter
- Author: Tola
- Language: en
- Custom0: ABC
- Custom1: XYZ
Common Info Fields
info.title/info.author/info.emailinfo.description/info.url/info.languageinfo.copyrightinfo.extrafor custom structured fields
Page Query API (@tola/pages)
Recent Posts Query
Recent 5 posts
README example converted to a reusable list block.
#import "@tola/pages:0.0.0": pages
#let posts = (pages()
.filter(p => "/posts/" in p.permalink)
.filter(p => p.at("date", default: none) != none)
.sorted(key: p => p.date)
.rev())
#let recent = posts.slice(0, calc.min(5, posts.len()))
#for post in recent {
[- #link(post.permalink)[#post.title]]
}- Placeholder Post 05 (2026-03-03)
- Placeholder Post 04 (2026-03-01)
- Virtual Package Examples (2026-02-28)
- Placeholder Post 03 (2026-02-27)
- Typst Basic Syntax (2026-02-25)
Pinned Ordering Strategy
Pinned posts + stable date grouping
Avoid mixed-type sort keys by splitting pinned pages into date/non-date groups.
#import "@tola/pages:0.0.0": pages
#let pinned = pages().filter(p => p.at("pinned", default: false))
#let with-date = (pinned
.filter(p => p.at("date", default: none) != none)
.sorted(key: p => p.date)
.rev())
#let without-date = pinned.filter(p => p.at("date", default: none) == none)
#let ordered = with-date + without-date- Virtual Package Examples (2026-02-28)
- Typst Basic Syntax (2026-02-25)
- Showcase
Tag Query Helpers
Tag query helpers: `by-tag`, `by-tags`, `all-tags`
Covers the helper APIs exported by `@tola/pages:0.0.0`.
#import "@tola/pages:0.0.0": by-tag, by-tags, all-tags
#let tutorial = by-tag("tutorial")
#let virtual-and-tutorial = by-tags("virtual-packages", "tutorial")
#let tags = all-tags()- tutorial: 3 posts
- virtual-packages + tutorial: 1 post
- tags: baseline, demo-data, feature, head, html, math, placeholder, seo, showcase, tutorial, typst, virtual-packages
Current Page API (@tola/current)
Context Fields
Current page context (`permalink`, `parent-permalink`, `path`, `filename`, `headings`)
Use permalink-based context values in templates for breadcrumbs, TOC, and layout decisions.
#import "@tola/current:0.0.0": current-permalink, parent-permalink, path, filename, headings
Current permalink: #current-permalink
Parent permalink: #parent-permalink
Source path: #path
Filename: #filename
Heading count: #headings.len()permalink: /posts/virtual-packages/parent-permalink: /posts/path: posts/virtual-packages.typfilename: virtual-packages.typheadings: 15
Filename-Derived Metadata
Extract date-like prefix from `filename`
Pattern from README: works for filenames like `2025_02_25_my-post.typ`.
#import "@tola/current:0.0.0": path, filename
#let file = filename.replace(".typ", "")
#let parts = file.split("_")
#let auto-date = if parts.len() >= 4 {
parts.slice(0, 3).join("-")
} else {
none
}- source path: posts/virtual-packages.typ
- filename: virtual-packages.typ
- derived date: <none>
- derived slug: virtual-packages
Hierarchy Navigation
Prev/next + breadcrumbs + hierarchy helpers
Navigation helpers from `@tola/current` for section pages and article pages.
#import "@tola/pages:0.0.0": pages
#import "@tola/current:0.0.0": prev, next, breadcrumbs, children, siblings
#let all = pages()
#let dated-posts = all
.filter(p => "/posts/" in p.permalink and p.date != none)
.sorted(key: p => p.date)
#let prev-page = prev(dated-posts)
#let next-page = next(dated-posts)
#let crumbs = breadcrumbs(all, include-root: true)
#let direct-children = children(all)
#let same-level = siblings(all)- prev: Placeholder Post 03
- next: Placeholder Post 04
- breadcrumbs:
Home / posts / Virtual Package Examples
- stats: children = 0, siblings = 6, links-to = 0, linked-by = 0
Offset Navigation Window
Offset helpers: `at-offset`, `take-prev`, `take-next`
Build navigation windows around the current page without manual index math.
#import "@tola/pages:0.0.0": pages
#import "@tola/current:0.0.0": at-offset, take-prev, take-next
#let dated = (pages()
.filter(p => "/posts/" in p.permalink and p.date != none)
.sorted(key: p => p.date))
#let two-back = at-offset(dated, -2)
#let two-forward = at-offset(dated, 2)
#let previous = take-prev(dated, n: 2)
#let next = take-next(dated, n: 2)- at-offset(-2): Typst Basic Syntax
- at-offset(+2): Placeholder Post 05
- take-prev(2) = 2, take-next(2) = 2
- Typst Basic Syntax (2026-02-25)
- Placeholder Post 03 (2026-02-27)
- Current page: Virtual Package Examples (2026-02-28)
- Placeholder Post 04 (2026-03-01)
- Placeholder Post 05 (2026-03-03)
Page Metadata Contract
Standard Fields
Each entry from pages() is page metadata and includes common keys like:
permalink,title,date,author,summarytags,draft- …
Custom Fields
Any custom metadata fields you provide:
pinned,order- …