Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/preview/manifesto/0.1.0/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Louis Grange

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
43 changes: 43 additions & 0 deletions packages/preview/manifesto/0.1.0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Manifesto for Typst

**Manifesto** 📚 is the first HTML Typst documentation template for packages, built to be very easy to use. It is **100% written in Typst, responsive** and directly uses your `typst.toml` to automatically get your package information.

[Report a bug](https://github.com/l0uisgrange/manifesto/issues?new) — [Forum](https://github.com/l0uisgrange/manifesto/discussions/categories/q-a)

You are free to check either [this example](https://bone.grangelouis.ch) from `bone` or [this one](https://zap.grangelouis.ch) from `zap`.

<img width="1232" height="758" alt="Capture d’écran 2026-01-22 à 18 39 15" src="https://github.com/user-attachments/assets/076e3071-6feb-4453-8b44-e5de1a32ec53" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you write an actual alternative text please?


## Quick usage

The template is very easy to use, and will transform your whole project into a stunning online documentation in just a second.

```typst
#import "@preview/manifesto:0.1.0"

#show: it => template(it, title: "MyAwesomePackage", toml: "PATH_TO/typst.toml")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't test but I think template is not in scope with the current import, it would be manifesto.template.


= Introduction

Lorem ipsum
```

## Options

The `template` function accepts a few parameters to customize the data displayed on the website.

| Option | Default value | Description |
| --- | --- | --- |
| `title` (required) | | The title of your package |
| `toml` | `none` | The path to the `typst.toml` file |
| `version` | `none` | Your package version |
| `description` | `none` | Your package description |
| `repository` | `none` | Your package repository URL (e.g. GitHub) |
| `universe` | `none` | Your package name on Typst universe name |
| `license` | `none` | Your package license |

Note that giving the `toml` file is aready enough, and will fill in the other options.

## Contributing

I highly welcome contributions 🌱! Creating and maintaining this template takes time and love. If you'd like to help, don't hesitate to join the journey 🤩!
30 changes: 30 additions & 0 deletions packages/preview/manifesto/0.1.0/icons.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#let qa-icon = html.elem(

Check failure on line 1 in packages/preview/manifesto/0.1.0/icons.typ

View check run for this annotation

Typst package check / @preview/manifesto:0.1.0

packages/preview/manifesto/0.1.0/icons.typ#L1

The following error was reported by the Typst compiler: unknown variable: html

Check failure on line 1 in packages/preview/manifesto/0.1.0/icons.typ

View check run for this annotation

Typst package check / @preview/manifesto:0.1.0

packages/preview/manifesto/0.1.0/icons.typ#L1

The following error was reported by the Typst compiler: unknown variable: html
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to provide some fallback when compiling to PDF, even if the template is supposed to be HTML-only. PDF export is still the default and it would be a bad experience to get a non-working template by default. Simply displaying a non-editable page that says "This template is HTML-only" (with a bit of styling if you feel like it) would be enough. You would also have to take care of not using html at the top-level like you are doing here, but to always wrap it behind a conditional or a function that is lazily evaluated.

If what I'm saying is not clear I can try to give a more explicit example, let me know.

"svg",
attrs: (
class: "fill-current size-6 dark:fill-zinc-400",
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 640 640",
),
html.elem(
"path",
attrs: (
d: "M416 208C416 305.2 330 384 224 384C197.3 384 171.9 379 148.8 370L67.2 413.2C57.9 418.1 46.5 416.4 39 409C31.5 401.6 29.8 390.1 34.8 380.8L70.4 313.6C46.3 284.2 32 247.6 32 208C32 110.8 118 32 224 32C330 32 416 110.8 416 208zM416 576C321.9 576 243.6 513.9 227.2 432C347.2 430.5 451.5 345.1 463 229.3C546.3 248.5 608 317.6 608 400C608 439.6 593.7 476.2 569.6 505.6L605.2 572.8C610.1 582.1 608.4 593.5 601 601C593.6 608.5 582.1 610.2 572.8 605.2L491.2 562C468.1 571 442.7 576 416 576z",
),
),
)

#let bug-icon = html.elem(
"svg",
attrs: (
class: "fill-current size-6 dark:fill-zinc-400",
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 640 640",
),
)[
#html.elem(
"path",
attrs: (
d: "M210.5 559.2C203 568.8 189.5 571.3 179.2 564.5C168.9 557.7 165.5 544 170.9 533L172.1 530.8L208.3 472.8L208.3 431.8L208.6 427.8C209.2 423.9 210.8 420.1 213.2 416.9L252.5 364.7L186.3 424.2C169.3 439.5 147.3 447.9 124.4 448L116.3 448C69.7 448 31.9 410.2 31.9 363.6C31.9 320.6 64.3 284.4 107 279.7L237.5 265.2L192.7 226.9L190.3 224.5C185 218.5 183 210.1 185 202.2L198.3 148.8L172.4 109.9L171.1 107.8C165.3 97 168.4 83.2 178.5 76.1C188.6 69 202.2 71 209.9 80.4L211.4 82.4L243.4 130.4L244.8 132.6C247.6 137.9 248.3 144.2 246.9 150.2L234.6 199.4L287.9 245.1L287.9 216.3C287.9 204.5 294.4 194.2 303.9 188.6L303.9 128.1L304.2 124.8C305.7 117.5 312.2 112.1 319.9 112.1C327.6 112.1 334.1 117.6 335.6 124.8L335.9 128L335.9 188.5C345.5 194 351.9 204.3 351.9 216.2L351.9 245.1L405.3 199.3L393 150.1C391.5 144.2 392.3 137.9 395.1 132.5L396.5 130.3L428.5 82.3L430 80.3C437.7 70.9 451.3 68.8 461.4 76C471.5 83.1 474.5 96.9 468.8 107.7L467.5 109.8L441.6 148.7L454.9 202.1C456.9 210.1 454.8 218.4 449.6 224.4L447.2 226.8L402.5 265.1L533 279.6C575.8 284.4 608.1 320.5 608.1 363.5C608.1 410.1 570.3 447.9 523.7 447.9L515.6 447.9C492.8 447.9 470.7 439.4 453.7 424.1L387.6 364.6L426.8 416.8C429.2 420 430.8 423.8 431.4 427.7L431.7 431.7L431.7 472.7L467.9 530.7L469.1 532.8C474.5 543.8 471.1 557.5 460.8 564.3C450.5 571.1 437 568.7 429.5 559L428.1 557L388.1 493L386.5 489.9C385.1 486.8 384.4 483.3 384.4 479.8L384.4 440L352 396.9L352 471.6C352 489.3 337.7 503.6 320 503.6C302.3 503.6 288 489.3 288 471.6L288 397L255.7 440L255.7 479.8C255.7 483.3 255 486.7 253.6 489.9L252 493L212 557L210.6 559z",
),
)
]
191 changes: 191 additions & 0 deletions packages/preview/manifesto/0.1.0/template.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#import "icons.typ": bug-icon, qa-icon

