I’m trying to write a function that normally generates a title + body in the same paragraph. But the body is received as argument and can be a block element. In that case the title won’t be inline anymore (that’s fine) but it can become an orphan:
How can I make the “Section:” title sticky when the following content is a block?
I can fiddle with the internals of body to try and guess if it’s a block, but that wouldn’t be a robust solution. For example it would fail for #section(context block[Body]).
You can’t make an inline title if you use block. The only way to do this is to make the inline title not be inline, and instead be a sticky block, then you can do this. Or wrap the body in box and hope it will stay together “inline”.
#set page(width: 6cm, height: 3cm, margin: 1cm)
#show block: it => it + [#metadata(none)<block>]
#let section(body) = {
context [#metadata(here())<start>]
let title = [*Section:*]
context {
let start = query(selector(<start>).before(here())).last().value
let end = query(selector(<end>).after(here())).first().value
let blocks = query(selector(<block>).after(start).before(end))
if blocks.len() == 0 { title } else { block(sticky: true, title) }
}
body
context [#metadata(here())<end>]
}
#section[Body]
#section(block[Body])
#section(block[Body])
I guess my question wasn’t clear, I’ve updated it: indeed the title is no longer inline if the next thing is a new block but that’s fine, I just want to avoid orphans.
Regarding your solution: nice idea! We can also restrict the block show rule to the section content and simplify the code a bit:
#set page(width: 6cm, height: 3cm, margin: 1cm)
#let section(body) = {
show block: it => it + [#metadata(none)<block>]
[#metadata(none)<start>]
let title = [*Section:*]
context {
let start = query(selector(<start>).before(here())).last().location()
let end = query(selector(<end>).after(here())).first().location()
let blocks = query(selector(<block>).after(start).before(end))
if blocks.len() == 0 { title + " " } else { block(sticky: true, title) }
}
body
[#metadata(none)<end>]
}
but this doesn’t work if the body starts with inline content with a block coming later: in that case it’s wrongly treated like a block:
I’m writing a package that handles exercises (among other things). I don’t want users to have to deal with this kind of technical issues. They should be able to write #exercise[Body...] and have things just work.