Summary
When building multi-tab architecture decks with the layout engine, the rendered diagrams carry a lot of dead whitespace and the icons look tiny once the figure is fit to a page (e.g. embedded in a Word/PDF page at ~6 in wide). Getting a dense, readable diagram currently requires a lot of manual per-diagram tuning. The root causes are all in src/layout-engine.mjs defaults, and I think a few small default changes + one new option would fix the common case out of the box.
Environment
- Kit around
feat(layout): numbered step badges, balanced fill, thin black edges + polished templates (v1.0.x), src/layout-engine.mjs.
- Use case: a 6-tab Databricks lakehouse deck (medallion + serving/integration/MLOps pillars), rendered to PNG and embedded in a document at page width.
Root causes (with current defaults)
1. Label-less layout wrappers still reserve a title strip + heavy padding.
group / frame / phantom / grid all default to:
gap: opts.gap ?? 30,
pad: opts.pad ?? 24,
header: label ? (opts.header ?? 36) : (opts.header ?? 14),
A label-less grouping wrapper (the common row()/col() = phantom with no title) still gets pad: 24 (→ 48 px horizontal + 48 px vertical) and a header: 14 dead strip. Because composing a real layout nests several of these wrappers, the invisible padding compounds: a couple of nested row/col wrappers can silently add 100–150 px of empty margin around content that has no visual container at all.
2. Icon size is a hard-coded module constant.
There is no build-API way to make icons bigger — not per-icon, not per-diagram, not global. Meanwhile mIcon floors the icon cell width by the label (Math.max(96, Math.min(200, label.length*7+24))), so a 48 px glyph sits in a 96–200 px cell. The icon:label ratio is fixed and small, so at page scale the glyphs are hard to read.
3. No compact primitive for single-item full-width bands.
A section/frame given a wide width with one centered icon (e.g. a "Governance" or "Connectivity" band spanning the diagram) renders as a big mostly-empty box. There's no built-in "title band + icon laid out horizontally" strip, so authors hand-roll one.
4. Frames don't shrink-to-content, and over-wide widths are silent.
Widths are passed explicitly and act as a floor; a width larger than the content just becomes whitespace, with no validator signal (unlike the existing palette / edge-crossing advice).
Impact / what I had to do manually
To turn a sprawling deck into a dense, readable one for the same content I had to, on every page: set pad: 0 on all label-less row/col wrappers, cut default gaps ~40–50%, shrink forced frame/band widths down to actual content, convert single-icon full-width bands into horizontal title+icon strips, and 2-column a tall 6-item stack. Concretely one page went from a 1742×1231 canvas to ~1376×485 for identical content — after which the (still 48 px) icons finally read clearly at page width. That's a lot of fiddling for something the engine could default better.
Suggestions
- Zero
pad/header for label-less wrappers. A phantom/row/col with no title shouldn't reserve a 14 px header or 24 px pad — default those to 0 when label is empty (keep the current values when a label is present). Biggest single win, low risk.
- Make icon size configurable. Support
new Diagram({ iconSize }) and/or icon(id, name, label, { size }), and have mIcon/place/emit honor it instead of the module const. (Keep 48 as the default.)
- Add a compact band primitive — title band + icon(s) laid out horizontally — for single-item full-width bands, so they don't render as big empty boxes.
- Shrink-to-content by default — treat an explicit frame width as a max/hint rather than a hard floor beyond the title-width floor; and add a validator advice for "diagram is mostly empty / nodes fill < N% of their frame", matching the existing palette / edge-crossing advisories.
- (Optional) a single
Diagram({ density: 'compact' }) switch that applies tighter gap/pad/icon defaults in one place.
Items 1 + 2 alone would remove most of the manual tuning. Happy to send a PR for #1 if that direction sounds good.
Summary
When building multi-tab architecture decks with the layout engine, the rendered diagrams carry a lot of dead whitespace and the icons look tiny once the figure is fit to a page (e.g. embedded in a Word/PDF page at ~6 in wide). Getting a dense, readable diagram currently requires a lot of manual per-diagram tuning. The root causes are all in
src/layout-engine.mjsdefaults, and I think a few small default changes + one new option would fix the common case out of the box.Environment
feat(layout): numbered step badges, balanced fill, thin black edges + polished templates(v1.0.x),src/layout-engine.mjs.Root causes (with current defaults)
1. Label-less layout wrappers still reserve a title strip + heavy padding.
group/frame/phantom/gridall default to:A label-less grouping wrapper (the common
row()/col()=phantomwith no title) still getspad: 24(→ 48 px horizontal + 48 px vertical) and aheader: 14dead strip. Because composing a real layout nests several of these wrappers, the invisible padding compounds: a couple of nestedrow/colwrappers can silently add 100–150 px of empty margin around content that has no visual container at all.2. Icon size is a hard-coded module constant.
There is no build-API way to make icons bigger — not per-icon, not per-diagram, not global. Meanwhile
mIconfloors the icon cell width by the label (Math.max(96, Math.min(200, label.length*7+24))), so a 48 px glyph sits in a 96–200 px cell. The icon:label ratio is fixed and small, so at page scale the glyphs are hard to read.3. No compact primitive for single-item full-width bands.
A
section/frame given a wide width with one centered icon (e.g. a "Governance" or "Connectivity" band spanning the diagram) renders as a big mostly-empty box. There's no built-in "title band + icon laid out horizontally" strip, so authors hand-roll one.4. Frames don't shrink-to-content, and over-wide widths are silent.
Widths are passed explicitly and act as a floor; a width larger than the content just becomes whitespace, with no validator signal (unlike the existing palette / edge-crossing advice).
Impact / what I had to do manually
To turn a sprawling deck into a dense, readable one for the same content I had to, on every page: set
pad: 0on all label-lessrow/colwrappers, cut default gaps ~40–50%, shrink forced frame/band widths down to actual content, convert single-icon full-width bands into horizontal title+icon strips, and 2-column a tall 6-item stack. Concretely one page went from a 1742×1231 canvas to ~1376×485 for identical content — after which the (still 48 px) icons finally read clearly at page width. That's a lot of fiddling for something the engine could default better.Suggestions
pad/headerfor label-less wrappers. Aphantom/row/colwith no title shouldn't reserve a 14 px header or 24 px pad — default those to0whenlabelis empty (keep the current values when a label is present). Biggest single win, low risk.new Diagram({ iconSize })and/oricon(id, name, label, { size }), and havemIcon/place/emit honor it instead of the module const. (Keep48as the default.)Diagram({ density: 'compact' })switch that applies tighter gap/pad/icon defaults in one place.Items 1 + 2 alone would remove most of the manual tuning. Happy to send a PR for #1 if that direction sounds good.