#let detoml = toml

#let template(doc, title: none, toml: none, copyright: true, version: none, notices: (), links: (), description: none, repository: none, universe: none, license: none) = [

#let title = if toml != none { detoml(toml).package.name } else { title }
#let version = if toml != none { detoml(toml).package.at("version", default: none) } else { version }
#let universe = if toml != none { detoml(toml).package.at("name", default: none) } else { universe }
#let license = if toml != none { detoml(toml).package.at("license", default: none) } else { license }
#let repository = if toml != none { detoml(toml).package.at("repository", default: none) } else { repository }
#let description = if toml != none { detoml(toml).package.at("description", default: none) } else { description }
#html.elem("html", attrs: (lang: "en", class: "scroll-smooth"))[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are using the untyped html.elem API you can support Typst 0.13, but if you want to only support 0.14 you could use the typed HTML API.

#html.elem("head")[
#html.elem("meta", attrs: (charset: "UTF-8"))
#html.elem("meta", attrs: (name: "viewport", content: "width=device-width, initial-scale=1.0"))
#html.elem("title")[#context document.title]
// Styling sources
#html.elem("script", attrs: (src: "https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio,container-queries"))
#html.elem("link", attrs: (rel: "preconnect", href: "https://fonts.googleapis.com"))
#html.elem("link", attrs: (rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: "anonymous"))
#html.elem("link", attrs: (rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Mona+Sans:ital,wght@0,200..900;1,200..900&display=swap"))
]
#html.elem("body", attrs: (style: "font-family: 'Mona Sans', sans-serif", class: "bg-zinc-50 dark:bg-zinc-950"))[
// Configuration
#set heading(numbering: none)
// Raw blocks styling
#show raw.where(block: false): it => {
html.elem("span", attrs: (class: "border-t px-1 py-0.5 border-none rounded dark:bg-zinc-800 bg-zinc-200/60 text-xs *:not-prose not-prose font-mono"), it)
}
#show raw.where(block: true): it => {
html.elem("div", attrs: (class: "p-4 border bg-zinc-100/30 dark:bg-zinc-900/20 dark:border-zinc-800 not-prose text-[.85rem] rounded-md"), it)
}
// Article
#html.elem(
"article",
attrs: (class: "max-w-[95rem] mx-auto grid md:grid-cols-[max-content_auto_max-content] relative gap-10 p-5 !antialised"),
)[
// Navigation
#html.elem("div", attrs: (class: "order-1 md:w-60 prose overflow-visible"))[
#html.elem("div", attrs: (class: "sticky top-5"))[
#html.elem("div", attrs: (
class: "dark:prose-invert prose-zinc dark:text-white prose-li:first:mt-0 prose-ol:p-0 prose-a:underline-offset-2 prose-li:p-0 prose-ol:m-0",
))[
#html.elem(
"h1",
attrs: (
class: if version != none { "rounded-bl-none " } else { "" }
+ "capitalize bg-zinc-800 mb-0 text-white max-w-max rounded-md px-2 py-0.5 text-3xl font-medium mb-0",
),
title,
)
#if version != none {
html.elem("div", attrs: (class: "text-xs py-0.5 px-2 max-w-max rounded-md rounded-t-none bg-zinc-700 mb-8 text-white"))[v#version]
}
#description
#let provider = if "github" in repository { "GitHub" } else if "gitlab" in repository { "Gitlab" } else { "Source" }
#table(
columns: 2,
if repository != none { [Repository] }, link(repository, provider),
[Typst Universe], link("https://typst.app/universe/package/"+ universe, title),
[License], [#license],
[Last update], [#datetime.today().display()]
)
#html.elem("div", attrs: (class: "grid grid-cols-2 lg:grid-cols-1 mt-8"))[
#if "qa" in notices [
#html.elem("div")[
#qa-icon
#html.elem("div", attrs: (class: "prose-sm mb-6 mt-2.5"))[
Got a question? \
Ask it on the #link(notices.qa)[community forum].
]
]
]
#if "bug" in notices [
#html.elem("div")[
#bug-icon
#html.elem("div", attrs: (class: "prose-sm mt-2"))[
Experienced a bug? \
Please #link(notices.bug)[report] it.
]
]
]
]
]
]
]
#html.elem("article", attrs: (
class: "order-3 md:order-2 flex-auto !max-w-4xl prose dark:prose-invert overflow-hidden prose-zinc prose-a:underline-offset-2 prose-td:align-middle prose-headings:scroll-my-7 prose-headings:font-medium prose-strong:font-medium",
))[
#doc
]
#html.elem("div", attrs: (class: "order-2 md:order-3 w-60 flex-none prose overflow-visible"))[
#html.elem("div", attrs: (class: "sticky top-5"))[
#html.elem("div", attrs: (
class: "dark:prose-invert prose-zinc dark:text-white prose-li:first:mt-0 prose-ol:p-0 prose-a:underline-offset-2 prose-li:p-0 prose-ol:m-0",
))[
#html.elem(
"div",
attrs: (
class: "prose-a:text-current *:*:*:mb-0.5 prose-a:font-normal prose-a:decoration-0 mb-12",
),
outline(depth: 1, title: none),
)
]
]
]
]

