Components and furniture
The overview below links to each definition. Function definitions show signatures with parameter and return types; variables show their declared types. Descriptions and defaults follow.
card
Wraps content in a clipped, semantically styled block.
This is the base the other components are built on, and the one to reach for when you want a panel that follows the deck’s colors without spelling them out.
#mosaic.components.card(role: "warning")[
The interval covers zero.
]Roles
A role names one color in the deck palette. The component paints its border and rails with that color, its text with the deck’s text color, and its panel with the color tinted into the deck’s canvas, so a component tracks the active theme instead of hard-coding paint.
neutral: the deck’s own surface, outlined in its line color. The default.accent: the deck’s accent.warning,error: the status colors.
Every one of them is a palette entry a theme or a deck can restate, so a deck that wants a different warning sets colors: (warning: ..) at setup once rather than restyling each component.
Overrides
fill, accent, stroke, radius, inset, align, and text each default to auto, meaning “take it from the role”. Pass one to override just that part. text is a dictionary of native text arguments and is merged into the role’s text color rather than replacing it.
#mosaic.components.card(
radius: 0pt,
stroke: none,
text: (size: 0.8em),
)[Quiet panel]card(
body: content,
role: str = "neutral",
fill: auto | color | gradient | tiling | none = auto,
accent: auto | color = auto,
stroke: auto | stroke | dictionary | none = auto,
radius: auto | length | dictionary = auto,
inset: auto | length | relative | dictionary = auto,
align: auto | alignment = auto,
text: dictionary = (:),
width: auto | length | relative | fraction = auto,
height: auto | length | relative | fraction = auto
) -> contentbody- Type:
content - Content inside the card.
- Type:
role- Type:
str - Default:
"neutral" - Semantic role name:
neutral,accent,warning, orerror.
- Type:
fill- Type:
autoorcolororgradientortilingornone - Default:
auto - Panel fill.
autouses the role’s color tinted into the deck canvas.
- Type:
accent- Type:
autoorcolor - Default:
auto - Border and rail color.
autouses the role’s own color.
- Type:
stroke- Type:
autoorstrokeordictionaryornone - Default:
auto - Border stroke.
autodraws the standard thickness inaccent.
- Type:
radius- Type:
autoorlengthordictionary - Default:
auto - Corner radius.
autouses the shared component radius.
- Type:
inset- Type:
autoorlengthorrelativeordictionary - Default:
auto - Padding inside the panel.
autouses the shared component inset.
- Type:
align- Type:
autooralignment - Default:
auto - Horizontal alignment of the body.
autoisleft.
- Type:
text- Type:
dictionary - Default:
(:) - Native
textarguments merged over the role’s text color.
- Type:
width- Type:
autoorlengthorrelativeorfraction - Default:
auto - Native block width.
autohugs the content.
- Type:
height- Type:
autoorlengthorrelativeorfraction - Default:
auto - Native block height.
autohugs the content.
- Type:
callout
Creates a callout with a semantic side stripe.
A callout is a card with two additions: a colored stripe down its left edge, and an optional bold title in the same color. Where card is a neutral panel, this one announces what kind of remark it holds.
#mosaic.components.callout(role: "warning", title: [Caveat])[
The estimate assumes independent errors.
]Roles
accent: the deck’s accent. The default.warning,error: the status colors.neutral: the deck’s own surface.
Custom colors
A role is the portable spelling, and it stays correct when the deck changes theme. When a callout needs a color the palette does not name, pass accent directly: it paints the stripe and the title. The panel keeps the role’s own fill, so a callout that should be tinted to match states fill as well.
#mosaic.components.callout(
accent: rgb("#7c3aed"),
fill: rgb("#f1ebfd"),
title: [Takeaway],
)[
Bounded work beats unbounded intent.
]See card for the full list of overrides.
callout(
body: content,
role: str = "accent",
title: content | none = none,
fill: auto | color | gradient | tiling | none = auto,
accent: auto | color = auto,
stroke: auto | stroke | dictionary | none = auto,
radius: auto | length | dictionary = auto,
inset: auto | length | relative | dictionary = auto,
align: auto | alignment = auto,
text: dictionary = (:),
width: auto | length | relative | fraction = 100%
) -> contentbody- Type:
content - Callout content.
- Type:
role- Type:
str - Default:
"accent" - Semantic role name:
accent,warning,error, orneutral.
- Type:
title- Type:
contentornone - Default:
none - Bold title set above the body in the stripe color.
- Type:
fill- Type:
autoorcolororgradientortilingornone - Default:
auto - Panel fill.
autouses the role’s color tinted into the deck canvas.
- Type:
accent- Type:
autoorcolor - Default:
auto - Stripe and title color.
autouses the role’s own color.
- Type:
stroke- Type:
autoorstrokeordictionaryornone - Default:
auto - Border stroke.
autodraws the left stripe and no other edge.
- Type:
radius- Type:
autoorlengthordictionary - Default:
auto - Corner radius.
autouses the shared component radius.
- Type:
inset- Type:
autoorlengthorrelativeordictionary - Default:
auto - Padding inside the panel.
autouses the shared component inset.
- Type:
align- Type:
autooralignment - Default:
auto - Horizontal alignment of the body.
autoisleft.
- Type:
text- Type:
dictionary - Default:
(:) - Native
textarguments merged over the role’s text color.
- Type:
width- Type:
autoorlengthorrelativeorfraction - Default:
100% - Native block width. Full width by default, so a column of callouts rules to one edge;
autohugs the content the waycarddoes.
- Type:
badge
Creates a compact inline badge.
A badge is a small pill that sits in the text flow rather than breaking it, which suits a status marker, a version marker, or a keyword beside a heading.
== Estimator #mosaic.components.badge(role: "accent")[stable]Any radius at least half the badge’s height rounds its ends completely, so an oversized value such as 999pt gives fully rounded pill ends. Restyle the body through text.
#mosaic.components.badge(
role: "warning",
radius: 999pt,
text: (weight: "bold", size: 0.7em),
)[draft]See card for the list of roles, how they resolve against the active theme, and the full list of overrides.
badge(
body: content,
role: str = "neutral",
radius: length | dictionary = component-tokens.badge-radius,
fill: auto | color | gradient | tiling | none = auto,
accent: auto | color = auto,
stroke: auto | stroke | dictionary | none = auto,
inset: auto | length | relative | dictionary = auto,
text: dictionary = (:)
) -> contentbody- Type:
content - Badge content, usually a word or two.
- Type:
role- Type:
str - Default:
"neutral" - Semantic role name:
neutral,accent,warning, orerror.
- Type:
radius- Type:
lengthordictionary - Default:
component-tokens.badge-radius - Corner radius. Oversize it (
999pt) for fully rounded pill ends.
- Type:
fill- Type:
autoorcolororgradientortilingornone - Default:
auto - Pill fill.
autouses the role’s color tinted into the deck canvas.
- Type:
accent- Type:
autoorcolor - Default:
auto - Border color.
autouses the role’s own color.
- Type:
stroke- Type:
autoorstrokeordictionaryornone - Default:
auto - Border stroke.
autodraws the standard thickness inaccent.
- Type:
inset- Type:
autoorlengthorrelativeordictionary - Default:
auto - Padding inside the pill.
autouses the compact badge inset.
- Type:
text- Type:
dictionary - Default:
(:) - Native
textarguments merged over the role’s text color, such assizeandweight.
- Type:
quote
Creates a quotation treatment with optional attribution.
The quotation sits in a lightly tinted card; attribution and source share one right-aligned line beneath it, joined by a comma when both are present.
#mosaic.components.quote(
attribution: [Ada Lovelace],
source: [Notes on the Analytical Engine, 1843],
)[
The Analytical Engine weaves algebraic patterns.
]To set a portrait beside the quotation, compose one natively:
#grid(
columns: (auto, 1fr),
gutter: 0.6em,
mosaic.components.image(path("ada.webp"), width: 4em, height: 4em),
mosaic.components.quote(attribution: [Ada Lovelace])[...],
)See card for the list of roles and the full list of overrides.
quote(
body: content,
attribution: content | none = none,
source: content | none = none,
role: str = "neutral",
fill: auto | color | gradient | tiling | none = auto,
accent: auto | color = auto,
stroke: auto | stroke | dictionary | none = auto,
radius: auto | length | dictionary = auto,
inset: auto | length | relative | dictionary = auto,
align: auto | alignment = auto,
text: dictionary = (:)
) -> contentbody- Type:
content - The quoted text.
- Type:
attribution- Type:
contentornone - Default:
none - Who is being quoted, set on the fine-print line below the quotation.
- Type:
source- Type:
contentornone - Default:
none - Where the quotation comes from, appended after the attribution.
- Type:
role- Type:
str - Default:
"neutral" - Semantic role name:
neutral,accent,warning, orerror.
- Type:
fill- Type:
autoorcolororgradientortilingornone - Default:
auto - Panel fill.
autois a faint wash of the deck’s text color.
- Type:
accent- Type:
autoorcolor - Default:
auto - Rail color.
autouses the role’s own color.
- Type:
stroke- Type:
autoorstrokeordictionaryornone - Default:
auto - Border stroke.
autodraws none.
- Type:
radius- Type:
autoorlengthordictionary - Default:
auto - Corner radius.
autouses the shared component radius.
- Type:
inset- Type:
autoorlengthorrelativeordictionary - Default:
auto - Padding inside the panel.
autouses the shared component inset.
- Type:
align- Type:
autooralignment - Default:
auto - Horizontal alignment of the body.
autoisleft.
- Type:
text- Type:
dictionary - Default:
(:) - Native
textarguments merged over the role’s text color.
- Type:
divider
Creates a horizontal divider, optionally split around a title.
Without a title it is one full-width rule. With one, it becomes two rules with the title centered between them, which is how a slide marks a change of name without spending a heading on it.
#mosaic.components.divider()
#mosaic.components.divider(
title: text(size: 0.7em)[Robustness checks],
stroke: 0.5pt + luma(70%),
)divider(title: content | none = none, stroke: auto | stroke = auto) -> contenttitle- Type:
contentornone - Default:
none - Content centered between the two line segments.
nonedraws one unbroken rule instead.
- Type:
stroke- Type:
autoorstroke - Default:
auto - Native Typst stroke used for both line segments.
autodraws the deck’s line color at the shared component thickness.
- Type:
progress
Displays progress through logical slides or semantic sections in the deck.
It reads the deck’s own counters, so it needs no arguments in the ordinary case. The usual home for it is a setup-level footer default, which puts one indicator on every slide that has a footer.
#show: mosaic.setup.with(
content: (
footer: align(right, mosaic.components.progress()),
),
)Variants
1/1: the current and final values, as text. The default.1: the current value alone.circle: a compact ring filling clockwise, sized bywidthandthickness.line: a horizontal bar filling left to right, sized bywidthandthickness.
What it counts
count selects the automatic counter: slides counts logical slides, and sections counts slides with layout: "section".
Both readings come from info(), which publishes the same position as slide and section records. Chrome that needs more than one indicator, or the section’s title rather than its number, reads that instead of composing several of these.
progress(
variant: str | function = "1/1",
count: str = "slides",
role: str = "accent",
width: auto | length | relative | fraction = auto,
thickness: length = component-tokens.rule-thickness,
fill: auto | color | gradient | tiling = auto,
accent: auto | color = auto
) -> contentvariant- Type:
strorfunction - Default:
"1/1" Visual treatment:
"1/1","1","circle","line", or a function drawing one. A renderer is called with a dictionary holdingcurrent,total,amount,accent,fill,width, andthickness, and returns the content to place.#mosaic.components.progress( variant: state => grid( columns: state.total, ..range(state.total).map(i => rect( height: state.thickness, fill: if i < state.current { state.accent } else { state.fill }, )), ), )
- Type:
count- Type:
str - Default:
"slides" - Which automatic counter to read:
"slides"or"sections".
- Type:
role- Type:
str - Default:
"accent" - Semantic role supplying the default colors:
accent,neutral,warning, orerror. The role’s own color paints the completed portion and its tinted fill paints the remainder.
- Type:
width- Type:
autoorlengthorrelativeorfraction - Default:
auto - Length of the
linevariant, or diameter of thecirclevariant.autois the full width forlineand a compact fixed diameter forcircle.
- Type:
thickness- Type:
length - Default:
component-tokens.rule-thickness - Stroke thickness of the
circleandlinevariants.
- Type:
fill- Type:
autoorcolororgradientortiling - Default:
auto - Paint of the inactive remainder.
autouses the role’s fill.
- Type:
accent- Type:
autoorcolor - Default:
auto - Paint of the completed portion, and of the text variants.
autouses the role’s accent.
- Type:
image
Creates a slide-sized native Typst image with an optional scrim.
This is Typst’s own image with presentation defaults: it fills its container instead of hugging its natural size, and it can carry a scrim. Everything else is forwarded unchanged, so alt, format, page, and scaling all work as usual.
#mosaic.slide(content: (
background: mosaic.components.image(path("cover.webp")),
body: [Text over the photograph],
))When Mosaic is imported as a package, wrap an asset of the calling project in a native path value, as above. A bare string would resolve relative to the package instead.
Scrims
A scrim paints a layer over the picture and under whatever text is composed on top of it, which is how a photograph is quieted enough to read against. It takes an ordinary Typst paint, so it accepts exactly what a fill accepts.
// Darken the whole picture evenly.
#mosaic.components.image(
path("cover.webp"),
scrim: black.transparentize(55%),
)
// Darken only the band the text occupies.
#mosaic.components.image(
path("cover.webp"),
scrim: gradient.linear(
black.transparentize(20%), black.transparentize(100%),
angle: 90deg,
),
)Without a scrim this returns the native image element directly, unwrapped.
image(
source: str | path | bytes,
width: auto | length | relative = 100%,
height: auto | length | relative | fraction = 100%,
fit: str = "cover",
scrim: none | color | gradient | tiling = none,
..native: arguments
) -> contentsource- Type:
strorpathorbytes - Native image source. Use
path(...)for an asset in the calling project.
- Type:
width- Type:
autoorlengthorrelative - Default:
100% - Width of the image area. Defaults to filling the container.
- Type:
height- Type:
autoorlengthorrelativeorfraction - Default:
100% - Height of the image area. Defaults to filling the container.
- Type:
fit- Type:
str - Default:
"cover" - Native Typst fitting mode:
"cover"fills and crops,"contain"fits the whole picture inside.
- Type:
scrim- Type:
noneorcolororgradientortiling - Default:
none - Paint layered over the picture and under any text composed on top of it. Accepts whatever a native
fillaccepts.
- Type:
..native- Type:
arguments - Further arguments forwarded unchanged to native Typst
image, such asalt,format,page, andscaling.
- Type:
figure
Creates a centered, captioned figure sized to its cell.
This is the in-cell counterpart to the image layout. Use the layout when one picture is the whole point of a slide; use this when a figure shares the slide with other content, most often as one of two side by side.
#mosaic.slide(layout: "content", columns: 2)[== Before and after][
#mosaic.components.figure(path("before.png"), caption: [Baseline])
][
#mosaic.components.figure(path("after.png"), caption: [After the reform])
]Pictures
Given an image source, this differs from mosaic.components.image in its defaults, which are the ones a chart or a photograph in a cell wants rather than the ones a full-bleed background wants: fit is "contain", so the picture is never cropped, and the result is centered in its cell.
The default height: auto gives the picture the cell’s full height, less whatever the caption and its gap consume. Nothing has to be measured by hand, and adjacent cells agree: each picture is as large as its own aspect ratio allows, and the captions share one baseline at the foot of the cells.
Other content
A table, a chart drawn in code, or a diagram is passed as content instead of a source. Such a body cannot be re-fitted the way a picture can, so it is laid out at the cell’s width and then scaled as a whole if it is still too tall, exactly as mosaic.fit does. Its own arrangement is preserved, the caption keeps the size the deck gave it, and a body that already fits is left alone. It is never magnified past its natural size.
#mosaic.components.figure(
table(columns: 3, ..cells),
caption: [Estimates by specification],
kind: table,
)A content body that does not fill its cell sits at the top of it and is captioned directly beneath itself, rather than stretching and captioning at the foot of the cell as a picture does.
Scaling a table also costs it the automatic kind a native figure would have detected, so state kind: table for the table numbering and the “Table” supplement. Further arguments go to the native figure here, and to the native image for a picture source.
Height
auto reads the size of the cell, not the space left over inside it, so a figure that follows prose in the same cell needs an explicit height:
#mosaic.slide(layout: "content", columns: 2)[== Two revenues][
- Payroll taxes carry the system
- Consumption taxes are regressive
#mosaic.components.figure(path("photo.jpg"), height: 50%)
][
#mosaic.components.figure(path("chart.png"), caption: [Shares since 1980])
]An explicit height sizes the picture area, and the caption follows directly beneath it rather than sitting at the foot of the cell. Such a figure takes only the height it was given, so it leaves whatever shares its cell where it was.
Captions
A caption composes a native Typst figure, so it takes the deck’s own show figure.caption styling, figure numbering, and references. Switch the numbering off with an ordinary set figure(numbering: none). Without a caption no figure is involved: the body is centered and sized, nothing more.
figure(
body: content | str | path | bytes,
caption: content | none = none,
fit: str = "contain",
width: auto | length | relative = 100%,
height: auto | length | relative | fraction = auto,
scrim: none | color | gradient | tiling = none,
..native: arguments
) -> contentbody- Type:
contentorstrorpathorbytes - The figure’s body: an image source, or finished content such as a table or a diagram. Use
path(...)for an image asset in the calling project.
- Type:
caption- Type:
contentornone - Default:
none - Caption composed beneath the body.
- Type:
fit- Type:
str - Default:
"contain" - How a picture fills its area.
"contain"fits the whole picture inside it, which is what a chart or a screenshot needs;"cover"fills the area and crops the overhang. Rejected for a content body, which is scaled rather than fitted.
- Type:
width- Type:
autoorlengthorrelative - Default:
100% - Width of the figure’s body.
- Type:
height- Type:
autoorlengthorrelativeorfraction - Default:
auto - Height of the figure’s body.
autotakes the cell’s height less the caption, which requires the figure to be the cell’s own content.
- Type:
scrim- Type:
noneorcolororgradientortiling - Default:
none - Paint layered over a picture. Accepts whatever a native
fillaccepts, and is rejected for a content body.
- Type:
..native- Type:
arguments - Further arguments forwarded unchanged to the native element this is built from:
imagefor a picture source, takingalt,format,page, andscaling;figurefor a content body, takingkind,supplement, andnumbering.
- Type: