"Razor RenderFragment flex rows with gap collapse on top of each other"
▸ SYMPTOM
A flex container with gap holding thin flex-row children renders all rows collapsed on top of each other when placed inside a RenderFragment expression (RenderFragment Foo() => @<div>...). The container also reports a shorter height than its content, causing the next sibling below the fragment to overlap the lower rows. The identical markup placed directly in the component's <root> (main markup) renders perfectly.
▸ CAUSE
The panel engine's height measurement for thin flex rows with gap breaks inside RenderFragment expressions. A RenderFragment expression wraps its content in an intermediate layout context that under-reports the height contribution of gap between thin children. Each row measures as ~0px tall, so they stack at the same Y position. The container's own measured height also comes up short, pushing following siblings into the fragment's space.
Two adjacent traps found in the same area:
- A
min-heighton a flex child that has a sibling can collapse the sibling row -- do not blanketmin-heighton flex children sharing a parent overflow-y: scrollon agap-ed flex column breaks gap/height computation for its direct children
▸ FIX
Use a combination of three defenses:
1. Render rows in the main component markup, not inside a fragment expression:
@* BAD -- gap+thin rows break inside a fragment *@
@{ RenderFragment ReadoutBlock() => @<div class="readout">@foreach(var r in rows){ ... }</div>; }
@* GOOD -- identical markup in the main root renders clean *@
<div class="readout">
@foreach (var r in rows) { <div class="row">@r.Label: @r.Value</div> }
</div>2. Pin a min-height on thin rows so they never collapse to ~0:
.row {
min-height: 19px; // prevents height collapse
display: flex;
align-items: center;
}3. Give the container an explicit min-height covering all its rows:
.readout {
display: flex;
flex-direction: column;
gap: 4px;
min-height: 111px; // 5 rows * 19px + 4 gaps * 4px
}Single-root fragments (RenderFragment Row() => @<div class="row">...</div>) rendered inline are fine. It is the gap-and-thin-rows-inside-a-fragment combination that breaks.
▸ WHY IT WORKS
Moving the markup out of the RenderFragment expression eliminates the intermediate layout context that under-reports gap contributions. The min-height pins provide a floor so that even if a future engine version re-introduces the measurement bug, rows cannot collapse to zero. The container min-height ensures following siblings are pushed to the correct position regardless of how the fragment's content is measured internally.