Reusable card
A card wraps content with matching style in HTML and paged output.
In Typst, #let is your primary building block for reusable formatting and construction. Use it to avoid repeating the same patterns.
#let banner(message, note) = [
#strong(message)
#v(0.3em)
#quote(note)
]
#banner("Reusable functions", "Define UI once and reuse it for many places.")Reusable function in action
“If you update this definition, both call sites update automatically.”
The calepin namespace also includes a compact set of reusable elements for cards, galleries, columns, tabs, and lightbox media. They are designed to demonstrate output-aware patterning in practical notebook websites.
Calepin exposes #calepin.elements.target() to pick content for HTML, static outputs, or a fallback. This example uses colors to make output differences explicit. In Typst 0.15, HTML color output was not fully supported, so HTML branches use explicit CSS via html.elem().
#let html-color-label(body, color) = html.elem("span", attrs: (style: "color: " + color + ";"))[
#body
]
#calepin.elements.target(
html: () => [#html-color-label([#strong("HTML")], "blue") output branch],
paged: () => [#text(fill: green)[#strong("PDF/SVG")] output branch],
fallback: () => [#text(fill: gray)[#strong("Fallback")] output branch],
)The rendered result:
HTML output branch
calepin.elements.card keeps one piece of content boxed consistently across formats.
#let callout = calepin.elements.card[
#heading(level: 3)[Reusable card]
A card wraps content with matching style in HTML and paged output.
]
#calloutcalepin.elements.callout renders AsciiDoc-style admonitions in HTML and paged output. Use kind: with note, tip, important, caution, or warning. The title defaults to the capitalized kind label; pass title: [...] to override it, or title: none to hide the title row.
#calepin.elements.callout(kind: "note")[
Notes highlight neutral supporting information.
]#calepin.elements.callout(kind: "warning", title: [Heads up])[
Warnings flag potential problems before they happen.
]You can also define a project-specific helper with Typst’s .with() method:
#let callout-custom = calepin.elements.callout.with(
kind: "important",
title: [Project note],
)
#callout-custom[
Use a local helper when several callouts should share the same kind and title.
]calepin.elements.sidenote and calepin.elements.sidefigure place supporting material in the margin. In HTML output, the built-in academic theme supports these elements directly and reserves the side column only on pages that use them.
In PDF output, Calepin leaves page geometry under your control. The academic theme connects these elements to marginalia, but it does not automatically reserve a wide outer margin. Add a marginalia.setup rule near the top of the document or in a local theme when you want Tufte-style PDF margins:
#import "@preview/marginalia:0.2.0" as marginalia
#show: marginalia.setup.with(
outer: (far: 8mm, width: 48mm, sep: 6mm),
book: false,
)Use numbering: none for an unnumbered side note.
Text#calepin.elements.sidenote[A margin note.] continues.
#calepin.elements.sidefigure(caption: [A small figure.])[
#image("figure.svg")
]calepin.elements.gallery accepts image items as tuples or dictionaries and handles local image metadata automatically in static outputs. In HTML output, it can activate lightbox behavior.
For offline sites or a strict Content Security Policy, override styles-url, lightbox-url, and module-url with locally hosted PhotoSwipe assets. Their defaults remain the pinned PhotoSwipe 5.4.4 files on unpkg. Frontend assets are emitted once, so every gallery on a page must use the same three URLs; Calepin reports conflicting configurations at compile time.
#calepin.elements.gallery(
(
("../assets/flowers_01.jpg", "First flower", [First flower]),
("../assets/flowers_04.jpg", "Fourth flower", [Fourth flower]),
("../assets/flowers_02.jpg", "Second flower", [Second flower]),
("../assets/flowers_03.jpg", "Third flower", [Third flower]),
("../assets/flowers_05.jpg", "Fifth flower", [Fifth flower]),
),
columns: 3,
max-width: 42em,
)