Any suggested workarounds for unexpected behavior from single item enums?

I’ve got a big document that has lots of numbered lists (among other things) and I’ve added padding to par so that all the text is in a narrow column but other things like tables and images can use the full width of the page.

Unfortunately, I’m getting unexpected behavior with my numbered lists that only have a single item. They don’t seem to respect the par padding. Or more accurately, they only don’t respect the padding when using automatic numbering shorthand.

Is there a different way to approach this or some creative trickery that I could use so my single item numbered lists are padded appropriately?

#show par: it => pad(right: 2.25in, it)

#set enum(numbering: "1.a.i.", tight:false)

= Single Item Numbered Lists Behaving Differently

When using the automatic numbering shortcut, padding is not respected:

+ All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

But when using the full syntax, padding _is_ respected:

#enum([All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating])

= Multi Item Examples

A multi item numbered list respects the padding:

+ All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

+ All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

But when a child item is a single item in a multi item list, it does not:

+ All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

  + All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

+ All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

Hi, welcome to the Typst forum!

The #set enum(tight: false) is only changing the default value of tight. This has an effect when you call #enum(...) without a tight argument. But writing a list in markup like

+ a
+ b

is equivalent to calling enum() with a tight argument that depends on the spacing between the markup items. So for markup lists, the tight argument is set and the default is not used.

This is the case even if you have a single item: in this case there’s no empty line between markup items, so it’s a tight list.

If you really want to, you can add a show rule to convert any tight list to non-tight:

#show enum.where(tight: true): it => {
  let (children, ..parameters) = it.fields()
  enum(..children, ..parameters, tight: false)
}

But your problem is not really about tight vs non-tight: you have a show rule on paragraphs, and you’re having this problem because tight list items don’t always make a paragraph. You can fix this manually in markup:

+ single item #parbreak() // adding parbreak to force a paragraph

or you can write a show rule to do that automatically:

#show enum.item: it => {
  let (number, body, ..parameters) = it.fields()
  if body.children.last() == parbreak() {
    return it
  }
  return enum.item(number, body + parbreak(), ..parameters)
}

However I’m not sure the whole thing with the par show rule is the right approach. It’s probably better to set the margins as you want for the text, and use show rules for tables, figures, etc. to make them wider than the text area. Or even better, use custom functions instead of global show rules, so that you control exactly which elements are wide:

#set page(margin: (left: 1in, right: 1in + 3in))

#lorem(20)

+ #lorem(20)

#let wide-element(it) = block(width: 100% + 3in, it)

#wide-element[Some wide text... #lorem(30)]

#wide-element(rect(width: 100%))

Thank you so much for the detailed response, sijo! I really appreciate it :smile:

I suspected I was heading down the wrong path with my padding approach and your suggested alternative is very helpful.

On the tight parameter, everything you’ve said makes sense and that’s helpful context for me. The one place where I’m still a little suspicious of the behavior is in the multi item list example:

+ All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

  + All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

+ All the small things true care, truth brings I'll take one lift Your ride, best trip Always, I know You'll be at my show Watching, waiting Commiserating

Based on the spacing, I would expect tight == false for the whole list, but it seems like maybe Typst treats this as a nested list situation and while the parent list has tight == false, the nested list has tight == true because a single item, by definition can’t have spacing set in markup mode. Or maybe I’m missing something more obvious?

1 Like

Yes that’s exactly right. You have an outer enum list, and one of the items happens to contain another enum list (with just one item). The tight parameter of both enum lists are independent.