Components and furniture

The contents below link to each definition. Function entries show the signature, then a description, then the parameters with their types and defaults; variable entries show the declared type.

Wraps content in a clipped, semantically styled block.

card(
  body,
  role: "neutral",
  fill: auto,
  accent: auto,
  stroke: auto,
  radius: auto,
  inset: auto,
  align: auto,
  text: (:),
  width: auto,
  height: auto
) -> content
Description

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.

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]
Arguments
bodycontent
Content inside the card.
rolestr
Semantic role name: neutral, accent, warning, or error.
fillauto | color | gradient | tiling | none
Panel fill. auto uses the role’s color tinted into the deck canvas.
accentauto | color
Border and rail color. auto uses the role’s own color.
strokeauto | stroke | dictionary | none
Border stroke. auto draws the standard thickness in accent.
radiusauto | length | dictionary
Corner radius. auto uses the shared component radius.
insetauto | length | relative | dictionary
Padding inside the panel. auto uses the shared component inset.
alignauto | alignment
Horizontal alignment of the body. auto is left.
textdictionary
Native text arguments merged over the role’s text color.
widthauto | length | relative | fraction
Native block width. auto hugs the content.
heightauto | length | relative | fraction
Native block height. auto hugs the content.

Creates a callout with a semantic side stripe.

callout(
  body,
  role: "accent",
  title: none,
  fill: auto,
  accent: auto,
  stroke: auto,
  radius: auto,
  inset: auto,
  align: auto,
  text: (:),
  width: 100%
) -> content
Description

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

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.

Arguments
bodycontent
Callout content.
rolestr
Semantic role name: accent, warning, error, or neutral.
titlecontent | none
Bold title set above the body in the stripe color.
fillauto | color | gradient | tiling | none
Panel fill. auto uses the role’s color tinted into the deck canvas.
accentauto | color
Stripe and title color. auto uses the role’s own color.
strokeauto | stroke | dictionary | none
Border stroke. auto draws the left stripe and no other edge.
radiusauto | length | dictionary
Corner radius. auto uses the shared component radius.
insetauto | length | relative | dictionary
Padding inside the panel. auto uses the shared component inset.
alignauto | alignment
Horizontal alignment of the body. auto is left.
textdictionary
Native text arguments merged over the role’s text color.
widthauto | length | relative | fraction
Native block width. Full width by default, so a column of callouts rules to one edge; auto hugs the content the way card does.

Creates a compact inline badge.

badge(
  body,
  role: "neutral",
  radius: component-tokens.badge-radius,
  fill: auto,
  accent: auto,
  stroke: auto,
  inset: auto,
  text: (:)
) -> content
Description

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.

Arguments
bodycontent
Badge content, usually a word or two.
rolestr
Semantic role name: neutral, accent, warning, or error.
radiuslength | dictionary
Corner radius. Oversize it (999pt) for fully rounded pill ends.
fillauto | color | gradient | tiling | none
Pill fill. auto uses the role’s color tinted into the deck canvas.
accentauto | color
Border color. auto uses the role’s own color.
strokeauto | stroke | dictionary | none
Border stroke. auto draws the standard thickness in accent.
insetauto | length | relative | dictionary
Padding inside the pill. auto uses the compact badge inset.
textdictionary
Native text arguments merged over the role’s text color, such as size and weight.

Creates a quotation treatment with optional attribution.

quote(
  body,
  attribution: none,
  source: none,
  role: "neutral",
  fill: auto,
  accent: auto,
  stroke: auto,
  radius: auto,
  inset: auto,
  align: auto,
  text: (:)
) -> content
Description

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.