#html.elem("div", attrs: (class: "border-t antialised dark:border-zinc-800"))[
#html.elem("div", attrs: (class: "p-5"))[
#if copyright [
#html.elem("span", attrs: (class: "text-zinc-500 text-sm"))[Made with #link("https://github.com/l0uisgrange/manifesto")[Manifesto] from Typst Universe]
]
]
]
]
]
]

#let warning(content, title: "Warning", ..params) = block(
..params,
html.elem("div", attrs: (class: "px-3 py-2.5 rounded-md bg-orange-600/10"), {
html.elem("span", attrs: (class: "flex items-center gap-2 not-prose"), {
html.elem("svg", attrs: (class: "size-5", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 640 640"), html.elem(
"path",
attrs: (
class: "fill-orange-600",
d: "M256.5 37.6C265.8 29.8 279.5 30.1 288.4 38.5C300.7 50.1 311.7 62.9 322.3 75.9C335.8 92.4 352 114.2 367.6 140.1C372.8 133.3 377.6 127.3 381.8 122.2C382.9 120.9 384 119.5 385.1 118.1C393 108.3 402.8 96 415.9 96C429.3 96 438.7 107.9 446.7 118.1C448 119.8 449.3 121.4 450.6 122.9C460.9 135.3 474.6 153.2 488.3 175.3C515.5 219.2 543.9 281.7 543.9 351.9C543.9 475.6 443.6 575.9 319.9 575.9C196.2 575.9 96 475.7 96 352C96 260.9 137.1 182 176.5 127C196.4 99.3 216.2 77.1 231.1 61.9C239.3 53.5 247.6 45.2 256.6 37.7zM321.7 480C347 480 369.4 473 390.5 459C432.6 429.6 443.9 370.8 418.6 324.6C414.1 315.6 402.6 315 396.1 322.6L370.9 351.9C364.3 359.5 352.4 359.3 346.2 351.4C328.9 329.3 297.1 289 280.9 268.4C275.5 261.5 265.7 260.4 259.4 266.5C241.1 284.3 207.9 323.3 207.9 370.8C207.9 439.4 258.5 480 321.6 480z",
),
))
html.elem("span", attrs: (class: "font-medium text-orange-600 *:p-0 !*:m-0 "), title)
})
content
}),
)

