Skip to content

Conversation

@mcanouil
Copy link
Collaborator

@mcanouil mcanouil commented Jan 30, 2026

Ensure terms and their descriptions remain together by preventing breaks within definition items.

The rule needs to be separate from the content rule, because here we play on the implicit block generated by Typst.

As this is a visual change, there are no tests, but I believe non breaking term/definition is a sensible default.

CodeBeforeAfter
---
title: "Quarto Playground"
format: typst
keep-typ: true
---

## Introduction

{{< lipsum 4 >}}

Multi-format output
: Generate HTML, PDF, Word, ePub, and more from one source.
Two-page document showing Quarto Playground title page with Lorem Ipsum text with at the bottom Multi-format output followed on the facing page with definition in centered box Two-page document showing Quarto Playground title page with Lorem Ipsum text and facing page with Multi-format output definition in centered box

Ensure that terms and their descriptions remain together by setting the block to be non-breakable.
@mcanouil mcanouil self-assigned this Jan 30, 2026
@posit-snyk-bot
Copy link
Collaborator

posit-snyk-bot commented Jan 30, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@mcanouil mcanouil changed the title fix: prevent breaking inside definition items fix(typst): prevent breaking inside definition items Jan 30, 2026
@mcanouil mcanouil marked this pull request as ready for review January 30, 2026 10:57
@mcanouil mcanouil requested a review from cderv January 30, 2026 11:00
Verifies that definition list terms and descriptions stay on the same page
and that descriptions are indented from terms.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@gordonwoodhull
Copy link
Contributor

I added a test using ensurePdfTextPositions. Without the fix, it reports

Cannot compare positions: "Generate HTML" is on page 2, "Multi-format" is on page 1    

Copy link
Collaborator

@cderv cderv left a comment

Choose a reason for hiding this comment

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

Thank you!

I see this is added in definitions.typ and concerns Definition block which is a Pandoc blocks initially.

Would that be worth also making a PR in Pandoc for this fix to be upstream too ?

@cderv cderv merged commit a4e7370 into quarto-dev:main Feb 2, 2026
50 of 51 checks passed
@mcanouil mcanouil deleted the fix/keep-term-definition-together branch February 2, 2026 09:01
@mcanouil
Copy link
Collaborator Author

mcanouil commented Feb 2, 2026

The terms rule is already different from Pandoc, should it be part of the PR?

Edit: in fact the rule is incompatible with how Pandoc writes terms definition.
It would be

#show terms: it => {
  it
    .children
    .map(child => block(breakable: false)[
      #text(weight: "bold")[#child.term]
      #block(inset: (left: 1.5em, top: -0.4em))[#child.description]
    ])
    .join()
}

Question is: should we update upstream and use the above rule instead of the two?

@cderv
Copy link
Collaborator

cderv commented Feb 2, 2026

I'll let @gordonwoodhull reply. I see it was adapted on our side for PDF accessibility so we need to keep ours, but maybe that would be worth reporting upstream this PDF accessibility improvement then 🤔 Commit: 9525c1b

Keeping our template improvement with upstream is best when we can. And when they don't accept it, then we'll need to patch at each update (and remember why we are different).

@mcanouil
Copy link
Collaborator Author

mcanouil commented Feb 2, 2026

The rule I'm showing is a mixed between our fix and Pandoc current implementation.
It should (I did not verify though) pass the accessibility check as it's not using #strong().

@cderv
Copy link
Collaborator

cderv commented Feb 2, 2026

To be clear, 9525c1b was a switch to what you see in Pandoc's template to a new way that pass PDF accessibility. Are you offering a third way?

I was thinking of sharing this resulting PR code to Pandoc upstream to report

  • PDF accessibility improvement
  • non-breaking definition list

@mcanouil
Copy link
Collaborator Author

mcanouil commented Feb 2, 2026

Are you offering a third way?

Yes, one that fits with the two rules. The initial suggestion was a simpler rule as it relied on implicit block, but that's also why it then needed a second rule to change breakable.

@cderv
Copy link
Collaborator

cderv commented Feb 2, 2026

Ok so doing

#show terms: it => {
  it
    .children
    .map(child => block(breakable: false)[
      #text(weight: "bold")[#child.term]
      #block(inset: (left: 1.5em, top: -0.4em))[#child.description]
    ])
    .join()
}

would avoid the #show terms.item: set block(breakable: false) ?

@gordonwoodhull what do you think ?

@mcanouil
Copy link
Collaborator Author

mcanouil commented Feb 2, 2026

Yes, it would

---
title: "Quarto Playground"
format:
  typst:
    keep-typ: true
---

```{=typst}
#show terms: it => {
  it
    .children
    .map(child => block(breakable: false)[
      #text(weight: "bold")[#child.term]
      #block(inset: (left: 1.5em, top: -0.4em))[#child.description]
    ])
    .join()
}
```

## Introduction

{{< lipsum 4 >}}

Multi-format output
: Generate HTML, PDF, Word, ePub, and more from one source.

@gordonwoodhull
Copy link
Contributor

gordonwoodhull commented Feb 2, 2026

I'm in favor of a unified rule, and contributing upstream. However, you'll want to test with

format:
  typst:
    pdf-standard: ua-1

as the above proposal fails Typst's validation:

[typst]: Compiling definition-item-no-break.typ to definition-item-no-break.pdf...

ERROR: error: PDF/UA-1 error: invalid list (L) structure
    ┌─ tests/docs/smoke-all/typst/definition-item-no-break.typ:382:2
    │
382 │ / Multi-format: #block[
    │   ^^^^^^^^^^^^
    │
    = hint: list (L) may not contain paragraph (P)
    = hint: this is probably caused by a show rule

error: PDF/UA-1 error: invalid list (L) structure
    ┌─ tests/docs/smoke-all/typst/definition-item-no-break.typ:382:0
    │  
382 │ ╭ / Multi-format: #block[
383 │ │ Generate HTML, PDF, Word, ePub, and more from one source.
384 │ │ ]
    │ ╰─^
    │  
    = hint: list (L) may not contain marked content directly
    = hint: this is probably caused by a show rule

It does pass the ensurePdfTextPositions test, and looks visually OK!

Interestingly, the previous code (from this PR) does pass UA-1.

@mcanouil
Copy link
Collaborator Author

mcanouil commented Feb 2, 2026

New variant that actually does not messed up the list structure as Pandoc code does.
Here we skip directly the parent to directly target the children (items), this way we can change the item content without touching the whole list structure.

#show terms.item: it => block(breakable: false)[
  #text(weight: "bold")[#it.term]
  #block(inset: (left: 1.5em, top: -0.4em))[#it.description]
]

This passed UA-1 Typst validation.

@gordonwoodhull
Copy link
Contributor

Much cleaner! We should definitely upstream this.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants