Skip to content

Expose baselines for box and block - #8150

Merged
laurmaedje merged 12 commits into
mainfrom
box-baselines
May 4, 2026
Merged

Expose baselines for box and block#8150
laurmaedje merged 12 commits into
mainfrom
box-baselines

Conversation

@PgBiel

@PgBiel PgBiel commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Needed for #7895. Fixes #4796 (which might also address #7319 (comment)), #5855. Partially addresses #493.

Boxes and blocks will now propagate the baseline of what's inside them - usually text or math - allowing them to properly align with their surrounding text / math. For this to work, text and math content now expose their baselines. For paragraphs, the baseline will be set to that under the first line of text. For math equations, the global baseline will be set to the baseline of the first row. And for content with more than one paragraph / equation, the baseline of the first one will be used. With that, several little annoyances when using box shall be gone.

This means that:

  • Boxed inline equations will no longer be vertically shifted compared to non-boxed equations (Boxing an inline equation shifts the baseline #4796). You can now peacefully use box($a x + b y = 10 - c z$) to ensure that pesky long inline equation won't break across lines.
  • Same for any boxed text whatsoever.
  • Which implies you can now add a stroke (or use other box effects) around the boxed text, and it will now remain aligned!
    image

For blocks, for now, the only meaningful change is when a block is used inside an equation: it will now be properly aligned with the rest. For example, this is how $ #block($y$) + y $ currently looks like:

image

And this is how it will now look like:

image

In the future (in #7895), block baselines will also be used for list marker alignment.

In addition, box(scale[Text]) will also use the correct baseline after the scaling with reflow: true. However, box(rotate[Text]) will stay as it is today regardless of reflow: true as it is not yet very clear where the baseline should go after, say, a 180-degree rotation of a paragraph.

Configuration

Despite all the goodies listed above, it's possible the old behavior is still desired. In that case, box(baseline: ...) now accepts new values (defaulting to auto):

  • box(baseline: bottom) ensures text aligns with bottom of the box - this was the previous default behavior.
  • box(baseline: top) and box(baseline: horizon) also allow moving the baseline to the top or middle of the box.

Note that box(baseline: 5pt) (to shift the baseline by that height compared to where it normally is) is still supported, and both options can be specified simultaneously through a dictionary: box(baseline: (at: bottom, shift: 5pt)).

This is a breaking change if you're reading context box.baseline as this will now always return a dictionary.

TODO

  • Address remaining tests.
@PgBiel PgBiel mentioned this pull request Apr 22, 2026
6 tasks
@MDLC01

MDLC01 commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator

Note that #5855 is not properly linked as being closed by this PR.

@PgBiel PgBiel linked an issue Apr 22, 2026 that may be closed by this pull request
1 task
@PgBiel

PgBiel commented Apr 22, 2026

Copy link
Copy Markdown
Contributor Author

Fixed, thanks!

@PgBiel
PgBiel force-pushed the box-baselines branch 3 times, most recently from 3750581 to 12d0268 Compare April 23, 2026 20:37
@PgBiel
PgBiel marked this pull request as ready for review April 23, 2026 20:39

@laurmaedje laurmaedje left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! I have some nitpicks, but all minor.

  • The test case block-clip-svg-glyphs is interesting. The auto baseline is pushed beyond the height of the box because already the first line overflows. I wonder whether we should restrict the baseline to not exceed the box's height. But I'm not sure.

  • Do you know what happened for these five tests? The PNG is the same, but PDF and SVG differ slightly:

    • line-numbers-page-scope-quasi-empty-first-column
    • math-equation-number-align-multiline-expand
    • math-lr-ignore-ignorant
    • show-text-regex-character-class
    • spacing-h-and-v

    I'm not sure whether we discussed these before, I might've forgotten.

  • Also, is the TODO "Address remaining tests" still relevant?

Comment thread crates/typst-layout/src/flow/distribute.rs
Comment thread crates/typst-layout/src/math/run.rs
Comment thread crates/typst-layout/src/transforms.rs Outdated
Comment thread crates/typst-library/src/layout/container.rs Outdated
Comment thread crates/typst-library/src/layout/container.rs Outdated
Comment thread crates/typst-library/src/layout/container.rs Outdated
Comment thread tests/src/world.rs
Comment thread tests/src/world.rs Outdated
Comment thread tests/suite/layout/inline/baseline.typ Outdated
Comment thread tests/suite/layout/container.typ Outdated
@laurmaedje laurmaedje added layout Related to the layout category, which is about composing, positioning, etc. waiting-on-author Pull request waits on author labels Apr 28, 2026
@PgBiel
PgBiel force-pushed the box-baselines branch 2 times, most recently from 2fcd480 to 92973c0 Compare May 1, 2026 02:42
@laurmaedje

Copy link
Copy Markdown
Member

Would be great if you could also rebase on top of main since there are conflicts.

@PgBiel

PgBiel commented May 2, 2026

Copy link
Copy Markdown
Contributor Author

The test case block-clip-svg-glyphs is interesting. The auto baseline is pushed beyond the height of the box because already the first line overflows. I wonder whether we should restrict the baseline to not exceed the box's height. But I'm not sure.

Yeah, that's an interesting question. I suppose we generally can't prevent the baseline from moving out of bounds... But your proposal does make some sense, if we consider that baseline is strictly a property of layout, not of render or purely visual. Worth thinking a bit about.

Do you know what happened for these five tests?

line-numbers-page-scope-quasi-empty-first-column was already discussed - it's unrelated to the baseline changes, more related to how line numbers themselves work, so I accepted it. (I was planning on open an issue to discuss this more deeply.)

The core problem here is that the text inside #box[text] is considered a separate line of text, so Hello #box(baseline: -5pt)[world] generates two separate line numbers. It might be worth thinking about whether we should restrict line numbers to semantic par by default - although note that it is already possible today with #show par: set par.line(numbering: "1."). (I believe my work on line numbers predated semantic paragraphs, otherwise this might have been discussed earlier.)

For the rest, I had found that the changes were mostly in subtle position changes of empty shapes, likely spaces, so I don't think anything major was spotted there. In any case, I'll take a second look to confirm.

Also, is the TODO "Address remaining tests" still relevant?

No.

PgBiel added 12 commits May 3, 2026 18:40
Element description says it shouldn't affect layout. Baselines affect layout (they change box sizing).
Proposed algorithm: new baseline crosses (width / 2, baseline) post-transform.
Box has a "non-semantic" paragraph inside it, whereas there is another paragraph holding the box outside. Hence, there were two line numbers: one below the box and one inside it. Now that the baselines coincide, the two line numbers were merged together. 

But this is something to investigate further in the future. Should par.line be restricted only to semantic paragraphs? That is not always desirable (e.g. headings should still be numbered, more often than not), and can be done today already with `#show par: set par.line(...)`. Overall, worth some further discussion.
@PgBiel

PgBiel commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

math-equation-number-align-multiline-expand
math-lr-ignore-ignorant
show-text-regex-character-class
spacing-h-and-v

For all of these, seems like only empty groups were affected, as seen below. I believe these were minor changes in the spacing due to the baseline update.

With that, I think we're only missing the decision on block-clip-svg-glyphs.

image
@laurmaedje

Copy link
Copy Markdown
Member

I tested what CSS does:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      div {
        display: flex;
        align-items: baseline;
        gap: 1em;
      }

      span {
        border: 1pt solid red;
      }

      .b {
        font-size: 2em;
        height: 10px;
      }
    </style>
  </head>
  <body>
    <div>
      <span class="a">A</span>
      <span class="b">B</span>
    </div>
  </body>
</html>
grafik

It seems like the current behavior is consistent with CSS. I'd be inclined to leave it as-is. It also requires less special casing in the code.

@laurmaedje laurmaedje removed the waiting-on-author Pull request waits on author label May 4, 2026
@laurmaedje
laurmaedje added this pull request to the merge queue May 4, 2026
@laurmaedje

Copy link
Copy Markdown
Member

Nice to have this merged. One step closer to having the list issues fixed, too. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

layout Related to the layout category, which is about composing, positioning, etc.

3 participants