#let tip(content, title: "Tip", ..params) = block(
..params,
html.elem("div", attrs: (class: "px-3 py-2.5 rounded-md bg-green-600/10"), {
html.elem("span", attrs: (class: "flex items-center gap-2 not-prose"), {
html.elem("svg", attrs: (class: "size-5", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 640 640"), html.elem(
"path",
attrs: (
class: "fill-green-600",
d: "M420.9 448C428.2 425.7 442.8 405.5 459.3 388.1C492 353.7 512 307.2 512 256C512 150 426 64 320 64C214 64 128 150 128 256C128 307.2 148 353.7 180.7 388.1C197.2 405.5 211.9 425.7 219.1 448L420.8 448zM416 496L224 496L224 512C224 556.2 259.8 592 304 592L336 592C380.2 592 416 556.2 416 512L416 496zM312 176C272.2 176 240 208.2 240 248C240 261.3 229.3 272 216 272C202.7 272 192 261.3 192 248C192 181.7 245.7 128 312 128C325.3 128 336 138.7 336 152C336 165.3 325.3 176 312 176z",
),
))
html.elem("span", attrs: (class: "font-medium text-green-600 *:p-0 !*:m-0 "), title)
})
content
}),
)


#let info(content, title: "Info", ..params) = block(
..params,
html.elem("div", attrs: (class: "px-3 py-2.5 rounded-md bg-blue-600/10"), {
html.elem("span", attrs: (class: "flex items-center gap-2 not-prose"), {
html.elem("svg", attrs: (class: "size-5", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 640 640"), html.elem(
"path",
attrs: (
class: "fill-blue-600",
d: "M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM288 224C288 206.3 302.3 192 320 192C337.7 192 352 206.3 352 224C352 241.7 337.7 256 320 256C302.3 256 288 241.7 288 224zM280 288L328 288C341.3 288 352 298.7 352 312L352 400L360 400C373.3 400 384 410.7 384 424C384 437.3 373.3 448 360 448L280 448C266.7 448 256 437.3 256 424C256 410.7 266.7 400 280 400L304 400L304 336L280 336C266.7 336 256 325.3 256 312C256 298.7 266.7 288 280 288z",
),
[],
))
html.elem("span", attrs: (class: "font-medium text-blue-600 *:p-0 !*:m-0 "), title)
})
content
}),
)

