← All updates

Smarter column-width defaults for table()

Omit widths in table() and the builder picks for you — every column sizes to content, and the column with the widest measured content automatically absorbs leftover space.

dslbuilderlayout

The table() builder used to require an explicit width on every column. In practice that meant authors guessed defensive percentages ("10%", "15%") for numeric columns and quietly hit the same trap whenever a real currency value was wider than the slot — the cell wrapped to two lines and the visible text dropped to the bottom of the row.

Widths are now optional. Omit them and the builder picks:

table(
  [["Description"], ["Qty", undefined, "right"], ["Amount", undefined, "right"]],
  "item in items",
  [["{{item.desc}}"], ["{{item.qty}}"], ["{{item.amount}}"]]
);

Every column starts as auto (sized to its widest measured content). After measurement, the single column with the largest aggregate content width is promoted to 1fr so it absorbs leftover space — usually whichever column carries the long free-form text. Tie-break is leftmost.

The promotion is purely additive. If you write a width — "1fr", "<n>%", <n>pt, or a number — for any column, the builder leaves your array alone. Existing templates render exactly as before.

A new grid token, auto-stretch, makes the candidate-then-promote behaviour available directly in attr.grid for hand-built rows that don't go through table(). Cell widths in the second table() argument are now optional too — the row's grid slot pins the column width, so per-cell widths were always redundant.

Skill: see pdf-template-author.md §Grid token forms.