My employer uses Quarto for automated reporting, and I’m currently working on a typst template that will be applied to our reports as a custom format. All of our figures (images and tables) include several text annotations at the bottom of the figure, which need to be styled. I can achieve the desired result in typst by including align and text in the figure call:
#set figure.caption(position: top)
#show figure.caption: set align(left)
#figure(
{
// an image
rect(fill: red)
// annotations
align(
left,
text(size: 10pt)[
Program Path: mypath/file.csv \
Abbreviations: DV = dependent variable \
]
)
},
caption: [My Caption]
)
My question is, can I apply the same styling using a show/set rule outside of the figure, so that I can keep the styling and “artifact” creation steps separate?
I can almost get the desired result by simply using:
#show figure: it => {
set align(left)
set text(size: 10pt)
it
}
#figure(
{
rect(fill: red)
[
Program Path: mypath/file.csv \
Abbreviations: DV = dependent variable \
]
},
caption: [My Caption]
)
Except that the code above also shifts the image (which should be centered), to the left.
I think you can introduce your own function here, making it
#annotation[
Program Path: mypath/file.csv \
Abbreviations: DV = dependent variable \
]
Then you have separated style from markup by only just a function call, but that’s enough. The function itself can either contain the styling, or mark up the content so that it’s recognizable somehow and leave style for a show rule.
Thank you both for your replies. Yes, @Andrew, the caption should be left-aligned as well. Based on your response, would the simplest solution be to add a show rule to your suggestion, or did you have something else in mind?
There are a lot of explicit things that are default anyway, so they basically just don’t give anything other than more code. I also noticed that basically the width of everything is the max width of caption/annotations/body. If this is what you want, then all three do have to be inside the same block, i.e., inside the figure. I think a basic wrapper with additional argument is the way.
#set figure.caption(position: top)
#show figure: set block(sticky: true, breakable: true)
#show figure.caption: it => context {
let numbering = if it.kind == image {
[~] + counter(figure.where(kind: image)).display()
} else if it.kind == table {
[~] + counter(figure.where(kind: table)).display()
} // kind: raw is excluded
set text(font: "Amaranth", weight: "bold")
set align(left)
grid(
columns: (1in, auto),
[#it.supplement#numbering#it.separator], it.body,
)
}
#let figure(annotations: none, body, ..args) = std.figure(..args, {
body
set align(left)
set text(10pt)
annotations
})
#figure(
rect(width: 15cm, height: 9cm),
caption: lorem(15),
annotations: [
Program Path: artifacts/create-data.jl \
Legend: #lorem(60) \
Abbreviations:
CL = clearance, \
],
)
#v(5em)
#figure(
table(columns: 2)[][][],
caption: lorem(15),
annotations: [
Program Path: artifacts/create-data.jl \
Legend: #lorem(10) \
Abbreviations:
CL = clearance, \
],
)
Can you elaborate here? Also, is there a reason for using grid over a box?
The solution you proposed works if the code is in the same document as the content; however, if I remove it to a template I get an error, unexpected argument: annotations
The figure.placement is none by default. The figure.kind is determined by the content automatically. Even with the wrapper, it works as expected, so also no reason to set it. The same goes to figure.supplement.
The things defined in the template function are scoped to that function and not accessible from outside, i.e., define it outside.
You don’t have a comma after title: "List of Figures". The target: figure.where(kind: image) uses custom wrapper function, so either use std.figure or rename the function.