How can I make a figure caption left-aligned or centered, matching the alignment to the number of lines in the caption

I want my figure captions to be centered if they occupy a single line, or left-aligned (or justified) if they span more than one line. I currently have a caption style that I want to preserve


show figure.caption: it => [
    #strong[#it.supplement
    #context it.counter.display(it.numbering) 
    #it.separator]
    #it.body
 ]

Would #show figure.caption: set align(left) suffice? It aligns the caption to the left of the figure block (the green rectangles in the second screenshot).

#show figure.caption: set align(left)

#show figure.caption: it => {
  strong({
    it.supplement
    [ ]
    context it.counter.display(it.numbering)
    it.separator
  })
  [ ]
  it.body
}

#figure(rect[Image], caption: [Caption])
#figure(rect[Image], caption: lorem(20))

Besides, newlines in markup mode ([โ€ฆ]) become spaces, and newlines in code mode ({โ€ฆ}) are ignored. You can use code mode to handle the spaces right.

1 Like

See Create a dedicated section on dangers of using square brackets carelessly (multi-line) ยท Issue #6844 ยท typst/typst ยท GitHub for more details about the newlines.

1 Like

I also want it to be centered in case the figure is large but the description is short; is there any way to achieve this dynamically without having to change:

#show figure.caption: set align(center)

And changed it back when the caption is large?

1 Like

Oh, I missed this case.

How about this? I put the caption into a block. The whole block is centered, and texts inside the block are aligned to the left.

#set page(height: auto, width: 240pt, margin: 1em)

#show figure.caption: it => block({
  set align(left)
  strong({
    it.supplement
    [ ]
    context it.counter.display(it.numbering)
    it.separator
  })
  [ ]
  it.body
})

#figure(rect[Image], caption: [Caption])
#figure(rect[A super large Image, very large], caption: [Caption])
#figure(rect[Image], caption: lorem(20))

1 Like

Very creative :slightly_smiling_face:. Yes, this solves the problem; I think it gives a better effect. Many books use this style.

1 Like

This is a great solution!
Is it possible to restrict the block to only a certain width while retaining the rest? Specifically, Iโ€™d love to insert shrink the width of the block statement as follows, but doing this leads to loss of the centering of the block in case of one-liners.

#set page(height: auto, width: 240pt, margin: 1em)

#show figure.caption: it => block(
width: 95%, 
{
  set align(left)
  strong({
    it.supplement
    [ ]
    context it.counter.display(it.numbering)
    it.separator
  })
  [ ]
  it.body
})

#figure(rect[Image], caption: [Caption])
#figure(rect[A super large Image, very large], caption: [Caption])
#figure(rect[Image], caption: lorem(20))

Thank you!

How about using two blocks?

#set page(height: auto, width: 240pt, margin: 1em)

#show figure.caption: it => block(
  width: 70%, // To make it more visible, I change it from 95% to 70%
  block({
    set align(left)
    strong({
      it.supplement
      [ ]
      context it.counter.display(it.numbering)
      it.separator
    })
    [ ]
    it.body
  }),
)

#block(width: 100%, stroke: green, inset: 1em)[
  For reference, the width of this block is #100%.
]

#figure(rect[Image], caption: [Caption])
#figure(rect[A super large Image, very large], caption: [Caption])
#figure(rect[Image], caption: lorem(20))

2 Likes

Perfect, thanks so much! :slight_smile:

1 Like