How to extend blocks to bottom of page/column?

I am making a poster, with the content presented in blocks in three columns. I would like all blocks to have alignment at the bottom of the poster. For example:

#set page("presentation-4-3", margin: 15pt)
#set par(first-line-indent: 1em)
#let title(txt, sup:false) = block(text(if sup {24pt} else {15pt})[*#txt*])
#let section(txt) = block(
  stroke: gray + 2pt,
  fill: gray.lighten(50%),
  radius: 5pt,
  width: 100%,
  inset: 10pt,
  txt
)

#section(align(center)[#title(sup:true)[Poster Title] #title[Subtitle]])
#columns(
  3, 
  gutter: 15pt, 
  for i in range(5) {
    section[
      #title[Section #(i+1)]
      #lorem(28*(i+1))
      $ (-b plus.minus sqrt(b^2 - 4 a c) ) / (2a) $
    ]
  }
)

Using blocks like this works great as they spill over into the next column if necessary. However, I would like all three columns to be aligned at the bottom (like they are automatically at the top), even if it means just adding whitespace at the end. Must I do this by hand (e.g., using #v()), or is there a way to force the blocks to fill vertical space?

2 Likes

Good question. Something like set(page: justify: true) would be cool to have vertical justification.

2 Likes

Thought 1: You can surely just add #v(1fr) to the bottom of the blocks you want to extend ;)

Thought 2: but the example you use allows breakable blocks. I don’t know how well that works together :)

Thought 3: And if you manage to make your blocks non-breakable then you might want to use grid

You need to be somewhat more precise what you actually want. Which of the blocks should fill more vertical space? The one at the end? The block that is breaking over to the next side (I think this would be exceptionally difficult to achieve) Or some kind of space inside the blocks but (above and) below of the content so that it is the same fraction of space in every block (above and below the respective content)?

And even if you have answered which of the options you want, I think it would be necessary to tell the columns where they must stop …

I played around a little with the columns element etc and I think that there is no somewhat sane solution to your problem. I am really hooked by your question because I think that it’s generally a desirable outcome.

initial thoughts on the concrete problem

In any way you probably want to wrap your columns inside a bigger block which takes up the whole width and height. Like this:

#block(width: 100%, height: 1fr, stroke: blue,
  columns(
    3, 
    gutter: 15pt,
    {
      ...
    }
  )
)

The problem I see: What you want is some kind of justify but for columns instead of lines. You can imagine the three columns as three lines of written text, but instead of words they consist of blocks. What you want is, that your blocks are touching both sides, the top and the bottom. Just like the individual characters touch both sides, left and right, when you set par(justify: true).

iirc is the history of justifying pretty long and the techniques are very complex/sophisticated nowadays. Especially because it usually comes with hyphenation, line-wrapping and so on. In our case you would need to define what are the characters and what are break points where we could insert more whitespace and where not? Line-breaks? Do you want your lines to be have more spacing because of this? paragraph breaks? or just between blocks? (one (rather manual) solution would be to define some points inside a given bigger block which contains the three columns, and then the remaining space would be distributed between these points evenly.)

On the other hand, one could argue that the machinery is already there then and especially in a framework like typst. I’d agree yes, but to be honest: I don’t have the slightest idea about all this stuff. Probably one of the maintainers could give us some insights …

... not even CSS can do this (I think)

I’m really no front-end developer, but I immediately got the reference to flexbox. This is a CSS container used to layout multiple containers. And I always wondered how to do the exact same question you asked. (In this case vertically or horizontally.) I have to say that I also never really dug into it, because on websites it does not matter that much (you essentially have an infinitely long page and content changes all the time anyway). However these web-design protocols are a freaking hell and over time they implemented literally every thinkable component. How comes that they did not develop such a thing? (Maybe I just missed it, then I’d appreciate a note :))
concise and well illustrated summary of flexbox

1 Like

I appreciate your thoughts on this @Xodarap! To be more explicit about what I was hoping for (though it looks like you were thinking along similar lines):

  • maintain the block-breaking behavior between columns
  • “vertically justify” the last block in each column so that their bottoms are all level with each other, even if that block breaks to the next column
  • I’m not picky about how this vertical space is added, but it could be between paragraphs, just after the main title, or even at the bottom of the block

I don’t know why I hadn’t thought of using #v(1fr), but that is a great solution here. I added that underneath the titles of each block that was the last of the column (I preferred to have the space added there instead of between lines in a paragraph), and this worked great. However, it would be nice to have some kind of way to make the #v(1fr) automatically added beneath titles if a given section (referring to my example doc above) is the last one in a column. I am not sure how to detect if an object is the last item in a column, but if I did, I would include a line that says if is_last() {#v(1fr)} in the definition of section. Any suggestions on how to detect if something is the last of its kind in a column?

A few more thoughts:

  • Given what has been said so far, as long as folks are not hoping to implement a “vertical justify” that involves changing spacing between lines, and instead just looks to change spacing between paras or at top/bottom, then implementing par(justify: true) might be as simple as adding #v(1fr) in the relevant locations during compilation.
  • In the end, poster-making requires some manual adjustment no matter what, and I don’t expect a solution to this to make any poster look good and use space appropriately. I hope it just takes a poster that is already well-organized and then make the sections line up along their bases.