Arguments
bodycontent
The quoted text.
attributioncontent | none
Who is being quoted, set on the fine-print line below the quotation.
sourcecontent | none
Where the quotation comes from, appended after the attribution.
rolestr
Semantic role name: neutral, accent, warning, or error.
fillauto | color | gradient | tiling | none
Panel fill. auto is a faint wash of the deck’s text color.
accentauto | color
Rail color. auto uses the role’s own color.
strokeauto | stroke | dictionary | none
Border stroke. auto draws none.
radiusauto | length | dictionary
Corner radius. auto uses the shared component radius.
insetauto | length | relative | dictionary
Padding inside the panel. auto uses the shared component inset.
alignauto | alignment
Horizontal alignment of the body. auto is left.
textdictionary
Native text arguments merged over the role’s text color.

Creates a horizontal divider, optionally split around a title.

divider(title: none, stroke: auto) -> content
Description

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%),
)
Arguments
titlecontent | none
Content centered between the two line segments. none draws one unbroken rule instead.
strokeauto | stroke
Native Typst stroke used for both line segments. auto draws the deck’s line color at the shared component thickness.

Displays progress through logical slides or semantic sections in the deck.

progress(
  variant: "1/1",
  count: "slides",
  quiet: auto,
  role: "accent",
  width: auto,
  thickness: component-tokens.rule-thickness,
  fill: auto,
  accent: auto
) -> content
Description

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

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.

Quieting

The indicator is deck furniture, so it quiets itself on pages where its counter has no meaningful reading: a slides count on unnumbered slides (titles and sections, by default), and a sections count before the first section slide. A sections count does display on section slides, where the counter reads the section being opened. quiet: overrides the rule in either direction.

Arguments
variant

str | function
Visual treatment: "1/1", "1", "circle", "line", or a function drawing one. A renderer is called with a dictionary holding current, total, amount, accent, fill, width, and thickness, 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 },
    )),
  ),
)
countstr
Which automatic counter to read: "slides" or "sections".
quietauto | bool
Whether the indicator quiets itself. auto silences a slides count on unnumbered slides and a sections count before the first section slide; true silences every unnumbered slide; false always displays.
rolestr
Semantic role supplying the default colors: accent, neutral, warning, or error. The role’s own color paints the completed portion and its tinted fill paints the remainder.
widthauto | length | relative | fraction
Length of the line variant, or diameter of the circle variant. auto is the full width for line and a compact fixed diameter for circle.
thicknesslength
Stroke thickness of the circle and line variants.
fillauto | color | gradient | tiling
Paint of the inactive remainder. auto uses the role’s fill.
accentauto | color
Paint of the completed portion, and of the text variants. auto uses the role’s accent.

Creates a slide-sized native Typst image with an optional scrim.

image(
  source,
  width: 100%,
  height: 100%,
  fit: "cover",
  scrim: none,
  ..native
) -> content
Description

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.

Arguments
sourcestr | path | bytes
Native image source. Use path(...) for an asset in the calling project.
widthauto | length | relative
Width of the image area. Defaults to filling the container.
heightauto | length | relative | fraction
Height of the image area. Defaults to filling the container.
fitstr
Native Typst fitting mode: "cover" fills and crops, "contain" fits the whole picture inside.
scrimnone | color | gradient | tiling
Paint layered over the picture and under any text composed on top of it. Accepts whatever a native fill accepts.
..nativearguments
Further arguments forwarded unchanged to native Typst image, such as alt, format, page, and scaling.

Creates a centered, captioned figure sized to its cell.

figure(
  body,
  caption: none,
  fit: "contain",
  width: 100%,
  height: auto,
  scrim: none,
  ..native
) -> content
Description

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.

Arguments
bodycontent | str | path | bytes
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.
captioncontent | none
Caption composed beneath the body.
fitstr
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.
widthauto | length | relative
Width of the figure’s body.
heightauto | length | relative | fraction
Height of the figure’s body. auto takes the cell’s height less the caption, which requires the figure to be the cell’s own content.
scrimnone | color | gradient | tiling
Paint layered over a picture. Accepts whatever a native fill accepts, and is rejected for a content body.
..nativearguments
Further arguments forwarded unchanged to the native element this is built from: image for a picture source, taking alt, format, page, and scaling; figure for a content body, taking kind, supplement, and numbering.