Solution will require you to move the context expression outside of the x-height definition. Hint: turn it into a function and provide context to it.
// Requires `context`
#let x-height(body) = {
{
measure(
box(
inset: 0pt,
outset: 0pt,
text(top-edge: "bounds", body),
),
)
}.height
}
#let body = [= Test
#lorem(20)]
#body
#context {
let height = x-height(body)
set text(
top-edge: 2.8 * height * 0.72,
bottom-edge: 2.8 * height * 0.28,
)
set par(leading: 2.8 * height * 0.28)
body
}
If you could please edit your first code snippet and enclose it in back ticks ``` snippet ```, that would help us copy/paste/read your code more easily. Thanks.
#show: rest => context {
let x-height = measure({
set text(bottom-edge: "baseline", top-edge: "x-height")
[x]
}).height
let ex = x-height / 1em.to-absolute() * 1em
set text(
top-edge: 2.8 * ex * 0.72,
bottom-edge: 2.8 * ex * 0.28,
)
set par(leading: 2.8 * ex * 0.28)
rest
}
#lorem(10)
Explanation:
The x-height will depend on the font, so it’s context-sensitive. Therefore, we have to put everything that depends on x-height in a context {…}.
To simplify later writing, I use a show: rest => … rule to transform the whole document afterwards.
let x-height = measure(…).height is your original idea. I use "x-height" instead of "bounds" for the top-edge parameter, because it is the metric defined by the font designer, and does not depend on the specific character.
x-height is an absolute length (in points), and let ex = x-height / 1em.to-absolute() * 1em will make it to relative to the font size. Doing so will make your leading settings look better for headings, where have larger font size.