How to make bullet list item bodies flow like paragraphs?

Tried to compile a document, and found out that this solution gives an error for PDF/UA-1:

error: PDF/UA-1 error: invalid list (L) structure
    ┌─ file.typ:200:20
    │
200 │         num = context box(
    │ ╭─────────────────────^
201 │ │         width: measure(max-num).width,
202 │ │         align(right, text(overhang: false, num)),
203 │ │       )
    │ ╰───────^
    │
    = 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
    ┌─ file.typ:200:20
    │
200 │         num = context box(
    │ ╭─────────────────────^
201 │ │         width: measure(max-num).width,
202 │ │         align(right, text(overhang: false, num)),
203 │ │       )
    │ ╰───────^
    │
    = 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
    ┌─ file.typ:200:20
    │
200 │         num = context box(
    │ ╭─────────────────────^
201 │ │         width: measure(max-num).width,
202 │ │         align(right, text(overhang: false, num)),
203 │ │       )
    │ ╰───────^
    │
    = hint: list (L) may not contain paragraph (P)
    = hint: this is probably caused by a show rule

So I can’t provide improved accessibility, basically because Hanging-indent for lists · Issue #5751 · typst/typst · GitHub is not implemented. Though technically/semantically, it still won’t be a paragraph.


I just now realized that the two overrides don’t work together, so I can’t nest enum in list. Just updating the second state with a special value that doesn’t modify the numbering/markers behavior is enough, though not very clean.

/// Spacing doesn't work the same way as native solution if par leading and
/// spacing are different.
#let correctly-indent-list-and-enum-items(doc) = {
  let first-line-indent() = if type(par.first-line-indent) == dictionary {
    par.first-line-indent.amount
  } else {
    par.first-line-indent
  }

  show list: li => {
    for (i, it) in li.children.enumerate() {
      let nesting = state("list-nesting", 0)
      let indent = context h((nesting.get() + 1) * li.indent)
      let get-nesting() = calc.div-euclid(nesting.get(), 10)
      let marker = context {
        let n = get-nesting()
        if type(li.marker) == array {
          li.marker.at(calc.rem-euclid(n, li.marker.len()))
        } else if type(li.marker) == content {
          li.marker
        } else {
          li.marker(n)
        }
      }
      let parents = state("enum-parents", ()) // Support enum nesting.
      let body = {
        parents.update(arr => arr + (-1,))
        nesting.update(x => x + 10)
        it.body + parbreak()
        nesting.update(x => x - 10)
        parents.update(arr => arr.slice(0, -1))
      }
      let content = {
        marker
        h(li.body-indent)
        body
      }
      context pad(left: int(nesting.get() != 0) * li.indent, content)
    }
  }

  show enum: en => {
    let start = if en.start == auto {
      if en.children.first().has("number") {
        if en.reversed { en.children.first().number } else { 1 }
      } else {
        if en.reversed { en.children.len() } else { 1 }
      }
    } else {
      en.start
    }
    let number = start
    for (i, it) in en.children.enumerate() {
      number = if it.number != auto { it.number } else { number }
      if en.reversed { number = start - i }
      let parents = state("enum-parents", ())
      let get-parents() = parents.get().filter(x => x >= 0)
      let indent = context h((get-parents().len() + 1) * en.indent)
      let num = if en.full {
        context numbering(en.numbering, ..get-parents(), number)
      } else {
        numbering(en.numbering, number)
      }
      let max-num = if en.full {
        context numbering(en.numbering, ..get-parents(), en.children.len())
      } else {
        numbering(en.numbering, en.children.len())
      }
      num = context box(
        width: measure(max-num).width,
        align(right, text(overhang: false, num)),
      )
      let list-nesting = state("list-nesting", 0) // Support list nesting.
      let body = {
        parents.update(arr => arr + (number,))
        list-nesting.update(x => x + 1)
        it.body + parbreak()
        list-nesting.update(x => x - 1)
        parents.update(arr => arr.slice(0, -1))
      }
      if not en.reversed { number += 1 }
      let content = {
        num
        h(en.body-indent)
        body
      }
      context pad(left: int(parents.get().len() != 0) * en.indent, content)
    }
  }
  doc
}
1 Like