#let circ(drawing) = html.elem("div", attrs: (class: "mb-7 rounded-md border dark:border-zinc-800 overflow-hidden flex-col flex *:m-0 *:block *:w-full *:even:rounded-t-none"), {
html.elem("div", attrs: (class: "p-7 bg-white rounded-t-md dark:invert dark:hue-rotate-180"))[
#html.frame[
#eval(drawing.text, mode: "markup")
]
]
html.elem("div", attrs: (class: "*:rounded-t-none *:border-none border-t dark:border-zinc-800 *:border-none overflow-x-scroll"), raw(
drawing.text.split("\n").slice(2).join("\n"),
block: true,
lang: "typst",
))
})

#let schema(drawing) = html.elem("div", attrs: (class: "mb-7 rounded-md border dark:border-zinc-800 overflow-hidden flex-col flex *:m-0 *:block *:w-full *:even:rounded-t-none"), {
html.elem("div", attrs: (class: "p-7 bg-white rounded-md dark:invert dark:hue-rotate-180"))[
#html.frame(drawing)
]
})
Binary file added packages/preview/manifesto/0.1.0/thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions packages/preview/manifesto/0.1.0/typst.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "manifesto"
version = "0.1.0"
compiler = "0.14.2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This corresponds to the lowest version number this package works with, so unless you depend on a fix that was introduced in 0.14.2, it should probably be 0.14.0 or even lower.

description = "Create stunning documentation websites using native HTML export"
repository = "https://github.com/l0uisgrange/manifesto"

Check failure on line 6 in packages/preview/manifesto/0.1.0/typst.toml

View check run for this annotation

Typst package check / @preview/manifesto:0.1.0

packages/preview/manifesto/0.1.0/typst.toml#L6

We could not fetch this URL. Details: reqwest::Error { kind: Status( 404, None, ), }
categories = ["layout", "visualization"]
disciplines = ["design", "education"]
keywords = ["documentation", "docs", "html", "website", "site", "static", "online"]
exclude = [".github"]
entrypoint = "template.typ"
authors = ["Louis Grange <https://github.com/l0uisgrange>"]
license = "MIT"

[template]
path = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your template should be in its own subdirectory. Only files in that directory will be copied to the user project. The "library" part of your package should contain utility and templating functions you don't expect the user to modify, while the template files should contain some default content.

thumbnail = "thumbnail.png"

Check failure on line 17 in packages/preview/manifesto/0.1.0/typst.toml

View check run for this annotation

Typst package check / @preview/manifesto:0.1.0

packages/preview/manifesto/0.1.0/typst.toml#L17

The thumbnail file should be outside of the template directory. When your template will be used as a base for users's projects, the template directory will be copied as is, and the thumbnail file is generally not displayed in documents based on your template.
entrypoint = "template.typ"