Website Documentation
This page explains how this website works, how to edit it, and how to publish a copy. The site is unusual because Typst is both its document language and its static-site generator: the same source can become a web page, a conventional PDF, a research paper, a book, or a presentation.
Download the reusable website template or continue below for the complete guide. The archive replaces personal material with examples, includes its own exporter, and can generate another sanitized copy of itself.
For general Typst reference, use the official Typst documentation. The Typst Examples Book is an especially useful example-driven companion. This page summarizes the part needed to maintain this site; it does not attempt to replace either reference.
What the site is
The repository contains source and generated output together. Typst files describe the content. The shared template turns them into HTML or PDF. GNU Make discovers pages and selects the required outputs. GitHub Actions runs the build and commits the generated files. GitHub Pages serves those static files without a database or application server.
The central file is cv.typ. It serves three roles:
- It is the source of the printable CV at
cv.pdf. - It becomes the homepage at
index.html. - It defines the configured
templateimported by every website page.
This arrangement prevents the homepage and CV from becoming separate, inconsistent copies. It also gives nested pages the same navigation, typography, helper functions, and target-dependent behavior.
Typst in fifteen minutes
Typst has three closely connected modes: markup for ordinary writing, code for computation, and math for equations. Most documents move between them rather than choosing only one.
Markup
Ordinary text is markup. Blank lines start paragraphs. Heading depth is the number of equals signs, - creates a bullet, + creates a numbered item, underscores emphasize text, and asterisks make it strong.
= A top-level heading
== A section
This is _emphasized_ and this is *strong*.
- First point
- Second point
- Nested point
+ First numbered point
+ Second numbered pointUse a backslash for an explicit line break or to escape markup punctuation. Line comments begin with //; block comments use /* ... */. Raw text uses backticks, while fenced raw blocks are appropriate for code.
Links, labels, references, and footnotes are content-producing functions:
#link("https://typst.app")[Typst]
= A result <result>
See @result for the result.
This claim has a footnote.#footnote[The supporting note.]On this site, HTML footnotes become numbered margin notes with backlinks. In PDFs they retain Typst’s normal footnote behavior.
Code and content
# leaves markup and evaluates one code expression. Square brackets create a content block, braces create a code block, and parentheses hold arguments or values. A content block is still structured Typst content—not merely a string.
#let greeting(name) = [Hello, #strong(name)!]
#greeting("Reader")
#let topics = ("HTML", "PDF", "RSS")
#for topic in topics [
- #topic
]
#if topics.len() > 2 [Several outputs are enabled.]Functions can take positional arguments, named arguments, defaults, and a trailing content argument. Arrays use parentheses; dictionaries use named entries. none means no value, auto delegates a choice, and context delays an expression until layout or document state is available.
Imports load definitions from another file or a package:
#import "../cv.typ": *Local paths are resolved relative to the file containing the path. Each page imports * from the nearest ancestor index.typ, so pages inherit the definitions exposed by their section. A section index imports ../cv.typ; a nested page imports its nearest parent index, such as ../index.typ. If no ancestor index exists, the page imports the appropriate relative path to cv.typ.
Styling and templates
A set rule changes properties of later elements. A show rule transforms matching content. Templates are normally functions that accept configuration and a final content argument, then install set/show rules around that content.
#set text(size: 11pt, lang: "en")
#show heading.where(level: 2): it => emph(it.body)
#let simple(title: "Untitled", content) = {
set document(title: title)
heading(title)
content
}
#show: simple.with(title: "Example")The distinction matters here: #show: template.with(...) sends the remainder of the page through the shared template. A missing show rule may leave a file compilable as a standalone PDF while preventing it from becoming a consistent website page.
Math, references, and bibliography
Inline math is enclosed by single dollar signs. Add spaces immediately inside the delimiters to produce a displayed equation.
The identity $sum_(i=1)^n i = n(n+1)/2$ is useful.
$
integral_0^1 x^2 dif x = 1/3
$ <integral>
Equation @integral is displayed above.This template numbers equations and exports HTML equations as embedded SVG/MathML-compatible frames. It also corrects equation references and renders heading references by title. The shared quick-math rules provide +-, |-, and <= shorthands used by existing notes.
Typst reads BibTeX files directly:
This is cited here @sample2026.
#bibliography("references.bib")The CV’s paper(...) helper has separate HTML and PDF behavior. HTML preserves title capitalization, links a title through its URL or DOI, bolds Iniyan Joseph’s name, and displays selected distinctions. PDF delegates to Typst’s bibliography engine.
Local editing
The web application at typst.app is the easiest place to learn Typst. For this repository, local development is more capable because Make, Git, Python, and browser assets participate in the build. Typst’s CLI compiles once with typst compile and watches a file with typst watch. An editor using Tinymist adds completion, diagnostics, navigation, formatting, and live preview.
The site currently expects Typst 0.15 or newer, GNU Make, Bash, GNU grep/realpath, Python 3, and ordinary Unix utilities. Run commands from the repository root.
Repository map
cv.typ: homepage, printable CV, global navigation, identity, and configured template.src/tufted.typ: HTML/PDF dispatcher and the main template API.src/assets/: Tufte-derived base adjustments and site-specific responsive CSS.src/fields.typ: dates, education, employment, awards, activities, performances, festivals, and publications.src/notes.typ: HTML margin footnotes and backlinks.src/layout.typ: margin-note and full-width helpers.src/figures.typ: HTML figures whose captions live in the margin.src/math.typ: numbered equations and HTML math frames.src/refs.typ: equation and heading-reference behavior.src/theorems.typ: theorem, lemma, proposition, and fact environments.src/rss.typ: dated posts, future-publication filtering, metadata, and RSS construction.src/presentation-viewer.htmland.js: full-viewport, PDF-backed slide viewer.blog/,school/,papers/, andprojects/: site content organized by route.book/: public-domain book finder, cleaner, previewer, Typst converter, and in-browser PDF renderer.noise/: local Web Audio brown-noise generator.digest/: deterministic weekly aggregation, email ingestion, Typst page, email output, and RSS output.Makefile: page discovery, dependency tracking, compilation, feeds, cleanup, and template export..github/workflows/: normal builds, the weekly digest, link checking, and manual template export..nojekyll: tells GitHub Pages to serve the generated tree without Jekyll processing.CNAME: optional custom-domain declaration.
HTML and PDF files beside their sources are generated artifacts. Edit the .typ, .js, .css, Python, BibTeX, or workflow source rather than editing generated HTML/PDF by hand.
Creating a page
Create a directory whose path should become the URL, then place index.typ inside it. A page at projects/example/index.typ becomes /projects/example/.
#import "../index.typ": *
#show: template.with(title: "Example Page")
== First section
Page content goes here.The Makefile begins with cv.typ and follows relative imports in reverse. Any Typst file that directly or indirectly imports cv.typ is treated as a website page. This means a new page normally needs no Makefile entry. Shared Typst changes are conservative dependencies of every output, so editing the template rebuilds dependent pages.
To add the route to the header, edit the nav tuple in cv.typ:
nav: (
("", "Home"),
("blog", "Blog"),
("documentation", "Documentation"),
("cv.pdf", "CV"),
),Routes omitted from navigation are unlisted, not private. Anyone who knows or discovers the URL can open them. Never publish secrets or genuinely private information in a GitHub Pages tree.
Template configuration
The shared tufted-web function accepts these inputs:
title: document title, browser title, page heading, and PDF metadata.author: author metadata and optionally a visible byline.lang: document language for HTML and text shaping.nav:(path, label)pairs used by the shared header.email: contact metadata shown in applicable PDF title blocks.institution: presentation and research-paper affiliation.abstract,keywords, anddate: research-paper metadata.website: explicit canonical personal-site address for PDF headers. The configuredcv.typtemplate supplies it even for manual PDF builds; the Makefile’ssite-urlinput overrides the site address.book: enables justified, indented book typography in paged output.research: uses Arkheion’s research-paper layout.presentation: uses Touying’s Metropolis presentation theme and the browser slide viewer.article: marks ordinary long-form writing without adding an automatic byline.show-author: opt-in HTML byline (true). The default omits the name beneath page titles. Ordinary PDFs show the website link instead; books and research papers retain their author information.references: a prepared bibliography value appended as a final section or slide.
Only one specialized paged mode should normally be enabled at a time.
Ordinary pages and articles
Ordinary pages use the responsive Tufte-inspired HTML layout and a compact paged format. Use article: true for authored essays:
#show: template.with(
title: "An Essay",
article: true,
)The HTML template supplies a semantic page header, navigation, one page-level h1, author metadata, mobile viewport metadata, stylesheets, and analytics. Shared controls have consistent focus, hover, disabled, and reduced-motion behavior. Images, video, SVG, canvas, headings, book controls, and the noise page adapt to narrow screens.
Research papers
Research mode uses Arkheion for title, author, affiliation, abstract, keywords, date, typography, and paper layout. Shared math, references, and theorem helpers remain available.
#show: template.with(
title: "A Research Result",
research: true,
abstract: [A short abstract.],
keywords: ("algorithms", "economics"),
references: bibliography("refs.bib", title: none),
)The Makefile automatically produces a PDF for files that explicitly enable research: true.
Presentations
Presentation mode produces a 16:9 Touying/Metropolis PDF with title metadata, author, institution, contact information, date, footer, citations, and an optional references slide.
#show: template.with(
title: "A Presentation",
presentation: true,
references: bibliography("refs.bib", title: none),
)
== First slide
Slide content.Presentations are not exported through Typst’s experimental HTML layout. Instead, the Makefile builds the PDF and generates a small viewer shell. The viewer renders one page at a time with PDF.js, fits it to the viewport, updates on resize, and falls back to a direct PDF link if loading fails. Arrow keys, Page Up/Down, Space, Home, End, taps, clicks, and horizontal swipes navigate. F toggles fullscreen.
Books
book: true changes paged paragraphs to justified text with first-line indentation, book-like leading, page numbers, and a title block linking back to /book/. The browser Book utility searches Standard Ebooks first and Project Gutenberg second, cleans a readable edition, identifies chapters and headings, previews the result, converts the cleaned DOM to Typst, downloads the generated source, and compiles a PDF in the browser using typst.ts. It uses Gutendex, Standard Ebooks, and a read-only text proxy as needed; availability therefore depends on those services and browser cross-origin policy.
Theorems, figures, notes, and layout
theorem, lemma, proposition, and fact produce numbered Frame-It boxes in ordinary documents. Research/presentation contexts use simpler blocks to remain compatible with their layout engines.
margin-note[...] places supplemental HTML in the wide right margin. On small screens, Tufte CSS’s responsive behavior brings it into the reading flow. Figure captions and footnotes use the same margin system. full-width[...] creates an HTML region that can extend beyond the normal text column.
The linked-image and linked-video helpers in cv.typ let a page choose target-appropriate media behavior. The custom stylesheet keeps all media within the viewport and gives inline and displayed equations separate HTML treatment.
Dated posts and RSS
Import post-section, post-link, and feed from cv.typ or src/rss.typ.
#post-section("Release Notes", datetime(year: 2026, month: 9, day: 12))[
The body of the post.
]
#feed(title: "Project updates", description: "New project notes")A dated post always appears in paged output, including future-dated material. HTML and RSS include it only when its date has arrived. post-section derives a stable heading slug; post-link points to a separate page. Both attach metadata used by feed.
The Makefile detects pages that call feed and generates a sibling feed.xml by evaluating the metadata value. Feed items are sorted newest first and use canonical URLs constructed from SITE_URL and the page’s output path. Feed pages also receive PDFs automatically; ordinary pages do not, unless they are research papers or presentations.
CV and structured fields
Use dt(year, month) for month-level dates. Structured helpers keep the HTML and PDF CV aligned:
#edu(
start: dt(2026, 8),
end: "Present",
degree: "Ph.D. in Computer Science",
university: "Example University",
advisor: "Example Advisor",
)
#job(
start: dt(2025, 8),
end: "Present",
role: "Research Assistant",
location: "Example University",
)award delegates to the general other entry format. performance and festival support the site’s music records. PDF entries use a two-column date/content table; HTML uses a compact linear representation. paper("papers/publications.bib") sorts work by descending year and uses each entry’s URL or DOI when possible.
Brown-noise utility
/noise/ is intentionally minimal: one Start/Stop toggle and no settings. A native audio buffer loops 30 seconds of brown noise with a continuous filter state across the seam. A leaky integrator keeps the lowest frequencies bounded, and a low-pass filter softens frequencies above 500 Hz. The signal retains headroom and uses gradual start/stop fades. Playback requires no JavaScript audio callback. Processing is entirely local; no audio is recorded or uploaded.
Weekly digest
The Sunday workflow turns several changing sources into one deterministic digest:
- ordinary RSS and arXiv feeds;
- selected Reddit communities and YouTube channels;
- local weather;
- every message received during the previous seven days by a dedicated Gmail newsletter inbox;
- campus, city, and theory-seminar events;
- research-conference deadlines;
- state and county election information;
- a maintenance report containing link-check status and source failures.
Email is read over IMAP and sent over SMTP. Multipart messages are decoded and cleaned. Scripts, forms, tracking pixels, hidden preheaders, advertising/promotion sections, social blocks, preference/unsubscribe footers, fragments, and common analytics parameters are removed. Attachments are ignored. The same cleaned material becomes an HTML email, a Typst page, and a flat RSS feed.
The email and website group newsletters into alphabetically ordered sender sections, with messages in chronological order beneath each sender. Sender grouping ignores differences in capitalization and whitespace.
For reproducibility, the run chooses one date/time, sorts messages and source results stably, normalizes URLs, and accepts DIGEST_DATE=YYYY-MM-DD for a repeatable historical run. Network sources may still change their responses; determinism means identical fetched input and run date produce identical ordering and rendering.
Customize digest/config.yml, then add these GitHub repository secrets under Settings → Secrets and variables → Actions:
SMTP_USER: the dedicated Gmail address;SMTP_PASS: a Gmail app password;RECIPIENT_EMAIL: the address that receives the digest.
GitHub supplies GITHUB_TOKEN. Do not commit passwords to config.yml, Typst files, workflows, or generated pages.
The Makefile
The principal targets are:
make # same as make all
make all # site, selected PDFs/feeds, and reusable zip
make publish # cv.pdf, all HTML, selected PDFs, and feeds
make html # all discovered HTML/viewer pages
make pdf # feed, research, and presentation PDFs
make personalwebsite # sanitized reusable archive
make clean # remove generated site outputsSITE_URL is the canonical deployment setting:
make SITE_URL=https://example.orgThe output path determines both its public route and how many ../ segments are needed to reach shared assets. Those values enter Typst through sys.inputs, so pages do not need to hard-code their route. Presentation viewer shells are always refreshed; other outputs use Make dependencies. make -B publish forces date-sensitive pages and feeds to be reevaluated.
make personalwebsite copies the reusable implementation, creates neutral sample content, replaces personal domains, names, analytics identifiers, and local event names, checks for leaked site-specific material, smoke-tests HTML and PDF generation, and creates projects/personalwebsite.zip. The copied Makefile and exporter make the template partially self-replicating: a customized copy can produce another clean starter archive.
Publishing with GitHub Pages
1. Download and unpack
Download the template, extract it, and enter the resulting directory. Replace sample content before publishing.
2. Create the repository
Create a GitHub repository named YOUR-USERNAME.github.io for a user site. Commit the extracted files at the repository root. A project site with another repository name can work, but root-relative assumptions in browser utilities may require adjustment.
git init
git add .
git commit -m "Create Typst website"
git branch -M main
git remote add origin git@github.com:YOUR-USERNAME/YOUR-USERNAME.github.io.git
git push -u origin main3. Personalize the source
At minimum:
- Set
SITE_URLinMakefile. - Replace the sample identity, email, navigation, CV records, and biography in
cv.typ. - Replace
photo.svgandpapers/publications.bib. - Replace or remove the Google Analytics identifier in
src/tufted.typandsrc/presentation-viewer.html. - Edit
digest/config.yml, or disable the weekly workflow if you do not want a digest. - Review external URLs in the event/election scrapers; their HTML structure is site-specific.
- Remove sample routes you do not want and run
make clean && make.
4. Build and push
Install Typst and Make locally, then run make. Commit both source and generated static output. The included build workflow also installs the current Typst CLI on GitHub’s Linux runner, builds the site, forces date-sensitive output, and commits changed artifacts.
5. Enable Pages
In the repository, open Settings → Pages. Under Build and deployment, select “Deploy from a branch,” choose main, choose /(root), and save. The root contains index.html, which GitHub Pages requires. .nojekyll prevents Jekyll from interpreting the generated tree.
GitHub Pages is public even when some plans allow its repository to be private. Unlinked pages are therefore still public. GitHub also notes that a commit made by a workflow using GITHUB_TOKEN does not itself trigger another Pages build; if your plan does not publish the generated bot commit as expected, use GitHub’s official Pages Actions deployment or make a normal follow-up push.
6. Optional custom domain
Put the domain name alone in CNAME, configure the same custom domain under Settings → Pages, and add the DNS records GitHub specifies. Also change SITE_URL; otherwise RSS, PDF links, and canonical page paths will point to the wrong host. Enable HTTPS after DNS verification.
7. Check the workflows
The normal build runs on pushes and daily. The digest runs Sunday and can be triggered manually. The template-export workflow can rebuild and upload the zip manually. Review Actions logs after the first run, because failed external sources, missing fonts/assets, malformed Typst, and unavailable secrets are visible there.
Troubleshooting
- A page is not built: ensure its
.typfile imports the nearest parentindex.typ(orcv.typwhen no parent index exists) and is not prefixed with_. - A nested page has broken CSS: check the relative import and let the Makefile compute
root; do not hard-code an asset depth in the page. - HTML differs from PDF: inspect
target()branches. Some paged layout concepts are unsupported by experimental HTML export. - A future post is missing: that is intentional in HTML/RSS; compile its PDF to see all dated content.
- A presentation is blank: open the PDF fallback, verify the PDF itself, then check PDF.js/network errors and viewer asset paths.
- Book search or rendering fails: the external catalog/source/compiler endpoint may be unavailable or blocked by cross-origin policy; the page has no server-side fallback.
- The digest is empty or fails: verify Gmail IMAP/app-password access, repository secrets, source URLs, and the workflow log.
- The zip refuses to build: its leak check found a site-specific identifier, or one of the included sample pages failed its smoke test.
- The HTML compiler prints a general warning: Typst HTML export remains experimental. The template deliberately unwraps unsupported alignment elements to suppress repetitive alignment-only diagnostics, but retains other warnings and errors.
License and attribution
The reusable archive is free to use for personal, academic, nonprofit, or commercial purposes under the Mozilla Public License 2.0. MPL‑2.0 is file-level copyleft: if you distribute modified covered source files, those files and their modifications remain available under MPL‑2.0. You may combine them with new files under other terms, and using the template does not place the personal writing, research, images, or other content you add under MPL‑2.0.
The main repository separates implementation from personal content. Original template code is covered by MPL‑2.0. CV data, blog posts, papers, application materials, personal images, and other authored content are not licensed for reuse unless a file says otherwise.
The design and implementation build on:
- Tufted by Vsevolod Shegolev, whose repository code is MIT-licensed and whose distributable template uses MIT-0.
- Tufte CSS by Dave Liepmann and contributors, with design and direction from Edward Tufte, released under the MIT License.
- PDF.js, loaded for slide rendering under the Apache License 2.0.
- Typst and the Typst package ecosystem. Packages remain under their respective licenses.
- The Typst Examples Book, used as a learning reference for this guide.
The archive includes third-party notices and the full MPL‑2.0 text. Copyright and license notices must remain intact. No trademark rights, warranty, hosting, support, or endorsement are granted.